AlteriomPainlessMesh v2.1.1
Easy ESP32/ESP8266 mesh networking with Alteriom IoT extensions
Loading...
Searching...
No Matches
ack.hpp
Go to the documentation of this file.
1#ifndef PAINLESS_MESH_ACK_HPP
2#define PAINLESS_MESH_ACK_HPP
3
4#include <functional>
5#include <list>
6#include <map>
7#include <set>
8
12
14
15// Maximum number of messages that may await acknowledgment at once.
16// Bounded to protect the small ESP8266 heap; override at build time if
17// your application legitimately needs more concurrent tracked sends.
18#ifndef PAINLESSMESH_MAX_PENDING_ACKS
19#define PAINLESSMESH_MAX_PENDING_ACKS 32
20#endif
21
22// Maximum number of delayed broadcast acknowledgments queued by a receiver.
23// A single shared scheduler task drains the queue after the jitter delay.
24#ifndef PAINLESSMESH_MAX_QUEUED_BROADCAST_ACKS
25#define PAINLESSMESH_MAX_QUEUED_BROADCAST_ACKS 32
26#endif
27
28namespace painlessmesh {
29
41namespace ack {
42
52typedef std::function<void(uint32_t nodeId, bool delivered, uint32_t latencyMs)>
54
64 public:
66 uint32_t messageId = 0;
67
68 MessageAckPackage() : SinglePackage(protocol::MESSAGE_ACK) {}
69
70 MessageAckPackage(uint32_t fromNode, uint32_t destNode, uint32_t msgId)
71 : SinglePackage(protocol::MESSAGE_ACK) {
72 from = fromNode;
73 dest = destNode;
74 messageId = msgId;
75 }
76
77 MessageAckPackage(JsonObject jsonObj) : SinglePackage(jsonObj) {
78 messageId = jsonObj["msgId"] | (uint32_t)0;
79 }
80
81 JsonObject addTo(JsonObject&& jsonObj) const {
82 jsonObj = SinglePackage::addTo(std::move(jsonObj));
83 jsonObj["msgId"] = messageId;
84 return jsonObj;
85 }
86
87#if ARDUINOJSON_VERSION_MAJOR < 7
88 size_t jsonObjectSize() const { return JSON_OBJECT_SIZE(noJsonFields + 1); }
89#endif
90};
91
95struct PendingAck {
97 uint32_t sentAt = 0;
98 uint32_t timeoutMs = 0;
99 std::set<uint32_t> waitingFor;
100};
101
115
124 public:
133 void seed(uint32_t value) { counter = value; }
134
136 uint32_t nextMessageId() {
137 if (++counter == 0) ++counter;
138 return counter;
139 }
140
142 bool full() const { return entries.size() >= PAINLESSMESH_MAX_PENDING_ACKS; }
143
155 bool track(uint32_t msgId, const std::list<uint32_t>& destinations,
156 deliveryCallback_t callback, uint32_t timeoutMs, uint32_t now) {
157 if (!callback || destinations.empty()) return false;
158 if (full()) {
160 "AckTracker: pending-ack limit (%u) reached, not tracking %u\n",
161 (unsigned)PAINLESSMESH_MAX_PENDING_ACKS, msgId);
162 return false;
163 }
164 PendingAck entry;
165 entry.callback = callback;
166 entry.sentAt = now;
167 entry.timeoutMs = timeoutMs;
168 entry.waitingFor.insert(destinations.begin(), destinations.end());
169 entries[msgId] = std::move(entry);
170 return true;
171 }
172
180 bool handleAck(uint32_t msgId, uint32_t fromNode, uint32_t now) {
181 std::list<DeliveryResult> results;
182 auto matched = collectAck(msgId, fromNode, now, results);
183 for (auto&& result : results) {
184 result.callback(result.nodeId, result.delivered, result.latencyMs);
185 }
186 return matched;
187 }
188
196 bool collectAck(uint32_t msgId, uint32_t fromNode, uint32_t now,
197 std::list<DeliveryResult>& results) {
198 auto it = entries.find(msgId);
199 if (it == entries.end()) return false;
200 if (it->second.waitingFor.count(fromNode) == 0) return false;
201 auto latency = (uint32_t)(now - it->second.sentAt);
202 if (latency >= it->second.timeoutMs) {
203 // The polling task may not have run exactly at the deadline. Reject
204 // this late ACK and expire every destination still waiting on the
205 // same message before invoking user code (callbacks may reenter us).
206 auto expired = std::move(it->second);
207 entries.erase(it);
208 for (auto&& nodeId : expired.waitingFor) {
210 "AckTracker: timeout waiting for ack from %u\n", nodeId);
211 results.push_back(
212 DeliveryResult(expired.callback, nodeId, false, expired.timeoutMs));
213 }
214 return false;
215 }
216 it->second.waitingFor.erase(fromNode);
217 auto callback = it->second.callback;
218 if (it->second.waitingFor.empty()) entries.erase(it);
219 results.push_back(DeliveryResult(callback, fromNode, true, latency));
220 return true;
221 }
222
229 size_t expire(uint32_t now) {
230 std::list<DeliveryResult> results;
231 collectExpired(now, results);
232 for (const auto& result : results) {
233 result.callback(result.nodeId, result.delivered, result.latencyMs);
234 }
235 return entries.size();
236 }
237
244 size_t collectExpired(uint32_t now, std::list<DeliveryResult>& results) {
245 // Collect expired entries and erase them from the map BEFORE exposing
246 // results to user callbacks: a callback may reenter this tracker (track a
247 // retry, call clear() via mesh.stop(), or poll checkAcks()), which
248 // would invalidate a live iterator into `entries`.
249 std::list<PendingAck> expired;
250 for (auto it = entries.begin(); it != entries.end();) {
251 if ((uint32_t)(now - it->second.sentAt) >= it->second.timeoutMs) {
252 expired.push_back(std::move(it->second));
253 it = entries.erase(it);
254 } else {
255 ++it;
256 }
257 }
258 for (auto&& entry : expired) {
259 for (auto&& nodeId : entry.waitingFor) {
261 "AckTracker: timeout waiting for ack from %u\n", nodeId);
262 results.push_back(
263 DeliveryResult(entry.callback, nodeId, false, entry.timeoutMs));
264 }
265 }
266 return entries.size();
267 }
268
270 size_t pending() const { return entries.size(); }
271
273 void clear() { entries.clear(); }
274
275 protected:
276 std::map<uint32_t, PendingAck> entries;
277 uint32_t counter = 0;
278};
279
280} // namespace ack
281} // namespace painlessmesh
282
283#endif
painlessmesh::logger::LogClass Log
Definition wifi.cpp:2
#define PAINLESSMESH_MAX_PENDING_ACKS
Definition ack.hpp:19
Definition ack.hpp:123
size_t collectExpired(uint32_t now, std::list< DeliveryResult > &results)
Definition ack.hpp:244
bool track(uint32_t msgId, const std::list< uint32_t > &destinations, deliveryCallback_t callback, uint32_t timeoutMs, uint32_t now)
Definition ack.hpp:155
std::map< uint32_t, PendingAck > entries
Definition ack.hpp:276
size_t expire(uint32_t now)
Definition ack.hpp:229
bool collectAck(uint32_t msgId, uint32_t fromNode, uint32_t now, std::list< DeliveryResult > &results)
Definition ack.hpp:196
void seed(uint32_t value)
Definition ack.hpp:133
uint32_t counter
Definition ack.hpp:277
bool handleAck(uint32_t msgId, uint32_t fromNode, uint32_t now)
Definition ack.hpp:180
void clear()
Definition ack.hpp:273
bool full() const
Definition ack.hpp:142
uint32_t nextMessageId()
Definition ack.hpp:136
size_t pending() const
Definition ack.hpp:270
MessageAckPackage(uint32_t fromNode, uint32_t destNode, uint32_t msgId)
Definition ack.hpp:70
JsonObject addTo(JsonObject &&jsonObj) const
Definition ack.hpp:81
uint32_t messageId
Definition ack.hpp:66
MessageAckPackage()
Definition ack.hpp:68
size_t jsonObjectSize() const
Definition ack.hpp:88
MessageAckPackage(JsonObject jsonObj)
Definition ack.hpp:77
Definition logger.hpp:46
Definition plugin.hpp:40
uint32_t dest
Definition plugin.hpp:46
int noJsonFields
Definition plugin.hpp:49
SinglePackage(int type)
Definition plugin.hpp:51
uint32_t from
Definition plugin.hpp:45
std::function< void(uint32_t nodeId, bool delivered, uint32_t latencyMs)> deliveryCallback_t
Definition ack.hpp:53
@ ERROR
Definition logger.hpp:19
@ COMMUNICATION
Definition logger.hpp:25
Definition wifi.hpp:34
Definition ack.hpp:102
DeliveryResult(deliveryCallback_t callback, uint32_t nodeId, bool delivered, uint32_t latencyMs)
Definition ack.hpp:108
deliveryCallback_t callback
Definition ack.hpp:103
uint32_t nodeId
Definition ack.hpp:104
bool delivered
Definition ack.hpp:105
uint32_t latencyMs
Definition ack.hpp:106
Definition ack.hpp:95
uint32_t sentAt
Definition ack.hpp:97
uint32_t timeoutMs
Definition ack.hpp:98
deliveryCallback_t callback
Definition ack.hpp:96
std::set< uint32_t > waitingFor
Definition ack.hpp:99
painlessmesh::logger::LogClass Log
Definition wifi.cpp:2