mirror of
https://github.com/vicliu624/trail-mate.git
synced 2026-11-13 15:47:38 +00:00
152 lines
5.8 KiB
C++
152 lines
5.8 KiB
C++
#include "hostlink/c6/c6_protocol.h"
|
|
#include "platform/esp/idf_common/wireless_companion/c6_companion.h"
|
|
#include "platform/memory/psram_ptr.h"
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cassert>
|
|
#include <cstring>
|
|
#include <mutex>
|
|
#include <vector>
|
|
#if defined(_GLIBCXX_HAS_GTHREADS)
|
|
#include <atomic>
|
|
#include <thread>
|
|
#endif
|
|
#if defined(_WIN32) && !defined(_GLIBCXX_HAS_GTHREADS)
|
|
namespace std
|
|
{
|
|
struct mutex
|
|
{
|
|
void lock() {}
|
|
void unlock() {}
|
|
};
|
|
struct recursive_mutex : mutex
|
|
{
|
|
};
|
|
} // namespace std
|
|
#endif
|
|
using namespace platform::esp::idf_common::wireless_companion;
|
|
constexpr size_t kWifiTcpRxBufferSize = 16384;
|
|
struct Tcp : WifiTcpTransport
|
|
{
|
|
C6CompanionStatus status_{};
|
|
WifiTcpStatus tcp_status_{};
|
|
uint8_t next_tcp_connection_id_ = 1;
|
|
mutable std::mutex tcp_rx_mutex_{};
|
|
std::recursive_mutex receive_owner_mutex_{};
|
|
using TcpRxBuffer = std::array<uint8_t, kWifiTcpRxBufferSize>;
|
|
platform::memory::PsramPtr<TcpRxBuffer> tcp_rx_buffer_{};
|
|
size_t tcp_rx_head_ = 0, tcp_rx_tail_ = 0, tcp_rx_size_ = 0;
|
|
bool send_ok = true;
|
|
bool read_during_open = false;
|
|
unsigned sends = 0;
|
|
bool begin() { return false; }
|
|
void poll() {}
|
|
void copy_text(char* out, const char* in) { std::strcpy(out, in); }
|
|
bool send_wifi_tcp_command(const tm_c6_wifi_tcp_header_t& header, const uint8_t*, size_t)
|
|
{
|
|
++sends;
|
|
if (read_during_open && header.operation == TM_C6_WIFI_TCP_OPEN)
|
|
{
|
|
uint8_t byte = 0;
|
|
assert(readTcp(&byte, 1) == 0);
|
|
}
|
|
return send_ok;
|
|
}
|
|
#include "c6_tcp_api.inc"
|
|
#include "c6_tcp_buffer.inc"
|
|
void event(uint8_t operation, size_t data_len = 0)
|
|
{
|
|
tm_c6_wifi_tcp_header_t header{};
|
|
header.operation = operation;
|
|
struct
|
|
{
|
|
std::vector<uint8_t> payload;
|
|
} frame;
|
|
frame.payload.resize(sizeof(header) + data_len);
|
|
#include "c6_tcp_events.inc"
|
|
}
|
|
Tcp()
|
|
{
|
|
status_.started = status_.present = status_.wifi_connected = true;
|
|
status_.supported_features = TM_C6_FEATURE_WIFI_TCP_PROXY;
|
|
}
|
|
};
|
|
int main()
|
|
{
|
|
Tcp tcp;
|
|
assert(live_bytes == 0 && allocations == 0); // Idle Wi-Fi has no TCP payload storage.
|
|
allocation_fails = true;
|
|
assert(!tcp.openTcp("example.org", 443) && tcp.sends == 0 && live_bytes == 0);
|
|
allocation_fails = false;
|
|
tcp.send_ok = false;
|
|
assert(!tcp.openTcp("example.org", 443) && live_bytes == 0);
|
|
tcp.send_ok = true;
|
|
tcp.read_during_open = true;
|
|
assert(tcp.openTcp("example.org", 443) && live_bytes == 16384);
|
|
tcp.closeTcp();
|
|
tcp.event(TM_C6_WIFI_TCP_CLOSED);
|
|
tcp.read_during_open = false;
|
|
std::array<uint8_t, 12000> data{};
|
|
for (size_t i = 0; i < data.size(); ++i) data[i] = static_cast<uint8_t>(i);
|
|
std::array<uint8_t, 12000> out{};
|
|
for (unsigned cycle = 0; cycle < 100; ++cycle)
|
|
{
|
|
assert(tcp.openTcp("example.org", 443) && live_bytes == 16384);
|
|
tcp.set_tcp_state(WifiTcpState::Connected, TM_C6_OK, "connected");
|
|
assert(tcp.append_tcp_rx(data.data(), data.size()));
|
|
assert(!tcp.append_tcp_rx(data.data(), data.size())); // Bounded, no growth on overflow.
|
|
assert(tcp.readTcp(out.data(), 10000) == 10000);
|
|
assert(std::equal(out.begin(), out.begin() + 10000, data.begin()));
|
|
assert(tcp.append_tcp_rx(data.data(), data.size())); // Wrap around the ring.
|
|
tcp.set_tcp_state(WifiTcpState::Disconnected, TM_C6_OK, "remote_closed");
|
|
assert(live_bytes == 16384); // Preserve unread bytes after EOF.
|
|
assert(tcp.readTcp(out.data(), 2000) == 2000);
|
|
assert(std::equal(out.begin(), out.begin() + 2000, data.begin() + 10000));
|
|
assert(tcp.readTcp(out.data(), out.size()) == out.size());
|
|
assert(out == data && live_bytes == 0);
|
|
assert(!tcp.append_tcp_rx(data.data(), 1)); // No storage resurrection on stale data.
|
|
assert(tcp.openTcp("example.org", 443));
|
|
assert(tcp.append_tcp_rx(data.data(), 20));
|
|
tcp.set_tcp_state(WifiTcpState::Disconnected, TM_C6_OK, "remote_closed");
|
|
tcp.closeTcp(); // Explicit close discards unread bytes and releases memory.
|
|
assert(live_bytes == 0);
|
|
}
|
|
assert(tcp.openTcp("example.org", 443));
|
|
tcp.reset_tcp_state(WifiTcpState::Disconnected, "wifi_disconnected");
|
|
assert(live_bytes == 0 && tcp.tcp_status_.buffered_bytes == 0);
|
|
assert(tcp.openTcp("example.org", 443));
|
|
tcp.event(TM_C6_WIFI_TCP_OPENED);
|
|
tcp.event(TM_C6_WIFI_TCP_DATA, 20);
|
|
assert(tcp.tcp_status_.buffered_bytes == 20);
|
|
tcp.closeTcp();
|
|
assert(live_bytes == 0 && tcp.tcp_status_.state == WifiTcpState::Closing);
|
|
const auto sends = tcp.sends;
|
|
tcp.event(TM_C6_WIFI_TCP_OPENED); // A delayed OPENED cannot resurrect a locally closed session.
|
|
tcp.event(TM_C6_WIFI_TCP_DATA, 20);
|
|
assert(live_bytes == 0 && tcp.sends == sends && tcp.tcp_status_.state == WifiTcpState::Closing);
|
|
tcp.event(TM_C6_WIFI_TCP_CLOSED);
|
|
assert(live_bytes == 0 && tcp.tcp_status_.state == WifiTcpState::Disconnected);
|
|
#if defined(_GLIBCXX_HAS_GTHREADS)
|
|
std::atomic<bool> running{true};
|
|
std::thread reader([&]
|
|
{
|
|
std::array<uint8_t, 64> bytes{};
|
|
while (running.load())
|
|
{
|
|
assert(tcp.readTcp(bytes.data(), bytes.size()) <= bytes.size());
|
|
assert(tcp.tcpStatus().buffered_bytes <= kWifiTcpRxBufferSize);
|
|
} });
|
|
for (unsigned i = 0; i < 500; ++i)
|
|
{
|
|
assert(tcp.openTcp("example.org", 443));
|
|
tcp.set_tcp_state(WifiTcpState::Connected, TM_C6_OK, "connected");
|
|
assert(tcp.append_tcp_rx(data.data(), data.size()));
|
|
tcp.set_tcp_state(WifiTcpState::Disconnected, TM_C6_OK, "remote_closed");
|
|
tcp.reset_tcp_state(WifiTcpState::Disconnected, "wifi_disconnected");
|
|
}
|
|
running.store(false);
|
|
reader.join();
|
|
assert(live_bytes == 0);
|
|
#endif
|
|
}
|