forked from espressif/esp-protocols
Compare commits
6 Commits
mqtt_cxx-v
...
old_fixes
Author | SHA1 | Date | |
---|---|---|---|
1903936b03 | |||
c7b8f813ed | |||
276d3f51c1 | |||
4d8d61e106 | |||
932c1869a4 | |||
37cac66e7e |
@ -37,7 +37,7 @@ idf_component_register(SRCS "${srcs}"
|
||||
|
||||
|
||||
set_target_properties(${COMPONENT_LIB} PROPERTIES
|
||||
CXX_STANDARD 17
|
||||
CXX_STANDARD 23
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
CXX_EXTENSIONS ON
|
||||
)
|
||||
|
@ -96,6 +96,8 @@ public:
|
||||
*/
|
||||
bool recover();
|
||||
|
||||
bool isInBullshitState() { if (m_isInBullshitState) { m_isInBullshitState = false; return true; } return false; }
|
||||
|
||||
private:
|
||||
|
||||
enum class protocol_mismatch_reason {
|
||||
@ -128,6 +130,9 @@ private:
|
||||
bool on_footer(CMuxFrame &frame);
|
||||
void recover_protocol(protocol_mismatch_reason reason);
|
||||
|
||||
//! When espressif again fucks up the recovering of the MUX
|
||||
bool m_isInBullshitState{};
|
||||
|
||||
std::function<bool(uint8_t *data, size_t len)> read_cb[MAX_TERMINALS_NUM]; /*!< Function pointers to read callbacks */
|
||||
std::shared_ptr<Terminal> term; /*!< The original terminal */
|
||||
cmux_state state; /*!< CMux protocol state */
|
||||
|
@ -31,9 +31,9 @@ namespace dce_commands {
|
||||
* @param fail_phrase String to be present in the reply to fail this command
|
||||
* @param timeout_ms Timeout in ms
|
||||
*/
|
||||
command_result generic_command(CommandableIf *t, const std::string &command,
|
||||
const std::string &pass_phrase,
|
||||
const std::string &fail_phrase, uint32_t timeout_ms);
|
||||
command_result generic_command(CommandableIf *t, std::string_view command,
|
||||
std::string_view pass_phrase,
|
||||
std::string_view fail_phrase, uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Declaration of all commands is generated from esp_modem_command_declare.inc
|
||||
|
@ -6,6 +6,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <span>
|
||||
|
||||
namespace esp_modem::dce_commands {
|
||||
|
||||
/**
|
||||
@ -17,9 +20,9 @@ namespace esp_modem::dce_commands {
|
||||
* @param timeout_ms Command timeout in ms
|
||||
* @return Generic command return type (OK, FAIL, TIMEOUT)
|
||||
*/
|
||||
command_result generic_command(CommandableIf *t, const std::string &command,
|
||||
const std::string &pass_phrase,
|
||||
const std::string &fail_phrase, uint32_t timeout_ms);
|
||||
command_result generic_command(CommandableIf *t, std::string_view command,
|
||||
std::string_view pass_phrase,
|
||||
std::string_view fail_phrase, uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Utility command to send command and return reply (after DCE says OK)
|
||||
@ -29,7 +32,7 @@ command_result generic_command(CommandableIf *t, const std::string &command,
|
||||
* @param timeout_ms Command timeout in ms
|
||||
* @return Generic command return type (OK, FAIL, TIMEOUT)
|
||||
*/
|
||||
template <typename T> command_result generic_get_string(CommandableIf *t, const std::string &command, T &output, uint32_t timeout_ms = 500);
|
||||
template <typename T> command_result generic_get_string(CommandableIf *t, std::string_view command, T &output, uint32_t timeout_ms = 500);
|
||||
|
||||
/**
|
||||
* @brief Generic command that passes on "OK" and fails on "ERROR"
|
||||
@ -38,6 +41,6 @@ template <typename T> command_result generic_get_string(CommandableIf *t, const
|
||||
* @param timeout_ms Command timeout in ms
|
||||
* @return Generic command return type (OK, FAIL, TIMEOUT)
|
||||
*/
|
||||
command_result generic_command_common(CommandableIf *t, const std::string &command, uint32_t timeout_ms = 500);
|
||||
command_result generic_command_common(CommandableIf *t, std::string_view command, uint32_t timeout_ms = 500);
|
||||
|
||||
} // esp_modem::dce_commands
|
||||
|
@ -74,7 +74,7 @@ public:
|
||||
return device.get();
|
||||
}
|
||||
|
||||
command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms)
|
||||
command_result command(std::string_view command, got_line_cb got_line, uint32_t time_ms)
|
||||
{
|
||||
return dte->command(command, std::move(got_line), time_ms);
|
||||
}
|
||||
|
@ -108,12 +108,12 @@ public:
|
||||
* @param time_ms Time in ms to wait for the answer
|
||||
* @return OK, FAIL, TIMEOUT
|
||||
*/
|
||||
command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms) override;
|
||||
command_result command(std::string_view command, got_line_cb got_line, uint32_t time_ms) override;
|
||||
|
||||
/**
|
||||
* @brief Sends the command (same as above) but with a specific separator
|
||||
*/
|
||||
command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms, char separator) override;
|
||||
command_result command(std::string_view command, got_line_cb got_line, uint32_t time_ms, char separator) override;
|
||||
|
||||
/**
|
||||
* @brief Allows this DTE to recover from a generic connection issue
|
||||
@ -122,6 +122,8 @@ public:
|
||||
*/
|
||||
bool recover();
|
||||
|
||||
bool isInBullshitState();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Allows for locking the DTE
|
||||
|
@ -79,8 +79,8 @@ public:
|
||||
* @param separator Character treated as a line separator, typically '\n'
|
||||
* @return OK, FAIL or TIMEOUT
|
||||
*/
|
||||
virtual command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms, const char separator) = 0;
|
||||
virtual command_result command(const std::string &command, got_line_cb got_line, uint32_t time_ms) = 0;
|
||||
virtual command_result command(std::string_view command, got_line_cb got_line, uint32_t time_ms, const char separator) = 0;
|
||||
virtual command_result command(std::string_view command, got_line_cb got_line, uint32_t time_ms) = 0;
|
||||
|
||||
virtual int write(uint8_t *data, size_t len) = 0;
|
||||
virtual void on_read(got_line_cb on_data) = 0;
|
||||
|
@ -187,6 +187,7 @@ bool CMux::on_recovery(CMuxFrame &frame)
|
||||
frame.ptr = recover_ptr;
|
||||
state = cmux_state::INIT;
|
||||
ESP_LOGI("CMUX", "Protocol recovered");
|
||||
m_isInBullshitState = true;
|
||||
if (frame.len > 1 && frame.ptr[1] == SOF_MARKER) {
|
||||
// empty frame
|
||||
frame.advance();
|
||||
|
@ -5,7 +5,8 @@
|
||||
*/
|
||||
|
||||
#include <charconv>
|
||||
#include <list>
|
||||
#include <span>
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "cxx_include/esp_modem_dce_module.hpp"
|
||||
@ -16,12 +17,12 @@ namespace esp_modem::dce_commands {
|
||||
|
||||
static const char *TAG = "command_lib";
|
||||
|
||||
static command_result generic_command(CommandableIf *t, const std::string &command,
|
||||
const std::list<std::string_view> &pass_phrase,
|
||||
const std::list<std::string_view> &fail_phrase,
|
||||
uint32_t timeout_ms)
|
||||
command_result generic_command(CommandableIf *t, std::string_view command,
|
||||
std::span<const std::string_view> pass_phrase,
|
||||
std::span<const std::string_view> fail_phrase,
|
||||
uint32_t timeout_ms)
|
||||
{
|
||||
ESP_LOGD(TAG, "%s command %s\n", __func__, command.c_str());
|
||||
ESP_LOGD(TAG, "%s command %.*s\n", __func__, command.size(), command.data());
|
||||
return t->command(command, [&](uint8_t *data, size_t len) {
|
||||
std::string_view response((char *)data, len);
|
||||
if (data == nullptr || len == 0 || response.empty()) {
|
||||
@ -41,13 +42,13 @@ static command_result generic_command(CommandableIf *t, const std::string &comma
|
||||
|
||||
}
|
||||
|
||||
command_result generic_command(CommandableIf *t, const std::string &command,
|
||||
const std::string &pass_phrase,
|
||||
const std::string &fail_phrase, uint32_t timeout_ms)
|
||||
command_result generic_command(CommandableIf *t, std::string_view command,
|
||||
std::string_view pass_phrase,
|
||||
std::string_view fail_phrase, uint32_t timeout_ms)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
const auto pass = std::list<std::string_view>({pass_phrase});
|
||||
const auto fail = std::list<std::string_view>({fail_phrase});
|
||||
const std::string_view pass[] { pass_phrase };
|
||||
const std::string_view fail[] { fail_phrase };
|
||||
return generic_command(t, command, pass, fail, timeout_ms);
|
||||
}
|
||||
|
||||
@ -79,7 +80,7 @@ bool set(std::span<char> &dest, std::string_view &src)
|
||||
|
||||
} // str_copy
|
||||
|
||||
template <typename T> command_result generic_get_string(CommandableIf *t, const std::string &command, T &output, uint32_t timeout_ms)
|
||||
template <typename T> command_result generic_get_string(CommandableIf *t, std::string_view command, T &output, uint32_t timeout_ms)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
return t->command(command, [&](uint8_t *data, size_t len) {
|
||||
@ -108,7 +109,7 @@ template <typename T> command_result generic_get_string(CommandableIf *t, const
|
||||
}, timeout_ms);
|
||||
}
|
||||
|
||||
command_result generic_command_common(CommandableIf *t, const std::string &command, uint32_t timeout_ms)
|
||||
command_result generic_command_common(CommandableIf *t, std::string_view command, uint32_t timeout_ms)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
return generic_command(t, command, "OK", "ERROR", timeout_ms);
|
||||
@ -243,7 +244,7 @@ command_result get_operator_name(CommandableIf *t, std::string &operator_name, i
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
std::string out;
|
||||
auto ret = generic_get_string(t, "AT+COPS?\r", out, 75000);
|
||||
auto ret = generic_get_string(t, "AT+COPS?\r", out, 1000);
|
||||
if (ret != command_result::OK) {
|
||||
return ret;
|
||||
}
|
||||
@ -313,9 +314,7 @@ command_result resume_data_mode(CommandableIf *t)
|
||||
command_result set_command_mode(CommandableIf *t)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
const auto pass = std::list<std::string_view>({"NO CARRIER", "OK"});
|
||||
const auto fail = std::list<std::string_view>({"ERROR"});
|
||||
return generic_command(t, "+++", pass, fail, 5000);
|
||||
return generic_command(t, "+++", {"NO CARRIER", "OK"}, {"ERROR"}, 5000);
|
||||
}
|
||||
|
||||
command_result get_imsi(CommandableIf *t, std::string &imsi_number)
|
||||
|
@ -130,11 +130,11 @@ void DTE::set_command_callbacks()
|
||||
|
||||
}
|
||||
|
||||
command_result DTE::command(const std::string &command, got_line_cb got_line, uint32_t time_ms, const char separator)
|
||||
command_result DTE::command(std::string_view command, got_line_cb got_line, uint32_t time_ms, const char separator)
|
||||
{
|
||||
Scoped<Lock> l1(internal_lock);
|
||||
command_cb.set(got_line, separator);
|
||||
primary_term->write((uint8_t *)command.c_str(), command.length());
|
||||
primary_term->write((uint8_t *)command.data(), command.length());
|
||||
command_cb.wait_for_line(time_ms);
|
||||
command_cb.set(nullptr);
|
||||
buffer.consumed = 0;
|
||||
@ -144,7 +144,7 @@ command_result DTE::command(const std::string &command, got_line_cb got_line, ui
|
||||
return command_cb.result;
|
||||
}
|
||||
|
||||
command_result DTE::command(const std::string &cmd, got_line_cb got_line, uint32_t time_ms)
|
||||
command_result DTE::command(std::string_view cmd, got_line_cb got_line, uint32_t time_ms)
|
||||
{
|
||||
return command(cmd, got_line, time_ms, '\n');
|
||||
}
|
||||
@ -368,6 +368,13 @@ bool DTE::recover()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DTE::isInBullshitState()
|
||||
{
|
||||
if (!cmux_term)
|
||||
return false;
|
||||
return cmux_term->isInBullshitState();
|
||||
}
|
||||
|
||||
void DTE::handle_error(terminal_error err)
|
||||
{
|
||||
if (err == terminal_error::BUFFER_OVERFLOW ||
|
||||
|
@ -554,16 +554,24 @@ static int esp_websocket_client_send_with_exact_opcode(esp_websocket_client_hand
|
||||
bool contained_fin = opcode & WS_TRANSPORT_OPCODES_FIN;
|
||||
|
||||
while (widx < len || opcode) { // allow for sending "current_opcode" only message with len==0
|
||||
if (need_write > client->buffer_size) {
|
||||
need_write = client->buffer_size;
|
||||
if (need_write > client->buffer_size - MAX_WEBSOCKET_HEADER_SIZE) {
|
||||
need_write = client->buffer_size - MAX_WEBSOCKET_HEADER_SIZE;
|
||||
opcode = opcode & ~WS_TRANSPORT_OPCODES_FIN;
|
||||
} else if (contained_fin) {
|
||||
opcode = opcode | WS_TRANSPORT_OPCODES_FIN;
|
||||
}
|
||||
memcpy(client->tx_buffer, data + widx, need_write);
|
||||
memcpy(client->tx_buffer + MAX_WEBSOCKET_HEADER_SIZE, data + widx, need_write);
|
||||
// send with ws specific way and specific opcode
|
||||
wlen = esp_transport_ws_send_raw(client->transport, opcode, (char *)client->tx_buffer, need_write,
|
||||
(timeout == portMAX_DELAY) ? -1 : timeout * portTICK_PERIOD_MS);
|
||||
wlen = esp_transport_ws_send_raw_optimized(client->transport, opcode, (char *)client->tx_buffer, need_write + MAX_WEBSOCKET_HEADER_SIZE,
|
||||
(timeout == portMAX_DELAY) ? -1 : timeout * portTICK_PERIOD_MS);
|
||||
if (wlen >= MAX_WEBSOCKET_HEADER_SIZE)
|
||||
wlen -= MAX_WEBSOCKET_HEADER_SIZE;
|
||||
else if (wlen > 0)
|
||||
{
|
||||
ESP_LOGW(TAG, "couldnt write enough for ws header, wlen=%d", wlen);
|
||||
wlen = 0;
|
||||
}
|
||||
|
||||
if (wlen < 0 || (wlen == 0 && need_write != 0)) {
|
||||
ret = wlen;
|
||||
esp_websocket_free_buf(client, true);
|
||||
@ -1085,6 +1093,17 @@ esp_err_t esp_websocket_client_start(esp_websocket_client_handle_t client)
|
||||
}
|
||||
|
||||
esp_err_t esp_websocket_client_stop(esp_websocket_client_handle_t client)
|
||||
{
|
||||
esp_err_t result = esp_websocket_client_initiate_stop(client);
|
||||
if (result != ESP_OK)
|
||||
return result;
|
||||
|
||||
xEventGroupWaitBits(client->status_bits, STOPPED_BIT, false, true, portMAX_DELAY);
|
||||
client->state = WEBSOCKET_STATE_UNKNOW;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_websocket_client_initiate_stop(esp_websocket_client_handle_t client)
|
||||
{
|
||||
if (client == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
@ -1101,13 +1120,30 @@ esp_err_t esp_websocket_client_stop(esp_websocket_client_handle_t client)
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
|
||||
client->run = false;
|
||||
xEventGroupWaitBits(client->status_bits, STOPPED_BIT, false, true, portMAX_DELAY);
|
||||
client->state = WEBSOCKET_STATE_UNKNOW;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool esp_websocket_client_is_already_stopped(esp_websocket_client_handle_t client)
|
||||
{
|
||||
if (client == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (client->run)
|
||||
return false;
|
||||
|
||||
EventBits_t bits = xEventGroupClearBits(client->status_bits, 0);
|
||||
if (bits & STOPPED_BIT)
|
||||
{
|
||||
client->state = WEBSOCKET_STATE_UNKNOW;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int esp_websocket_client_send_close(esp_websocket_client_handle_t client, int code, const char *additional_data, int total_len, TickType_t timeout)
|
||||
{
|
||||
uint8_t *close_status_data = NULL;
|
||||
|
@ -208,6 +208,38 @@ esp_err_t esp_websocket_client_start(esp_websocket_client_handle_t client);
|
||||
*/
|
||||
esp_err_t esp_websocket_client_stop(esp_websocket_client_handle_t client);
|
||||
|
||||
/**
|
||||
* @brief Initiate an asynchronus stop of the WebSocket connection without
|
||||
* websocket closing handshake
|
||||
*
|
||||
* This API initiates a non-blocking asynchronus stop of ws client. This will
|
||||
* close TCP connection later directly without sending close frames. This is
|
||||
* method is similar to esp_websocket_client_stop() but does not block the calling
|
||||
* task. It is required to then later periodically call
|
||||
* esp_websocket_client_is_already_stopped() to find out when its done and to
|
||||
* finalize the closing of the websocket client.
|
||||
*
|
||||
* Notes:
|
||||
* - Cannot be called from the websocket event handler
|
||||
*
|
||||
* @param[in] client The client
|
||||
*
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t esp_websocket_client_initiate_stop(esp_websocket_client_handle_t client);
|
||||
|
||||
/**
|
||||
* @brief After an asynchronus stop was initiated, this method returns if it is done already.
|
||||
* If this method returns true, it also performed some final cleanup. It should then not
|
||||
* be called anymore unless esp_websocket_client_initiate_stop() is called again.
|
||||
*
|
||||
* @param[in] client The client
|
||||
*
|
||||
* @return
|
||||
* - bool if it is done or not
|
||||
*/
|
||||
bool esp_websocket_client_is_already_stopped(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.
|
||||
|
Reference in New Issue
Block a user