@alteriom/painlessmesh 1.8.2 → 1.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/README.md +62 -11
- package/RELEASE_GUIDE.md +57 -16
- package/docs/ARDUINO_LIBRARY_MANAGER_SUBMISSION.md +331 -0
- package/docs/features/DIAGNOSTICS_API.md +534 -0
- package/docs/getting-started/arduino-manual-install.md +313 -0
- package/docs/implementation/BRIDGE_ARCHITECTURE_IMPLEMENTATION.md +340 -0
- package/docs/implementation/BRIDGE_HEALTH_MONITORING_IMPLEMENTATION.md +213 -0
- package/docs/implementation/BRIDGE_STATUS_FEATURE.md +635 -0
- package/docs/implementation/DIAGNOSTICS_API_IMPLEMENTATION.md +232 -0
- package/docs/implementation/IMPLEMENTATION_COMPLETE.md +228 -0
- package/docs/implementation/IMPLEMENTATION_NTP_TIME_SYNC.md +325 -0
- package/docs/implementation/IMPLEMENTATION_SUMMARY.md +316 -0
- package/docs/implementation/MESSAGE_QUEUE_IMPLEMENTATION.md +405 -0
- package/docs/implementation/MULTI_BRIDGE_IMPLEMENTATION.md +520 -0
- package/docs/implementation/NTP_TIME_SYNC_FEATURE.md +392 -0
- package/docs/internal/CUSTOM_AGENT_ANALYSIS.md +391 -0
- package/docs/internal/ISSUE_65_VERIFICATION.md +947 -0
- package/docs/internal/ISSUE_66_CLOSURE.md +249 -0
- package/docs/internal/ISSUE_66_STATUS.md +316 -0
- package/docs/internal/PR_SUMMARY.md +315 -0
- package/docs/internal/REVIEW_SUMMARY.md +332 -0
- package/docs/releases/PUBLISH_v1.8.0_INSTRUCTIONS.md +163 -0
- package/docs/releases/QUICK_START_RELEASES.md +113 -0
- package/docs/releases/RELEASE_CHECKLIST_v1.8.0.md +331 -0
- package/docs/releases/RELEASE_CHECKLIST_v1.8.2.md +309 -0
- package/docs/releases/RELEASE_NOTES_v1.8.0.md +685 -0
- package/docs/releases/RELEASE_NOTES_v1.8.1.md +221 -0
- package/docs/releases/RELEASE_NOTES_v1.8.2.md +421 -0
- package/docs/releases/RELEASE_NOTES_v1.8.3.md +292 -0
- package/docs/troubleshooting/ARDUINO_IDE_VERSION_FIX_SUMMARY.md +229 -0
- package/docs/troubleshooting/ARDUINO_LIBRARY_NAME_FIX.md +197 -0
- package/docs/troubleshooting/NPM_PUBLISHING_ISSUE_SUMMARY.md +110 -0
- package/docs/troubleshooting/station-reconnection-issues.md +172 -0
- package/examples/priority/README.md +274 -0
- package/examples/priority/priority_basic_example.ino +115 -0
- package/examples/priority/priority_with_queue.ino +249 -0
- package/examples/routing_demo/README.md +172 -0
- package/examples/routing_demo/routing_demo.ino +102 -0
- package/library.json +1 -1
- package/library.properties +3 -3
- package/package.json +1 -1
- package/src/arduino/wifi.hpp +49 -16
- package/src/painlessMesh.h +15 -0
- package/src/painlessMeshSTA.cpp +7 -1
- package/src/painlessmesh/buffer.hpp +218 -37
- package/src/painlessmesh/connection.hpp +21 -1
- package/src/painlessmesh/mesh.hpp +253 -19
- package/src/painlessmesh/router.hpp +31 -0
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
#define _PAINLESS_MESH_BUFFER_HPP_
|
|
3
3
|
|
|
4
4
|
#include <list>
|
|
5
|
+
#include <map>
|
|
6
|
+
#include <queue>
|
|
5
7
|
|
|
6
8
|
#include "Arduino.h"
|
|
7
9
|
#include "painlessmesh/configuration.hpp"
|
|
@@ -103,30 +105,65 @@ inline void ReceiveBuffer<std::string>::stringAppend(std::string &buffer,
|
|
|
103
105
|
}
|
|
104
106
|
#endif
|
|
105
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Structure to hold a message with its priority level
|
|
110
|
+
*/
|
|
111
|
+
template <class T>
|
|
112
|
+
struct PrioritizedMessage {
|
|
113
|
+
T message;
|
|
114
|
+
uint8_t priority; // 0=CRITICAL, 1=HIGH, 2=NORMAL, 3=LOW
|
|
115
|
+
|
|
116
|
+
PrioritizedMessage(const T& msg, uint8_t prio = 2) : message(msg), priority(prio) {}
|
|
117
|
+
};
|
|
118
|
+
|
|
106
119
|
/**
|
|
107
120
|
* \brief SentBuffer stores messages (strings) and allows them to be read in any
|
|
108
|
-
* length
|
|
121
|
+
* length with priority-based scheduling
|
|
109
122
|
*/
|
|
110
123
|
template <class T>
|
|
111
124
|
class SentBuffer {
|
|
112
125
|
public:
|
|
113
|
-
SentBuffer() {
|
|
126
|
+
SentBuffer() {
|
|
127
|
+
current_read_iterator = prioritizedMessages.end();
|
|
128
|
+
};
|
|
114
129
|
|
|
115
130
|
/**
|
|
116
|
-
* push a message into the buffer.
|
|
131
|
+
* push a message into the buffer with multi-level priority support.
|
|
117
132
|
*
|
|
118
|
-
* \param
|
|
133
|
+
* \param message The message to queue
|
|
134
|
+
* \param priority Whether this is a high priority message (legacy bool API)
|
|
119
135
|
*
|
|
120
|
-
* High priority messages will be sent to the front of the buffer
|
|
136
|
+
* Legacy API: High priority messages (true) will be sent to the front of the buffer
|
|
137
|
+
* This maintains backward compatibility with existing code.
|
|
121
138
|
*/
|
|
122
139
|
void push(const T &message, bool priority = false) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
140
|
+
// Legacy API: map bool to uint8_t priority (false=2 NORMAL, true=1 HIGH)
|
|
141
|
+
uint8_t priorityLevel = priority ? 1 : 2;
|
|
142
|
+
pushWithPriority(message, priorityLevel);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* push a message with explicit priority level (0-3)
|
|
147
|
+
*
|
|
148
|
+
* \param message The message to queue
|
|
149
|
+
* \param priorityLevel Priority level: 0=CRITICAL, 1=HIGH, 2=NORMAL, 3=LOW
|
|
150
|
+
*
|
|
151
|
+
* Messages are scheduled in priority order. Within same priority, FIFO order is maintained.
|
|
152
|
+
*/
|
|
153
|
+
void pushWithPriority(const T &message, uint8_t priorityLevel) {
|
|
154
|
+
// Clamp priority to valid range
|
|
155
|
+
if (priorityLevel > 3) priorityLevel = 3;
|
|
156
|
+
|
|
157
|
+
prioritizedMessages.push_back(PrioritizedMessage<T>(message, priorityLevel));
|
|
158
|
+
|
|
159
|
+
// Track statistics
|
|
160
|
+
totalMessagesQueued++;
|
|
161
|
+
switch(priorityLevel) {
|
|
162
|
+
case 0: criticalQueued++; break;
|
|
163
|
+
case 1: highQueued++; break;
|
|
164
|
+
case 2: normalQueued++; break;
|
|
165
|
+
case 3: lowQueued++; break;
|
|
166
|
+
}
|
|
130
167
|
}
|
|
131
168
|
|
|
132
169
|
/**
|
|
@@ -135,12 +172,14 @@ class SentBuffer {
|
|
|
135
172
|
* Returns the actual length available (<= the requested length
|
|
136
173
|
*/
|
|
137
174
|
size_t requestLength(size_t buffer_length) {
|
|
138
|
-
|
|
175
|
+
// Use highest priority message available
|
|
176
|
+
auto* msg = getNextMessage();
|
|
177
|
+
if (!msg)
|
|
139
178
|
return 0;
|
|
140
179
|
else
|
|
141
180
|
// String.toCharArray automatically turns the last character into
|
|
142
181
|
// a \0, we need the extra space to deal with that annoyance
|
|
143
|
-
return std::min(buffer_length - 1,
|
|
182
|
+
return std::min(buffer_length - 1, msg->length() + 1);
|
|
144
183
|
}
|
|
145
184
|
|
|
146
185
|
/**
|
|
@@ -156,20 +195,29 @@ class SentBuffer {
|
|
|
156
195
|
// Note that toCharrArray always null terminates
|
|
157
196
|
// independent of whether the whole string was read so we use one extra
|
|
158
197
|
// space
|
|
159
|
-
|
|
160
|
-
|
|
198
|
+
auto* msg = getNextMessage();
|
|
199
|
+
if (msg) {
|
|
200
|
+
msg->toCharArray(buf.buffer, length + 1);
|
|
201
|
+
last_read_size = length;
|
|
202
|
+
last_read_priority = getCurrentMessagePriority();
|
|
203
|
+
}
|
|
161
204
|
}
|
|
162
205
|
|
|
163
206
|
/**
|
|
164
|
-
* Returns a pointer directly to the
|
|
207
|
+
* Returns a pointer directly to the highest priority message
|
|
165
208
|
*
|
|
166
209
|
* Note the user should first make sure the requested length is available
|
|
167
210
|
* using `SentBuffer.requestLength()`, otherwise this function might fail.
|
|
168
211
|
* Note that if multiple messages are read then they are separated using '\0'.
|
|
169
212
|
*/
|
|
170
213
|
const char *readPtr(size_t length) {
|
|
171
|
-
|
|
172
|
-
|
|
214
|
+
auto* msg = getNextMessage();
|
|
215
|
+
if (msg) {
|
|
216
|
+
last_read_size = length;
|
|
217
|
+
last_read_priority = getCurrentMessagePriority();
|
|
218
|
+
return msg->c_str();
|
|
219
|
+
}
|
|
220
|
+
return nullptr;
|
|
173
221
|
}
|
|
174
222
|
|
|
175
223
|
/**
|
|
@@ -178,27 +226,156 @@ class SentBuffer {
|
|
|
178
226
|
* Should be called after a call of read() to clear the buffer.
|
|
179
227
|
*/
|
|
180
228
|
void freeRead() {
|
|
181
|
-
if (
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
229
|
+
if (prioritizedMessages.empty()) {
|
|
230
|
+
last_read_size = 0;
|
|
231
|
+
current_read_iterator = prioritizedMessages.end();
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Find and remove the message that was just read (highest priority)
|
|
236
|
+
auto it = findHighestPriorityMessage();
|
|
237
|
+
if (it != prioritizedMessages.end()) {
|
|
238
|
+
auto& msg = it->message;
|
|
239
|
+
|
|
240
|
+
if (last_read_size == msg.length() + 1) {
|
|
241
|
+
// Whole message was read, remove it
|
|
242
|
+
// Track statistics
|
|
243
|
+
switch(it->priority) {
|
|
244
|
+
case 0: criticalSent++; break;
|
|
245
|
+
case 1: highSent++; break;
|
|
246
|
+
case 2: normalSent++; break;
|
|
247
|
+
case 3: lowSent++; break;
|
|
248
|
+
}
|
|
249
|
+
prioritizedMessages.erase(it);
|
|
250
|
+
current_read_iterator = prioritizedMessages.end(); // Reset iterator
|
|
251
|
+
clean = true;
|
|
252
|
+
} else {
|
|
253
|
+
// Partial message read, remove the read portion
|
|
254
|
+
stringEraseFront(msg, last_read_size);
|
|
255
|
+
// Keep current_read_iterator pointing to this message for next read
|
|
256
|
+
clean = false;
|
|
257
|
+
}
|
|
188
258
|
}
|
|
189
259
|
last_read_size = 0;
|
|
190
260
|
}
|
|
191
261
|
|
|
192
|
-
bool empty() { return
|
|
262
|
+
bool empty() { return prioritizedMessages.empty(); }
|
|
193
263
|
|
|
194
|
-
void clear() {
|
|
264
|
+
void clear() {
|
|
265
|
+
prioritizedMessages.clear();
|
|
266
|
+
current_read_iterator = prioritizedMessages.end();
|
|
267
|
+
totalMessagesQueued = 0;
|
|
268
|
+
criticalQueued = highQueued = normalQueued = lowQueued = 0;
|
|
269
|
+
criticalSent = highSent = normalSent = lowSent = 0;
|
|
270
|
+
}
|
|
195
271
|
|
|
196
|
-
size_t size() { return
|
|
272
|
+
size_t size() { return prioritizedMessages.size(); }
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Get priority of the last read message
|
|
276
|
+
*/
|
|
277
|
+
uint8_t getLastReadPriority() const { return last_read_priority; }
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Get statistics about queued and sent messages
|
|
281
|
+
*/
|
|
282
|
+
struct SendStats {
|
|
283
|
+
uint32_t totalQueued;
|
|
284
|
+
uint32_t criticalQueued;
|
|
285
|
+
uint32_t highQueued;
|
|
286
|
+
uint32_t normalQueued;
|
|
287
|
+
uint32_t lowQueued;
|
|
288
|
+
uint32_t criticalSent;
|
|
289
|
+
uint32_t highSent;
|
|
290
|
+
uint32_t normalSent;
|
|
291
|
+
uint32_t lowSent;
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
SendStats getStats() const {
|
|
295
|
+
SendStats stats;
|
|
296
|
+
stats.totalQueued = totalMessagesQueued;
|
|
297
|
+
stats.criticalQueued = criticalQueued;
|
|
298
|
+
stats.highQueued = highQueued;
|
|
299
|
+
stats.normalQueued = normalQueued;
|
|
300
|
+
stats.lowQueued = lowQueued;
|
|
301
|
+
stats.criticalSent = criticalSent;
|
|
302
|
+
stats.highSent = highSent;
|
|
303
|
+
stats.normalSent = normalSent;
|
|
304
|
+
stats.lowSent = lowSent;
|
|
305
|
+
return stats;
|
|
306
|
+
}
|
|
197
307
|
|
|
198
308
|
private:
|
|
199
309
|
size_t last_read_size = 0;
|
|
310
|
+
uint8_t last_read_priority = 2; // NORMAL default
|
|
200
311
|
bool clean = true;
|
|
201
|
-
std::list<T
|
|
312
|
+
std::list<PrioritizedMessage<T>> prioritizedMessages;
|
|
313
|
+
typename std::list<PrioritizedMessage<T>>::iterator current_read_iterator;
|
|
314
|
+
|
|
315
|
+
// Statistics tracking
|
|
316
|
+
uint32_t totalMessagesQueued = 0;
|
|
317
|
+
uint32_t criticalQueued = 0;
|
|
318
|
+
uint32_t highQueued = 0;
|
|
319
|
+
uint32_t normalQueued = 0;
|
|
320
|
+
uint32_t lowQueued = 0;
|
|
321
|
+
uint32_t criticalSent = 0;
|
|
322
|
+
uint32_t highSent = 0;
|
|
323
|
+
uint32_t normalSent = 0;
|
|
324
|
+
uint32_t lowSent = 0;
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Find the highest priority message in the buffer
|
|
328
|
+
* Returns iterator to the highest priority message, or end() if empty
|
|
329
|
+
*
|
|
330
|
+
* Special handling: If we're in the middle of reading a message (not clean),
|
|
331
|
+
* we continue with that message even if higher priority messages arrive.
|
|
332
|
+
* This maintains backward compatibility with partial read behavior.
|
|
333
|
+
*/
|
|
334
|
+
typename std::list<PrioritizedMessage<T>>::iterator findHighestPriorityMessage() {
|
|
335
|
+
if (prioritizedMessages.empty()) {
|
|
336
|
+
return prioritizedMessages.end();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// If we're in the middle of a partial read (!clean), continue with current_read_iterator
|
|
340
|
+
// This maintains backward compatibility with the original buffer behavior
|
|
341
|
+
if (!clean && current_read_iterator != prioritizedMessages.end()) {
|
|
342
|
+
return current_read_iterator;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Find message with lowest priority value (0=CRITICAL is highest)
|
|
346
|
+
auto highest = prioritizedMessages.begin();
|
|
347
|
+
for (auto it = prioritizedMessages.begin(); it != prioritizedMessages.end(); ++it) {
|
|
348
|
+
if (it->priority < highest->priority) {
|
|
349
|
+
highest = it;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Cache this for partial reads
|
|
354
|
+
current_read_iterator = highest;
|
|
355
|
+
return highest;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Get pointer to next message to send (highest priority)
|
|
360
|
+
*/
|
|
361
|
+
T* getNextMessage() {
|
|
362
|
+
auto it = findHighestPriorityMessage();
|
|
363
|
+
if (it != prioritizedMessages.end()) {
|
|
364
|
+
return &(it->message);
|
|
365
|
+
}
|
|
366
|
+
return nullptr;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Get priority of the current highest priority message
|
|
371
|
+
*/
|
|
372
|
+
uint8_t getCurrentMessagePriority() {
|
|
373
|
+
auto it = findHighestPriorityMessage();
|
|
374
|
+
if (it != prioritizedMessages.end()) {
|
|
375
|
+
return it->priority;
|
|
376
|
+
}
|
|
377
|
+
return 2; // NORMAL default
|
|
378
|
+
}
|
|
202
379
|
|
|
203
380
|
inline void stringEraseFront(T &string, size_t length) {
|
|
204
381
|
string.remove(0, length);
|
|
@@ -208,13 +385,17 @@ class SentBuffer {
|
|
|
208
385
|
#ifdef PAINLESSMESH_ENABLE_STD_STRING
|
|
209
386
|
template <>
|
|
210
387
|
inline void SentBuffer<std::string>::read(size_t length, temp_buffer_t &buf) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
388
|
+
auto* msg = getNextMessage();
|
|
389
|
+
if (msg) {
|
|
390
|
+
msg->copy(buf.buffer, length);
|
|
391
|
+
// Mimic String.toCharArray behaviour, which will insert
|
|
392
|
+
// null termination at the end of original string and the last
|
|
393
|
+
// character
|
|
394
|
+
if (length == msg->length() + 1) buf.buffer[length - 1] = '\0';
|
|
395
|
+
buf.buffer[length] = '\0';
|
|
396
|
+
last_read_size = length;
|
|
397
|
+
last_read_priority = getCurrentMessagePriority();
|
|
398
|
+
}
|
|
218
399
|
}
|
|
219
400
|
|
|
220
401
|
template <>
|
|
@@ -129,6 +129,18 @@ class BufferedConnection
|
|
|
129
129
|
sentBufferTask.forceNextIteration();
|
|
130
130
|
return true;
|
|
131
131
|
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Write data with explicit priority level (0-3)
|
|
135
|
+
*
|
|
136
|
+
* \param data The data to send
|
|
137
|
+
* \param priorityLevel Priority level: 0=CRITICAL, 1=HIGH, 2=NORMAL, 3=LOW
|
|
138
|
+
*/
|
|
139
|
+
bool writeWithPriority(const TSTRING &data, uint8_t priorityLevel) {
|
|
140
|
+
sentBuffer.pushWithPriority(data, priorityLevel);
|
|
141
|
+
sentBufferTask.forceNextIteration();
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
132
144
|
|
|
133
145
|
void onDisconnect(std::function<void()> callback) {
|
|
134
146
|
disconnectCallback = callback;
|
|
@@ -164,7 +176,15 @@ class BufferedConnection
|
|
|
164
176
|
auto data_ptr = sentBuffer.readPtr(len);
|
|
165
177
|
auto written = client->write(data_ptr, len, 1);
|
|
166
178
|
if (written == len) {
|
|
167
|
-
|
|
179
|
+
// Get priority before freeing the read buffer
|
|
180
|
+
uint8_t msgPriority = sentBuffer.getLastReadPriority();
|
|
181
|
+
|
|
182
|
+
// Call send() for high priority messages (CRITICAL=0, HIGH=1)
|
|
183
|
+
// This ensures they are transmitted immediately
|
|
184
|
+
if (msgPriority <= 1) {
|
|
185
|
+
client->send();
|
|
186
|
+
}
|
|
187
|
+
|
|
168
188
|
sentBuffer.freeRead();
|
|
169
189
|
sentBufferTask.forceNextIteration();
|
|
170
190
|
return true;
|