AlteriomPainlessMesh v2.1.1
Easy ESP32/ESP8266 mesh networking with Alteriom IoT extensions
Loading...
Searching...
No Matches
message_queue.hpp
Go to the documentation of this file.
1#ifndef _PAINLESS_MESH_MESSAGE_QUEUE_HPP_
2#define _PAINLESS_MESH_MESSAGE_QUEUE_HPP_
3
4#include <vector>
5#include <algorithm>
8
9// External logger instance
11
12namespace painlessmesh {
13
19 PRIORITY_CRITICAL = 0, // Life/safety critical (O2 alarms, fire alarms)
20 PRIORITY_HIGH = 1, // Important but not life-threatening
21 PRIORITY_NORMAL = 2, // Regular sensor data
22 PRIORITY_LOW = 3 // Non-essential telemetry
23};
24
29 QUEUE_EMPTY, // Queue is empty
30 QUEUE_NORMAL, // Queue has space available
31 QUEUE_75_PERCENT, // Queue is 75% full - warning threshold
32 QUEUE_FULL // Queue is full - dropping low priority messages
33};
34
40 uint32_t id = 0; // Unique message ID
41 MessagePriority priority = PRIORITY_NORMAL; // Message priority level
42 uint32_t timestamp = 0; // When message was queued (millis)
43 uint32_t attempts = 0; // Number of send attempts
44 TSTRING payload; // Message content
45 TSTRING destination; // Cloud endpoint/topic (optional metadata)
46
48
49 QueuedMessage(uint32_t msgId, MessagePriority prio, uint32_t ts,
50 const TSTRING& data, const TSTRING& dest = "")
51 : id(msgId), priority(prio), timestamp(ts), attempts(0),
52 payload(data), destination(dest) {}
53};
54
58struct QueueStats {
59 uint32_t totalQueued = 0; // Total messages ever queued
60 uint32_t totalSent = 0; // Total messages successfully sent
61 uint32_t totalDropped = 0; // Total messages dropped (queue full)
62 uint32_t currentSize = 0; // Current queue size
63 uint32_t maxSize = 0; // Configured max size
64
65 // Priority-specific counts
66 uint32_t criticalQueued = 0;
67 uint32_t highQueued = 0;
68 uint32_t normalQueued = 0;
69 uint32_t lowQueued = 0;
70};
71
72// Queue state change callback type
73typedef std::function<void(QueueState state, uint32_t messageCount)> queueStateChangedCallback_t;
74
113public:
118 explicit MessageQueue(uint32_t maxSize = 1000)
119 : maxQueueSize(maxSize), nextMessageId(1) {
120 }
121
130 uint32_t enqueue(MessagePriority priority, const TSTRING& payload,
131 const TSTRING& destination = "") {
132 // Check if queue is full
133 if (messages.size() >= maxQueueSize) {
134 // Try to make space by removing low priority messages
135 if (!makeSpace(priority)) {
136 stats.totalDropped++;
137 Log(logger::ERROR, "MessageQueue: Failed to queue message (queue full, priority too low)\n");
138 return 0; // Could not queue
139 }
140 }
141
142 uint32_t msgId = nextMessageId++;
143 uint32_t timestamp = millis();
144
145 QueuedMessage msg(msgId, priority, timestamp, payload, destination);
146 messages.push_back(msg);
147
148 // Update statistics
149 stats.totalQueued++;
150 stats.currentSize = messages.size();
151 updatePriorityStats();
152
153 // Check for state change
154 checkStateChange();
155
156 Log(logger::GENERAL, "MessageQueue: Enqueued message #%u (priority=%d, size=%zu/%zu)\n",
157 msgId, priority, messages.size(), (size_t)maxQueueSize);
158
159 return msgId;
160 }
161
167 bool remove(uint32_t messageId) {
168 auto it = std::find_if(messages.begin(), messages.end(),
169 [messageId](const QueuedMessage& msg) {
170 return msg.id == messageId;
171 });
172
173 if (it != messages.end()) {
174 messages.erase(it);
175 stats.totalSent++;
176 stats.currentSize = messages.size();
177 updatePriorityStats();
178 checkStateChange();
179
180 Log(logger::GENERAL, "MessageQueue: Removed message #%u (size=%zu)\n",
181 messageId, messages.size());
182 return true;
183 }
184
185 return false;
186 }
187
192 std::vector<QueuedMessage> getMessages() const {
193 return messages;
194 }
195
200 uint32_t size() const {
201 return messages.size();
202 }
203
209 uint32_t size(MessagePriority priority) const {
210 return std::count_if(messages.begin(), messages.end(),
211 [priority](const QueuedMessage& msg) {
212 return msg.priority == priority;
213 });
214 }
215
220 bool empty() const {
221 return messages.empty();
222 }
223
227 void clear() {
228 messages.clear();
229 stats.currentSize = 0;
230 updatePriorityStats();
231 checkStateChange();
232 Log(logger::GENERAL, "MessageQueue: Cleared all messages\n");
233 }
234
240 return stats;
241 }
242
248 stateChangedCallback = callback;
249 }
250
256 uint32_t incrementAttempts(uint32_t messageId) {
257 auto it = std::find_if(messages.begin(), messages.end(),
258 [messageId](const QueuedMessage& msg) {
259 return msg.id == messageId;
260 });
261
262 if (it != messages.end()) {
263 it->attempts++;
264 return it->attempts;
265 }
266
267 return 0;
268 }
269
275 uint32_t pruneOldMessages(uint32_t maxAgeMs) {
276 uint32_t currentTime = millis();
277 uint32_t removedCount = 0;
278
279 auto it = messages.begin();
280 while (it != messages.end()) {
281 if (currentTime - it->timestamp > maxAgeMs) {
282 it = messages.erase(it);
283 removedCount++;
284 } else {
285 ++it;
286 }
287 }
288
289 if (removedCount > 0) {
290 stats.currentSize = messages.size();
291 updatePriorityStats();
292 checkStateChange();
293 Log(logger::GENERAL, "MessageQueue: Pruned %u old messages\n", removedCount);
294 }
295
296 return removedCount;
297 }
298
299private:
300 std::vector<QueuedMessage> messages;
301 uint32_t maxQueueSize = 1000;
302 uint32_t nextMessageId = 1;
303 QueueStats stats;
304 QueueState currentState = QUEUE_EMPTY;
305 queueStateChangedCallback_t stateChangedCallback;
306
312 bool makeSpace(MessagePriority newPriority) {
313 // CRITICAL messages can always evict lower priority
314 // HIGH messages can evict NORMAL and LOW
315 // NORMAL messages can only evict LOW
316 // LOW messages cannot evict anything
317
318 if (newPriority == PRIORITY_LOW) {
319 return false; // LOW priority can't evict anything
320 }
321
322 // Try to remove lowest priority messages first
323 for (int targetPriority = PRIORITY_LOW; targetPriority > newPriority; targetPriority--) {
324 auto it = std::find_if(messages.begin(), messages.end(),
325 [targetPriority](const QueuedMessage& msg) {
326 return msg.priority == static_cast<MessagePriority>(targetPriority);
327 });
328
329 if (it != messages.end()) {
330 Log(logger::GENERAL, "MessageQueue: Evicting message #%u (priority=%d) to make space\n",
331 it->id, it->priority);
332 messages.erase(it);
333 stats.totalDropped++;
334 return true;
335 }
336 }
337
338 return false;
339 }
340
344 void updatePriorityStats() {
348 stats.lowQueued = size(PRIORITY_LOW);
349 }
350
354 void checkStateChange() {
355 QueueState newState;
356
357 if (messages.empty()) {
358 newState = QUEUE_EMPTY;
359 } else if (messages.size() >= maxQueueSize) {
360 newState = QUEUE_FULL;
361 } else if (messages.size() >= maxQueueSize * 3 / 4) {
362 newState = QUEUE_75_PERCENT;
363 } else {
364 newState = QUEUE_NORMAL;
365 }
366
367 if (newState != currentState) {
368 currentState = newState;
369 if (stateChangedCallback) {
370 stateChangedCallback(currentState, messages.size());
371 }
372 }
373 }
374};
375
376} // namespace painlessmesh
377
378#endif // _PAINLESS_MESH_MESSAGE_QUEUE_HPP_
Definition message_queue.hpp:112
bool empty() const
Definition message_queue.hpp:220
void clear()
Definition message_queue.hpp:227
std::vector< QueuedMessage > getMessages() const
Definition message_queue.hpp:192
void onStateChanged(queueStateChangedCallback_t callback)
Definition message_queue.hpp:247
uint32_t incrementAttempts(uint32_t messageId)
Definition message_queue.hpp:256
uint32_t size(MessagePriority priority) const
Definition message_queue.hpp:209
uint32_t size() const
Definition message_queue.hpp:200
QueueStats getStats() const
Definition message_queue.hpp:239
MessageQueue(uint32_t maxSize=1000)
Definition message_queue.hpp:118
uint32_t pruneOldMessages(uint32_t maxAgeMs)
Definition message_queue.hpp:275
uint32_t enqueue(MessagePriority priority, const TSTRING &payload, const TSTRING &destination="")
Definition message_queue.hpp:130
bool remove(uint32_t messageId)
Definition message_queue.hpp:167
Definition logger.hpp:46
String TSTRING
Definition configuration.hpp:132
painlessmesh::logger::LogClass Log
Definition wifi.cpp:2
@ GENERAL
Definition logger.hpp:26
@ ERROR
Definition logger.hpp:19
Definition wifi.hpp:34
std::function< void(QueueState state, uint32_t messageCount)> queueStateChangedCallback_t
Definition message_queue.hpp:73
MessagePriority
Definition message_queue.hpp:18
@ PRIORITY_HIGH
Definition message_queue.hpp:20
@ PRIORITY_CRITICAL
Definition message_queue.hpp:19
@ PRIORITY_LOW
Definition message_queue.hpp:22
@ PRIORITY_NORMAL
Definition message_queue.hpp:21
QueueState
Definition message_queue.hpp:28
@ QUEUE_EMPTY
Definition message_queue.hpp:29
@ QUEUE_NORMAL
Definition message_queue.hpp:30
@ QUEUE_75_PERCENT
Definition message_queue.hpp:31
@ QUEUE_FULL
Definition message_queue.hpp:32
Definition message_queue.hpp:58
uint32_t lowQueued
Definition message_queue.hpp:69
uint32_t totalSent
Definition message_queue.hpp:60
uint32_t totalQueued
Definition message_queue.hpp:59
uint32_t totalDropped
Definition message_queue.hpp:61
uint32_t currentSize
Definition message_queue.hpp:62
uint32_t highQueued
Definition message_queue.hpp:67
uint32_t criticalQueued
Definition message_queue.hpp:66
uint32_t maxSize
Definition message_queue.hpp:63
uint32_t normalQueued
Definition message_queue.hpp:68
Definition message_queue.hpp:39
QueuedMessage(uint32_t msgId, MessagePriority prio, uint32_t ts, const TSTRING &data, const TSTRING &dest="")
Definition message_queue.hpp:49
TSTRING payload
Definition message_queue.hpp:44
MessagePriority priority
Definition message_queue.hpp:41
QueuedMessage()
Definition message_queue.hpp:47
uint32_t attempts
Definition message_queue.hpp:43
uint32_t timestamp
Definition message_queue.hpp:42
TSTRING destination
Definition message_queue.hpp:45
uint32_t id
Definition message_queue.hpp:40
painlessmesh::logger::LogClass Log
Definition wifi.cpp:2