forked from markqvist/RNode_Firmware
-
-
Notifications
You must be signed in to change notification settings - Fork 33
Expand file tree
/
Copy pathWebSocketConsole.cpp
More file actions
138 lines (118 loc) · 4.44 KB
/
Copy pathWebSocketConsole.cpp
File metadata and controls
138 lines (118 loc) · 4.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#if defined(ENABLE_WEBSOCKETS) && __has_include(<WiFi.h>)
#include "WebSocketConsole.h"
#include "WebSocketServer.h"
#include <cstdint>
#include <cstddef>
// KISS frame boundary. Defined inline here rather than including Framing.h
// because that header defines several globals at file scope (command,
// IN_FRAME, frame_len, ESCAPE) — including it from this second TU would
// cause multiple-definition link errors. The byte value is the KISS
// standard and isn't going to change.
constexpr uint8_t FEND = 0xC0;
// Provided by RNode_Firmware.ino — pushes one byte into serialFIFO when
// space is available, drops it on the floor otherwise. Mirrors the
// behavior of the other inbound paths in buffer_serial().
extern "C" void serial_fifo_push(uint8_t byte);
namespace {
// Max single KISS frame we'll emit over WS. Has to cover the largest
// Provisioning response (msgpack-encoded schema for all registered
// namespaces) plus KISS escaping overhead — schemas exceed 1 KiB on
// builds with LORA_TRANSPORT registered, and any future namespace
// additions only push it higher. The WS framing already supports 16-bit
// ext-len, so this cap can grow up to 0xFFFF before the wire protocol
// would need work. If a frame ever exceeds this, on_serial_write logs
// "OUTBOUND FRAME DROPPED" and the in-flight frame is discarded.
constexpr size_t TX_BUF_CAP = 4096;
WebSocketServer* g_server = nullptr;
uint8_t g_tx_buf[TX_BUF_CAP];
size_t g_tx_len = 0;
bool g_collecting = false; // true between an opening FEND and
// the next FEND that completes a
// non-empty frame
void on_ws_message(const uint8_t* data, size_t len, bool /*is_text*/,
void* /*ctx*/) {
// Hand each byte of the WS frame to the KISS parser. The browser is
// expected to send one full KISS frame per binary WS frame, but the
// serial_poll() KISS parser is byte-stream-driven — it doesn't care
// about our WS frame boundaries.
for (size_t i = 0; i < len; ++i) {
serial_fifo_push(data[i]);
}
}
void flush_outbound_frame() {
if (g_tx_len == 0) return;
if (g_server && g_server->connected()) {
g_server->send_binary(g_tx_buf, g_tx_len);
}
g_tx_len = 0;
}
} // namespace
namespace ws_console {
void init(uint16_t port, bool bind_public) {
if (g_server) return;
g_server = new WebSocketServer(port, bind_public);
g_server->on_message(&on_ws_message);
g_server->begin();
}
void service() {
if (g_server) g_server->service();
// Drop any outbound buffer if the client went away mid-frame — the
// next FEND from serial_write() will start a fresh frame.
if (!client_attached() && g_tx_len > 0) {
g_tx_len = 0;
g_collecting = false;
}
}
void on_serial_write(uint8_t byte) {
if (!g_server) return;
// We don't drop bytes when no client is attached; we just keep the
// buffer empty by never appending. Cheap and avoids the cost of
// formatting frames that go nowhere.
if (!g_server->connected()) {
g_tx_len = 0;
g_collecting = false;
return;
}
if (byte == FEND) {
if (g_collecting && g_tx_len > 1) {
// Frame end: append the closing FEND and emit.
if (g_tx_len < TX_BUF_CAP) g_tx_buf[g_tx_len++] = byte;
flush_outbound_frame();
g_collecting = false;
} else {
// Frame start (or a run of idle FENDs). Reset the buffer
// with a single leading FEND and wait for the first data
// byte.
g_tx_buf[0] = FEND;
g_tx_len = 1;
g_collecting = true;
}
return;
}
if (!g_collecting) {
// Mid-stream byte with no preceding FEND — shouldn't happen
// under correct KISS, but be lenient: drop it.
return;
}
if (g_tx_len < TX_BUF_CAP) {
g_tx_buf[g_tx_len++] = byte;
} else {
// Frame overflows our buffer. Drop the in-flight frame; the
// next FEND will start fresh.
g_tx_len = 0;
g_collecting = false;
}
}
bool client_attached() {
return g_server && g_server->connected();
}
void shutdown() {
if (!g_server) return;
g_server->shutdown();
delete g_server;
g_server = nullptr;
g_tx_len = 0;
g_collecting = false;
}
} // namespace ws_console
#endif // ENABLE_WEBSOCKETS && __has_include(<WiFi.h>)