mirror of
https://github.com/ratspeak/ratdeck.git
synced 2026-05-15 11:55:13 +00:00
9b7980665c
- Rewrite MessageView with word-wrapped chat bubbles, timestamps, incoming/outgoing alignment, pixel-based scrolling - Add IdentityManager for multi-identity support (create/switch/delete, 8 slots max, per-identity display names) - Fix transport mode: default to endpoint (no rebroadcast) — was acting as transport node, rebroadcasting every TCP announce over LoRa causing 5-11 second UI freezes per TX. Loop time: 11146ms → 6ms - Add Transport Node toggle in Settings > Network - TCP interface now drains up to 10 frames per loop (was 1) - Reduce LoRa debug polling from 5s to 30s
130 lines
3.5 KiB
C++
130 lines
3.5 KiB
C++
#include "TCPClientInterface.h"
|
|
#include "config/Config.h"
|
|
|
|
TCPClientInterface::TCPClientInterface(const char* host, uint16_t port, const char* name)
|
|
: RNS::InterfaceImpl(name), _host(host), _port(port)
|
|
{
|
|
_IN = true;
|
|
_OUT = true;
|
|
_bitrate = 1000000;
|
|
_HW_MTU = 500;
|
|
}
|
|
|
|
TCPClientInterface::~TCPClientInterface() {
|
|
stop();
|
|
}
|
|
|
|
bool TCPClientInterface::start() {
|
|
_online = true;
|
|
tryConnect();
|
|
return true;
|
|
}
|
|
|
|
void TCPClientInterface::stop() {
|
|
_online = false;
|
|
if (_client.connected()) {
|
|
_client.stop();
|
|
Serial.printf("[TCP] Disconnected from %s:%d\n", _host.c_str(), _port);
|
|
}
|
|
}
|
|
|
|
void TCPClientInterface::tryConnect() {
|
|
_lastAttempt = millis();
|
|
Serial.printf("[TCP] Connecting to %s:%d...\n", _host.c_str(), _port);
|
|
|
|
if (_client.connect(_host.c_str(), _port, TCP_CONNECT_TIMEOUT_MS)) {
|
|
Serial.printf("[TCP] Connected to %s:%d\n", _host.c_str(), _port);
|
|
} else {
|
|
Serial.printf("[TCP] Failed to connect to %s:%d\n", _host.c_str(), _port);
|
|
}
|
|
}
|
|
|
|
void TCPClientInterface::loop() {
|
|
if (!_online) return;
|
|
|
|
// Auto-reconnect
|
|
if (!_client.connected()) {
|
|
if (millis() - _lastAttempt >= TCP_RECONNECT_INTERVAL_MS) {
|
|
tryConnect();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Drain multiple incoming frames per loop (up to 10)
|
|
for (int i = 0; i < 10 && _client.available(); i++) {
|
|
unsigned long rxStart = millis();
|
|
int len = readFrame(_rxBuffer, sizeof(_rxBuffer));
|
|
if (len > 0) {
|
|
RNS::Bytes data(_rxBuffer, len);
|
|
Serial.printf("[TCP] RX %d bytes from %s:%d (%lums)\n",
|
|
len, _host.c_str(), _port, millis() - rxStart);
|
|
InterfaceImpl::handle_incoming(data);
|
|
} else {
|
|
break; // Incomplete frame, wait for more data
|
|
}
|
|
}
|
|
}
|
|
|
|
void TCPClientInterface::send_outgoing(const RNS::Bytes& data) {
|
|
if (!_online || !_client.connected()) return;
|
|
|
|
sendFrame(data.data(), data.size());
|
|
Serial.printf("[TCP] TX %d bytes to %s:%d\n", (int)data.size(), _host.c_str(), _port);
|
|
InterfaceImpl::handle_outgoing(data);
|
|
}
|
|
|
|
// HDLC-like framing: [0x7E] [escaped data] [0x7E]
|
|
void TCPClientInterface::sendFrame(const uint8_t* data, size_t len) {
|
|
_client.write(FRAME_START);
|
|
for (size_t i = 0; i < len; i++) {
|
|
if (data[i] == FRAME_START || data[i] == FRAME_ESC) {
|
|
_client.write(FRAME_ESC);
|
|
_client.write(data[i] ^ FRAME_XOR);
|
|
} else {
|
|
_client.write(data[i]);
|
|
}
|
|
}
|
|
_client.write(FRAME_START);
|
|
_client.flush();
|
|
}
|
|
|
|
int TCPClientInterface::readFrame(uint8_t* buffer, size_t maxLen) {
|
|
if (!_client.available()) return 0;
|
|
|
|
bool inFrame = false;
|
|
bool escaped = false;
|
|
size_t pos = 0;
|
|
int bytesRead = 0;
|
|
constexpr int MAX_BYTES_PER_CALL = 512;
|
|
|
|
while (_client.available() && pos < maxLen && bytesRead < MAX_BYTES_PER_CALL) {
|
|
uint8_t b = _client.read();
|
|
bytesRead++;
|
|
|
|
if (b == FRAME_START) {
|
|
if (inFrame && pos > 0) {
|
|
return pos; // End of frame
|
|
}
|
|
inFrame = true;
|
|
pos = 0;
|
|
continue;
|
|
}
|
|
|
|
if (!inFrame) continue;
|
|
|
|
if (b == FRAME_ESC) {
|
|
escaped = true;
|
|
continue;
|
|
}
|
|
|
|
if (escaped) {
|
|
buffer[pos++] = b ^ FRAME_XOR;
|
|
escaped = false;
|
|
} else {
|
|
buffer[pos++] = b;
|
|
}
|
|
}
|
|
|
|
return 0; // Incomplete frame
|
|
}
|