AlteriomPainlessMesh v2.1.1
Easy ESP32/ESP8266 mesh networking with Alteriom IoT extensions
Loading...
Searching...
No Matches
plugin.hpp
Go to the documentation of this file.
1#ifndef _PAINLESS_MESH_PLUGIN_HPP_
2#define _PAINLESS_MESH_PLUGIN_HPP_
3
4#include "Arduino.h"
6
8#include <utility>
9#include <vector>
10
11namespace painlessmesh {
12
38namespace plugin {
39
41 public:
42 // `from` and `dest` are the application's to fill; a package built by
43 // the type constructor and sent before they are set addresses node 0,
44 // not whatever the stack held.
45 uint32_t from = 0;
46 uint32_t dest = 0;
48 int type = 0;
49 int noJsonFields = 4;
50
51 SinglePackage(int type) : routing(router::SINGLE), type(type) {}
52
53 SinglePackage(JsonObject jsonObj) {
54 from = jsonObj["from"];
55 dest = jsonObj["dest"];
56 type = jsonObj["type"];
57 routing = static_cast<router::Type>(jsonObj["routing"].as<int>());
58 }
59
60 JsonObject addTo(JsonObject&& jsonObj) const {
61 jsonObj["from"] = from;
62 jsonObj["dest"] = dest;
63 jsonObj["routing"] = static_cast<int>(routing);
64 jsonObj["type"] = type;
65 return jsonObj;
66 }
67};
68
70 public:
71 uint32_t from = 0;
73 int type = 0;
74 int noJsonFields = 3;
75
76 BroadcastPackage(int type) : routing(router::BROADCAST), type(type) {}
77
78 BroadcastPackage(JsonObject jsonObj) {
79 from = jsonObj["from"];
80 type = jsonObj["type"];
81 routing = static_cast<router::Type>(jsonObj["routing"].as<int>());
82 }
83
84 JsonObject addTo(JsonObject&& jsonObj) const {
85 jsonObj["from"] = from;
86 jsonObj["routing"] = static_cast<int>(routing);
87 jsonObj["type"] = type;
88 return jsonObj;
89 }
90};
91
93 public:
97
98 NeighbourPackage(JsonObject jsonObj) : SinglePackage(jsonObj) {}
99};
100
118 public:
119 uint8_t priority = 5; // Bridge priority (10=highest, 1=lowest)
120 TSTRING role = "secondary"; // Role: "primary", "secondary", "standby"
121 std::vector<uint32_t> peerBridges; // List of known bridge node IDs
122 uint8_t load = 0; // Current load percentage (0-100)
123 uint32_t timestamp = 0; // Coordination timestamp
124 int noJsonFields = 8; // Base fields (3) + new fields (5)
125
127
128 BridgeCoordinationPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
129 priority = jsonObj["priority"] | 5;
130 role = jsonObj["role"].as<TSTRING>();
131 load = jsonObj["load"] | 0;
132 timestamp = jsonObj["timestamp"] | 0;
133
134 // Parse peer bridge array
135 if (jsonObj["peerBridges"].is<JsonArray>()) {
136 JsonArray peers = jsonObj["peerBridges"];
137 for (JsonVariant peer : peers) {
138 peerBridges.push_back(peer.as<uint32_t>());
139 }
140 }
141 }
142
143 JsonObject addTo(JsonObject&& jsonObj) const {
144 jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
145 jsonObj["priority"] = priority;
146 jsonObj["role"] = role;
147 jsonObj["load"] = load;
148 jsonObj["timestamp"] = timestamp;
149
150 // Add peer bridge array
151 JsonArray peers = jsonObj["peerBridges"].to<JsonArray>();
152 for (uint32_t peerId : peerBridges) {
153 peers.add(peerId);
154 }
155
156 return jsonObj;
157 }
158
159#if ARDUINOJSON_VERSION_MAJOR < 7
160 size_t jsonObjectSize() const {
161 // Base fields + string length + array overhead
162 size_t peerArraySize = JSON_ARRAY_SIZE(peerBridges.size()) +
163 (peerBridges.size() * sizeof(uint32_t));
164 return JSON_OBJECT_SIZE(noJsonFields) + role.length() + peerArraySize;
165 }
166#endif
167};
168
177template <typename T>
179 public:
180 // NOTE: scheduler is optional (defaults to nullptr) for backward
181 // compatibility with existing call sites; without it we can't detect
182 // the "current" task and behaviour falls back to the original one.
183 // Passing the scheduler (mesh.hpp already has it as mScheduler) avoids
184 // the use-after-free described below.
185 void stop(Scheduler* scheduler = nullptr) {
186 // If stop() is called from within the callback of one of the tasks in
187 // taskList (e.g. promoteToBridge()), that task is
188 // "scheduler->getCurrentTask()" at this exact moment. Disabling it,
189 // clearing its callback, or letting its shared_ptr refcount drop to
190 // zero here would destroy its closure - which is still on the stack,
191 // in the middle of its own execution - causing a use-after-free crash
192 // (same bug family as upstream issue #373, but triggered by the
193 // shared_ptr refcount instead of a raw delete in onDisable).
194 // Move it out of the reusable list while retaining ownership for the
195 // handler's lifetime. This also protects a stop()/init() sequence inside
196 // the callback: a new scheduler must receive a new task rather than
197 // mutating the task that is still executing on the old scheduler.
198 Task* current = scheduler ? scheduler->getCurrentTask() : nullptr;
199 for (auto it = taskList.begin(); it != taskList.end();) {
200 if (current != nullptr && it->get() == current) {
201 // Keep the executing task alive, but remove it from the reusable
202 // pool. A callback may stop() and immediately init() with a new
203 // scheduler; reusing this still-running task would rewrite its
204 // closure and would not attach it to that new scheduler.
205 quarantinedTasks.push_back({scheduler, *it});
206 it = taskList.erase(it);
207 continue;
208 }
209 (*it)->disable();
210 (*it)->setCallback(NULL);
211 it = taskList.erase(it);
212 }
213 callbackList.clear();
214 }
215
217 if (taskList.size() > 0)
219 "~PackageHandler(): Always call PackageHandler::stop(scheduler) "
220 "before calling this destructor");
221 }
222
224 auto variant = protocol::Variant(pkg);
225 // if single or neighbour with direction
226 if (variant.routing() == router::SINGLE ||
227 (variant.routing() == router::NEIGHBOUR && variant.dest() != 0)) {
228 return router::send(variant, (*this));
229 }
230
231 // if broadcast or neighbour without direction
232 if (variant.routing() == router::BROADCAST ||
233 (variant.routing() == router::NEIGHBOUR && variant.dest() == 0)) {
234 auto i = router::broadcast(variant, (*this), 0);
235 if (i > 0) return true;
236 return false;
237 }
238 return false;
239 }
240
241 void onPackage(int type, std::function<bool(protocol::Variant&)> function) {
242 auto func = [function](protocol::Variant& var, std::shared_ptr<T>,
243 uint32_t) { return function(var); };
244 this->callbackList.onPackage(type, func);
245 }
246
255 std::shared_ptr<Task> addTask(Scheduler& scheduler, unsigned long aInterval,
256 long aIterations,
257 std::function<void()> aCallback) {
258 using namespace painlessmesh::logger;
260 for (auto&& task : taskList) {
261 if (task.use_count() == 1 && !task->isEnabled()) {
262 task->set(aInterval, aIterations, aCallback, NULL, NULL);
263 // Use enableDelayed() for delayed one-shot tasks to prevent immediate execution
264 // This ensures tasks with intervals execute after the delay, not immediately
265 if (aInterval > 0 && aIterations == TASK_ONCE) {
266 task->enableDelayed();
267 } else {
268 task->enable();
269 }
270 return task;
271 }
272 }
273
274 std::shared_ptr<Task> task =
275 std::make_shared<Task>(aInterval, aIterations, aCallback);
276 scheduler.addTask((*task));
277 // Use enableDelayed() for delayed one-shot tasks to prevent immediate execution
278 // This ensures tasks with intervals execute after the delay, not immediately
279 if (aInterval > 0 && aIterations == TASK_ONCE) {
280 task->enableDelayed();
281 } else {
282 task->enable();
283 }
284 taskList.push_front(task);
285 return task;
286 }
287
288 std::shared_ptr<Task> addTask(Scheduler& scheduler,
289 std::function<void()> aCallback) {
290 return this->addTask(scheduler, 0, TASK_ONCE, aCallback);
291 }
292
293 protected:
295 for (auto it = quarantinedTasks.begin(); it != quarantinedTasks.end();) {
296 if (it->first != nullptr &&
297 it->first->getCurrentTask() == it->second.get()) {
298 ++it;
299 continue;
300 }
301 it->second->disable();
302 it->second->setCallback(NULL);
303 it = quarantinedTasks.erase(it);
304 }
305 }
306
308 std::list<std::shared_ptr<Task> > taskList = {};
309 // Tasks detached while their own callback is executing, paired with the
310 // scheduler whose stack still references them. They become reclaimable
311 // after that scheduler no longer reports the task as current.
312 std::list<std::pair<Scheduler*, std::shared_ptr<Task> > > quarantinedTasks =
313 {};
314};
315
316} // namespace plugin
317} // namespace painlessmesh
318#endif
Definition layout.hpp:98
int noJsonFields
Definition plugin.hpp:124
BridgeCoordinationPackage(JsonObject jsonObj)
Definition plugin.hpp:128
size_t jsonObjectSize() const
Definition plugin.hpp:160
BridgeCoordinationPackage()
Definition plugin.hpp:126
JsonObject addTo(JsonObject &&jsonObj) const
Definition plugin.hpp:143
std::vector< uint32_t > peerBridges
Definition plugin.hpp:121
TSTRING role
Definition plugin.hpp:120
uint8_t priority
Definition plugin.hpp:119
uint8_t load
Definition plugin.hpp:122
uint32_t timestamp
Definition plugin.hpp:123
JsonObject addTo(JsonObject &&jsonObj) const
Definition plugin.hpp:84
BroadcastPackage(int type)
Definition plugin.hpp:76
int noJsonFields
Definition plugin.hpp:74
router::Type routing
Definition plugin.hpp:72
int type
Definition plugin.hpp:73
uint32_t from
Definition plugin.hpp:71
BroadcastPackage(JsonObject jsonObj)
Definition plugin.hpp:78
NeighbourPackage(JsonObject jsonObj)
Definition plugin.hpp:98
NeighbourPackage(int type)
Definition plugin.hpp:94
Definition plugin.hpp:178
~PackageHandler()
Definition plugin.hpp:216
bool sendPackage(const protocol::PackageInterface *pkg)
Definition plugin.hpp:223
void reclaimQuarantinedTasks()
Definition plugin.hpp:294
std::shared_ptr< Task > addTask(Scheduler &scheduler, unsigned long aInterval, long aIterations, std::function< void()> aCallback)
Definition plugin.hpp:255
std::list< std::pair< Scheduler *, std::shared_ptr< Task > > > quarantinedTasks
Definition plugin.hpp:312
void onPackage(int type, std::function< bool(protocol::Variant &)> function)
Definition plugin.hpp:241
std::shared_ptr< Task > addTask(Scheduler &scheduler, std::function< void()> aCallback)
Definition plugin.hpp:288
callback::MeshPackageCallbackList< T > callbackList
Definition plugin.hpp:307
std::list< std::shared_ptr< Task > > taskList
Definition plugin.hpp:308
void stop(Scheduler *scheduler=nullptr)
Definition plugin.hpp:185
Definition plugin.hpp:40
uint32_t dest
Definition plugin.hpp:46
int type
Definition plugin.hpp:48
int noJsonFields
Definition plugin.hpp:49
SinglePackage(JsonObject jsonObj)
Definition plugin.hpp:53
SinglePackage(int type)
Definition plugin.hpp:51
router::Type routing
Definition plugin.hpp:47
JsonObject addTo(JsonObject &&jsonObj) const
Definition plugin.hpp:60
uint32_t from
Definition plugin.hpp:45
Definition protocol.hpp:481
String TSTRING
Definition configuration.hpp:132
Definition logger.hpp:7
@ ERROR
Definition logger.hpp:19
Type
Definition protocol.hpp:30
@ NEIGHBOUR
Definition protocol.hpp:30
@ SINGLE
Definition protocol.hpp:30
@ BROADCAST
Definition protocol.hpp:30
bool send(T &package, std::shared_ptr< U > conn, bool priority=false)
Definition router.hpp:65
size_t broadcast(protocol::Variant &variant, const layout::Layout< T > &layout, uint32_t exclude)
Definition router.hpp:161
Definition wifi.hpp:34
painlessmesh::logger::LogClass Log
Definition wifi.cpp:2