@alteriom/painlessmesh 1.6.1 → 1.7.2
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 +380 -143
- package/LICENSE +674 -674
- package/README.md +477 -434
- package/RELEASE_GUIDE.md +504 -418
- package/docs/DOCUMENTATION_MIGRATION_PLAN.md +175 -175
- package/docs/IMPLEMENTATION_PLAN_MESH_TOPOLOGY.md +1062 -0
- package/docs/MESH_TOPOLOGY_GUIDE.md +992 -0
- package/docs/MESH_TOPOLOGY_PROGRESS.md +422 -0
- package/docs/MQTT_BRIDGE_COMMANDS.md +894 -0
- package/docs/MQTT_BRIDGE_IMPLEMENTATION_SUMMARY.md +324 -0
- package/docs/MQTT_COMMAND_SCHEMA_PROPOSAL.md +576 -0
- package/docs/MQTT_SCHEMA_COMPLIANCE.md +285 -0
- package/docs/MQTT_SCHEMA_PROPOSALS.md +446 -0
- package/docs/MQTT_SCHEMA_REVIEW.md +690 -0
- package/docs/OTA_COMMANDS_REFERENCE.md +554 -0
- package/docs/PHASE1_GUIDE.md +349 -0
- package/docs/PHASE2_GUIDE.md +543 -0
- package/docs/README.md +77 -70
- package/docs/SCHEMA_VALIDATION_CHECKLIST.md +222 -0
- package/docs/alteriom/overview.md +507 -507
- package/docs/api/core-api.md +606 -606
- package/docs/architecture/mesh-architecture.md +378 -378
- package/docs/architecture/plugin-system.md +516 -516
- package/docs/getting-started/first-mesh.md +409 -409
- package/docs/getting-started/installation.md +274 -274
- package/docs/getting-started/quickstart.md +157 -157
- package/docs/improvements/FEATURE_PROPOSALS.md +337 -0
- package/docs/improvements/PHASE1_IMPLEMENTATION.md +325 -0
- package/docs/improvements/PHASE2_IMPLEMENTATION.md +567 -0
- package/docs/improvements/README.md +86 -68
- package/docs/improvements/ota-and-status-enhancements.md +911 -0
- package/docs/improvements/ota-status-architecture-diagrams.md +658 -0
- package/docs/improvements/ota-status-quick-reference.md +284 -0
- package/docs/platformio-publishing.md +255 -0
- package/docs/platformio-setup-summary.md +121 -0
- package/docs/troubleshooting/common-issues.md +520 -520
- package/docs/troubleshooting/faq.md +472 -472
- package/docs/tutorials/basic-examples.md +717 -717
- package/docs/wiki/API-Reference.md +245 -245
- package/docs/wiki/Complete-Documentation.md +122 -122
- package/examples/alteriom/README.md +139 -81
- package/examples/alteriom/alteriom.ino +186 -185
- package/examples/alteriom/alteriom_sensor_package.hpp +240 -127
- package/examples/alteriom/platformio.ini +24 -24
- package/examples/alteriomImproved/alteriom_sensor_package.hpp +224 -0
- package/examples/{alteriom → alteriomImproved}/improved_sensor_node.ino +245 -245
- package/examples/alteriomImproved/platformio.ini +25 -0
- package/examples/alteriomPhase1/alteriom_sensor_package.hpp +224 -0
- package/examples/alteriomPhase1/phase1_features.ino +242 -0
- package/examples/alteriomPhase1/platformio.ini +25 -0
- package/examples/alteriomPhase2/alteriom_sensor_package.hpp +224 -0
- package/examples/alteriomPhase2/phase2_features.ino +186 -0
- package/examples/alteriomPhase2/platformio.ini +25 -0
- package/examples/{alteriom → alteriomSensorNode}/alteriom_sensor_node.ino +183 -183
- package/examples/alteriomSensorNode/alteriom_sensor_package.hpp +224 -0
- package/examples/alteriomSensorNode/platformio.ini +25 -0
- package/examples/basic/basic.ino +66 -66
- package/examples/basic/platformio.ini +25 -25
- package/examples/bridge/bridge.ino +51 -51
- package/examples/bridge/mesh_event_publisher.hpp +253 -0
- package/examples/bridge/mesh_topology_reporter.hpp +303 -0
- package/examples/bridge/mqtt_command_bridge.hpp +459 -0
- package/examples/bridge/mqtt_status_bridge.hpp +519 -0
- package/examples/bridge/platformio.ini +25 -25
- package/examples/echoNode/echoNode.ino +33 -33
- package/examples/echoNode/platformio.ini +25 -25
- package/examples/logClient/logClient.ino +109 -109
- package/examples/logClient/platformio.ini +25 -25
- package/examples/logServer/logServer.ino +81 -81
- package/examples/logServer/platformio.ini +25 -25
- package/examples/meshCommandNode/alteriom_sensor_package.hpp +235 -0
- package/examples/meshCommandNode/meshCommandNode.ino +263 -0
- package/examples/meshCommandNode/platformio.ini +25 -0
- package/examples/mqttBridge/mqttBridge.ino +118 -118
- package/examples/mqttBridge/platformio.ini +26 -26
- package/examples/mqttCommandBridge/alteriom_sensor_package.hpp +235 -0
- package/examples/mqttCommandBridge/mesh_event_publisher.hpp +253 -0
- package/examples/mqttCommandBridge/mesh_topology_reporter.hpp +303 -0
- package/examples/mqttCommandBridge/mqttCommandBridge.ino +252 -0
- package/examples/mqttCommandBridge/mqtt_command_bridge.hpp +459 -0
- package/examples/mqttCommandBridge/platformio.ini +26 -0
- package/examples/mqttStatusBridge/mqttStatusBridge.ino +216 -0
- package/examples/mqttStatusBridge/mqtt_status_bridge.hpp +522 -0
- package/examples/mqttStatusBridge/platformio.ini +26 -0
- package/examples/mqttTopologyTest/README.md +467 -0
- package/examples/mqttTopologyTest/mqttTopologyTest.ino +748 -0
- package/examples/mqttTopologyTest/platformio.ini +26 -0
- package/examples/namedMesh/namedMesh.ino +97 -97
- package/examples/namedMesh/platformio.ini +25 -25
- package/examples/otaReceiver/otaReceiver.ino +79 -79
- package/examples/otaReceiver/platformio.ini +25 -25
- package/examples/otaSender/otaSender.ino +160 -151
- package/examples/otaSender/platformio.ini +25 -25
- package/examples/startHere/platformio.ini +25 -25
- package/examples/startHere/startHere.ino +159 -159
- package/examples/webServer/platformio.ini +27 -27
- package/examples/webServer/webServer.ino +89 -89
- package/keywords.txt +48 -48
- package/library.json +55 -34
- package/library.properties +10 -10
- package/package.json +86 -78
- package/src/AlteriomPainlessMesh.h +97 -97
- package/src/arduino/wifi.hpp +365 -365
- package/src/boost/asynctcp.hpp +279 -279
- package/src/painlessMesh.h +70 -70
- package/src/painlessMeshSTA.cpp +236 -236
- package/src/painlessMeshSTA.h +58 -58
- package/src/painlessTaskOptions.h +4 -4
- package/src/painlessmesh/base64.hpp +111 -111
- package/src/painlessmesh/buffer.hpp +229 -229
- package/src/painlessmesh/callback.hpp +91 -91
- package/src/painlessmesh/configuration.hpp +77 -77
- package/src/painlessmesh/connection.hpp +192 -192
- package/src/painlessmesh/layout.hpp +188 -188
- package/src/painlessmesh/logger.hpp +158 -158
- package/src/painlessmesh/memory.hpp +119 -119
- package/src/painlessmesh/mesh.hpp +761 -560
- package/src/painlessmesh/metrics.hpp +322 -322
- package/src/painlessmesh/ntp.hpp +263 -263
- package/src/painlessmesh/ota.hpp +582 -553
- package/src/painlessmesh/plugin.hpp +188 -188
- package/src/painlessmesh/protocol.hpp +813 -813
- package/src/painlessmesh/router.hpp +322 -322
- package/src/painlessmesh/tcp.hpp +71 -71
- package/src/painlessmesh/validation.hpp +238 -238
- package/src/plugin/performance.hpp +214 -214
- package/src/plugin/remote.hpp +64 -64
- package/src/scheduler.cpp +10 -10
- package/src/wifi.cpp +2 -2
|
@@ -1,323 +1,323 @@
|
|
|
1
|
-
#ifndef _PAINLESS_MESH_METRICS_HPP_
|
|
2
|
-
#define _PAINLESS_MESH_METRICS_HPP_
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Performance metrics and monitoring utilities for painlessMesh
|
|
6
|
-
*
|
|
7
|
-
* This module provides comprehensive performance monitoring capabilities
|
|
8
|
-
* to help optimize mesh network performance and diagnose issues.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
#include <algorithm>
|
|
12
|
-
#include <string>
|
|
13
|
-
#ifndef ARDUINO
|
|
14
|
-
#include <chrono>
|
|
15
|
-
#endif
|
|
16
|
-
#include "painlessmesh/configuration.hpp"
|
|
17
|
-
|
|
18
|
-
namespace painlessmesh {
|
|
19
|
-
namespace metrics {
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* High-resolution timer for performance measurements
|
|
23
|
-
*/
|
|
24
|
-
class Timer {
|
|
25
|
-
public:
|
|
26
|
-
Timer() : start_time_(get_time()) {}
|
|
27
|
-
|
|
28
|
-
void reset() { start_time_ = get_time(); }
|
|
29
|
-
|
|
30
|
-
uint32_t elapsed_ms() const {
|
|
31
|
-
return static_cast<uint32_t>(get_time() - start_time_);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
uint32_t elapsed_us() const {
|
|
35
|
-
#ifdef ESP32
|
|
36
|
-
return static_cast<uint32_t>((esp_timer_get_time() - start_time_us_) /
|
|
37
|
-
1000);
|
|
38
|
-
#else
|
|
39
|
-
return elapsed_ms() * 1000; // Fallback to millisecond precision
|
|
40
|
-
#endif
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
private:
|
|
44
|
-
uint32_t get_time() const {
|
|
45
|
-
#ifdef ARDUINO
|
|
46
|
-
return millis();
|
|
47
|
-
#else
|
|
48
|
-
auto now = std::chrono::steady_clock::now();
|
|
49
|
-
auto duration = now.time_since_epoch();
|
|
50
|
-
return std::chrono::duration_cast<std::chrono::milliseconds>(duration)
|
|
51
|
-
.count();
|
|
52
|
-
#endif
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
uint32_t start_time_;
|
|
56
|
-
#ifdef ESP32
|
|
57
|
-
int64_t start_time_us_ = esp_timer_get_time();
|
|
58
|
-
#endif
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Message statistics tracking
|
|
63
|
-
*/
|
|
64
|
-
struct MessageStats {
|
|
65
|
-
uint32_t messages_sent = 0;
|
|
66
|
-
uint32_t messages_received = 0;
|
|
67
|
-
uint32_t messages_dropped = 0;
|
|
68
|
-
uint32_t messages_retransmitted = 0;
|
|
69
|
-
uint32_t bytes_sent = 0;
|
|
70
|
-
uint32_t bytes_received = 0;
|
|
71
|
-
uint32_t parse_errors = 0;
|
|
72
|
-
uint32_t validation_errors = 0;
|
|
73
|
-
|
|
74
|
-
// Latency tracking
|
|
75
|
-
uint32_t min_latency_ms = UINT32_MAX;
|
|
76
|
-
uint32_t max_latency_ms = 0;
|
|
77
|
-
uint32_t total_latency_ms = 0;
|
|
78
|
-
uint32_t latency_samples = 0;
|
|
79
|
-
|
|
80
|
-
void record_sent_message(size_t bytes) {
|
|
81
|
-
messages_sent++;
|
|
82
|
-
bytes_sent += bytes;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
void record_received_message(size_t bytes) {
|
|
86
|
-
messages_received++;
|
|
87
|
-
bytes_received += bytes;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
void record_dropped_message() { messages_dropped++; }
|
|
91
|
-
|
|
92
|
-
void record_retransmission() { messages_retransmitted++; }
|
|
93
|
-
|
|
94
|
-
void record_parse_error() { parse_errors++; }
|
|
95
|
-
|
|
96
|
-
void record_validation_error() { validation_errors++; }
|
|
97
|
-
|
|
98
|
-
void record_latency(uint32_t latency_ms) {
|
|
99
|
-
min_latency_ms = std::min(min_latency_ms, latency_ms);
|
|
100
|
-
max_latency_ms = std::max(max_latency_ms, latency_ms);
|
|
101
|
-
total_latency_ms += latency_ms;
|
|
102
|
-
latency_samples++;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
uint32_t average_latency_ms() const {
|
|
106
|
-
return latency_samples > 0 ? total_latency_ms / latency_samples : 0;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
double throughput_bps() const {
|
|
110
|
-
#ifdef ARDUINO
|
|
111
|
-
uint32_t uptime_s = millis() / 1000;
|
|
112
|
-
#else
|
|
113
|
-
// For non-Arduino builds, use a simpler approach or disable this feature
|
|
114
|
-
uint32_t uptime_s = 1; // Avoid division by zero
|
|
115
|
-
#endif
|
|
116
|
-
return uptime_s > 0 ? (bytes_sent + bytes_received) * 8.0 / uptime_s : 0.0;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
double packet_loss_rate() const {
|
|
120
|
-
uint32_t total_attempted = messages_sent + messages_dropped;
|
|
121
|
-
return total_attempted > 0
|
|
122
|
-
? static_cast<double>(messages_dropped) / total_attempted
|
|
123
|
-
: 0.0;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
void reset() { *this = MessageStats{}; }
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Memory usage tracking
|
|
131
|
-
*/
|
|
132
|
-
struct MemoryStats {
|
|
133
|
-
uint32_t heap_free = 0;
|
|
134
|
-
uint32_t heap_max_alloc = 0;
|
|
135
|
-
uint32_t heap_min_free = UINT32_MAX;
|
|
136
|
-
uint32_t psram_free = 0;
|
|
137
|
-
uint32_t stack_high_water = 0;
|
|
138
|
-
|
|
139
|
-
void update() {
|
|
140
|
-
#ifdef ESP32
|
|
141
|
-
heap_free = ESP.getFreeHeap();
|
|
142
|
-
heap_max_alloc = ESP.getMaxAllocHeap();
|
|
143
|
-
heap_min_free = std::min(heap_min_free, heap_free);
|
|
144
|
-
|
|
145
|
-
#ifdef BOARD_HAS_PSRAM
|
|
146
|
-
psram_free = ESP.getFreePsram();
|
|
147
|
-
#endif
|
|
148
|
-
|
|
149
|
-
stack_high_water = uxTaskGetStackHighWaterMark(NULL);
|
|
150
|
-
#elif defined(ESP8266)
|
|
151
|
-
heap_free = ESP.getFreeHeap();
|
|
152
|
-
heap_max_alloc = ESP.getMaxFreeBlockSize();
|
|
153
|
-
heap_min_free = std::min(heap_min_free, heap_free);
|
|
154
|
-
#endif
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
bool is_memory_critical() const {
|
|
158
|
-
return heap_free < 10000; // Less than 10KB free is critical
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
bool is_memory_low() const {
|
|
162
|
-
return heap_free < 20000; // Less than 20KB free is low
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Network topology metrics
|
|
168
|
-
*/
|
|
169
|
-
struct TopologyStats {
|
|
170
|
-
uint32_t node_count = 0;
|
|
171
|
-
uint32_t connection_count = 0;
|
|
172
|
-
uint32_t max_hops = 0;
|
|
173
|
-
uint32_t connection_changes = 0;
|
|
174
|
-
uint32_t failed_connections = 0;
|
|
175
|
-
|
|
176
|
-
void record_topology_change(uint32_t nodes, uint32_t connections,
|
|
177
|
-
uint32_t hops) {
|
|
178
|
-
node_count = nodes;
|
|
179
|
-
connection_count = connections;
|
|
180
|
-
max_hops = hops;
|
|
181
|
-
connection_changes++;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
void record_failed_connection() { failed_connections++; }
|
|
185
|
-
|
|
186
|
-
double connection_stability() const {
|
|
187
|
-
return connection_changes > 0
|
|
188
|
-
? 1.0 - static_cast<double>(failed_connections) /
|
|
189
|
-
connection_changes
|
|
190
|
-
: 1.0;
|
|
191
|
-
}
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Comprehensive metrics collector
|
|
196
|
-
*/
|
|
197
|
-
class MetricsCollector {
|
|
198
|
-
public:
|
|
199
|
-
MetricsCollector() : start_time_(get_current_time()) {}
|
|
200
|
-
|
|
201
|
-
MessageStats& message_stats() { return message_stats_; }
|
|
202
|
-
MemoryStats& memory_stats() { return memory_stats_; }
|
|
203
|
-
TopologyStats& topology_stats() { return topology_stats_; }
|
|
204
|
-
|
|
205
|
-
const MessageStats& message_stats() const { return message_stats_; }
|
|
206
|
-
const MemoryStats& memory_stats() const { return memory_stats_; }
|
|
207
|
-
const TopologyStats& topology_stats() const { return topology_stats_; }
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Update all metrics (call periodically)
|
|
211
|
-
*/
|
|
212
|
-
void update() {
|
|
213
|
-
memory_stats_.update();
|
|
214
|
-
last_update_ = get_current_time();
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Get uptime in seconds
|
|
219
|
-
*/
|
|
220
|
-
uint32_t uptime_seconds() const {
|
|
221
|
-
return (get_current_time() - start_time_) / 1000;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Generate JSON status report
|
|
226
|
-
*/
|
|
227
|
-
TSTRING generate_status_json() const {
|
|
228
|
-
TSTRING json = "{";
|
|
229
|
-
json += "\"uptime\":";
|
|
230
|
-
json += std::to_string(uptime_seconds());
|
|
231
|
-
json += ",";
|
|
232
|
-
json += "\"messages\":{";
|
|
233
|
-
json += "\"sent\":";
|
|
234
|
-
json += std::to_string(message_stats_.messages_sent);
|
|
235
|
-
json += ",";
|
|
236
|
-
json += "\"received\":";
|
|
237
|
-
json += std::to_string(message_stats_.messages_received);
|
|
238
|
-
json += ",";
|
|
239
|
-
json += "\"dropped\":";
|
|
240
|
-
json += std::to_string(message_stats_.messages_dropped);
|
|
241
|
-
json += ",";
|
|
242
|
-
json += "\"avg_latency\":";
|
|
243
|
-
json += std::to_string(message_stats_.average_latency_ms());
|
|
244
|
-
json += ",";
|
|
245
|
-
json += "\"throughput\":";
|
|
246
|
-
json += std::to_string(message_stats_.throughput_bps());
|
|
247
|
-
json += ",";
|
|
248
|
-
json += "\"packet_loss\":";
|
|
249
|
-
json += std::to_string(message_stats_.packet_loss_rate());
|
|
250
|
-
json += "},";
|
|
251
|
-
json += "\"memory\":{";
|
|
252
|
-
json += "\"heap_free\":";
|
|
253
|
-
json += std::to_string(memory_stats_.heap_free);
|
|
254
|
-
json += ",";
|
|
255
|
-
json += "\"heap_min\":";
|
|
256
|
-
json += std::to_string(memory_stats_.heap_min_free);
|
|
257
|
-
json += ",";
|
|
258
|
-
json += "\"critical\":";
|
|
259
|
-
json += (memory_stats_.is_memory_critical() ? "true" : "false");
|
|
260
|
-
json += "},";
|
|
261
|
-
json += "\"topology\":{";
|
|
262
|
-
json += "\"nodes\":";
|
|
263
|
-
json += std::to_string(topology_stats_.node_count);
|
|
264
|
-
json += ",";
|
|
265
|
-
json += "\"connections\":";
|
|
266
|
-
json += std::to_string(topology_stats_.connection_count);
|
|
267
|
-
json += ",";
|
|
268
|
-
json += "\"max_hops\":";
|
|
269
|
-
json += std::to_string(topology_stats_.max_hops);
|
|
270
|
-
json += ",";
|
|
271
|
-
json += "\"stability\":";
|
|
272
|
-
json += std::to_string(topology_stats_.connection_stability());
|
|
273
|
-
json += "}";
|
|
274
|
-
json += "}";
|
|
275
|
-
return json;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Reset all metrics
|
|
280
|
-
*/
|
|
281
|
-
void reset() {
|
|
282
|
-
message_stats_.reset();
|
|
283
|
-
topology_stats_ = TopologyStats{};
|
|
284
|
-
start_time_ = get_current_time();
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* Check if metrics indicate performance issues
|
|
289
|
-
*/
|
|
290
|
-
bool has_performance_issues() const {
|
|
291
|
-
return memory_stats_.is_memory_critical() ||
|
|
292
|
-
message_stats_.packet_loss_rate() > 0.1 || // > 10% packet loss
|
|
293
|
-
topology_stats_.connection_stability() < 0.8; // < 80% stability
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
private:
|
|
297
|
-
uint32_t get_current_time() const {
|
|
298
|
-
#ifdef ARDUINO
|
|
299
|
-
return millis();
|
|
300
|
-
#else
|
|
301
|
-
auto now = std::chrono::steady_clock::now();
|
|
302
|
-
auto duration = now.time_since_epoch();
|
|
303
|
-
return std::chrono::duration_cast<std::chrono::milliseconds>(duration)
|
|
304
|
-
.count();
|
|
305
|
-
#endif
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
MessageStats message_stats_;
|
|
309
|
-
MemoryStats memory_stats_;
|
|
310
|
-
TopologyStats topology_stats_;
|
|
311
|
-
uint32_t start_time_;
|
|
312
|
-
uint32_t last_update_ = 0;
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
// Global metrics instance (optional, can be disabled)
|
|
316
|
-
#ifdef PAINLESS_MESH_ENABLE_METRICS
|
|
317
|
-
extern MetricsCollector global_metrics;
|
|
318
|
-
#endif
|
|
319
|
-
|
|
320
|
-
} // namespace metrics
|
|
321
|
-
} // namespace painlessmesh
|
|
322
|
-
|
|
1
|
+
#ifndef _PAINLESS_MESH_METRICS_HPP_
|
|
2
|
+
#define _PAINLESS_MESH_METRICS_HPP_
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Performance metrics and monitoring utilities for painlessMesh
|
|
6
|
+
*
|
|
7
|
+
* This module provides comprehensive performance monitoring capabilities
|
|
8
|
+
* to help optimize mesh network performance and diagnose issues.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
#include <algorithm>
|
|
12
|
+
#include <string>
|
|
13
|
+
#ifndef ARDUINO
|
|
14
|
+
#include <chrono>
|
|
15
|
+
#endif
|
|
16
|
+
#include "painlessmesh/configuration.hpp"
|
|
17
|
+
|
|
18
|
+
namespace painlessmesh {
|
|
19
|
+
namespace metrics {
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* High-resolution timer for performance measurements
|
|
23
|
+
*/
|
|
24
|
+
class Timer {
|
|
25
|
+
public:
|
|
26
|
+
Timer() : start_time_(get_time()) {}
|
|
27
|
+
|
|
28
|
+
void reset() { start_time_ = get_time(); }
|
|
29
|
+
|
|
30
|
+
uint32_t elapsed_ms() const {
|
|
31
|
+
return static_cast<uint32_t>(get_time() - start_time_);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
uint32_t elapsed_us() const {
|
|
35
|
+
#ifdef ESP32
|
|
36
|
+
return static_cast<uint32_t>((esp_timer_get_time() - start_time_us_) /
|
|
37
|
+
1000);
|
|
38
|
+
#else
|
|
39
|
+
return elapsed_ms() * 1000; // Fallback to millisecond precision
|
|
40
|
+
#endif
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private:
|
|
44
|
+
uint32_t get_time() const {
|
|
45
|
+
#ifdef ARDUINO
|
|
46
|
+
return millis();
|
|
47
|
+
#else
|
|
48
|
+
auto now = std::chrono::steady_clock::now();
|
|
49
|
+
auto duration = now.time_since_epoch();
|
|
50
|
+
return std::chrono::duration_cast<std::chrono::milliseconds>(duration)
|
|
51
|
+
.count();
|
|
52
|
+
#endif
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
uint32_t start_time_;
|
|
56
|
+
#ifdef ESP32
|
|
57
|
+
int64_t start_time_us_ = esp_timer_get_time();
|
|
58
|
+
#endif
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Message statistics tracking
|
|
63
|
+
*/
|
|
64
|
+
struct MessageStats {
|
|
65
|
+
uint32_t messages_sent = 0;
|
|
66
|
+
uint32_t messages_received = 0;
|
|
67
|
+
uint32_t messages_dropped = 0;
|
|
68
|
+
uint32_t messages_retransmitted = 0;
|
|
69
|
+
uint32_t bytes_sent = 0;
|
|
70
|
+
uint32_t bytes_received = 0;
|
|
71
|
+
uint32_t parse_errors = 0;
|
|
72
|
+
uint32_t validation_errors = 0;
|
|
73
|
+
|
|
74
|
+
// Latency tracking
|
|
75
|
+
uint32_t min_latency_ms = UINT32_MAX;
|
|
76
|
+
uint32_t max_latency_ms = 0;
|
|
77
|
+
uint32_t total_latency_ms = 0;
|
|
78
|
+
uint32_t latency_samples = 0;
|
|
79
|
+
|
|
80
|
+
void record_sent_message(size_t bytes) {
|
|
81
|
+
messages_sent++;
|
|
82
|
+
bytes_sent += bytes;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
void record_received_message(size_t bytes) {
|
|
86
|
+
messages_received++;
|
|
87
|
+
bytes_received += bytes;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
void record_dropped_message() { messages_dropped++; }
|
|
91
|
+
|
|
92
|
+
void record_retransmission() { messages_retransmitted++; }
|
|
93
|
+
|
|
94
|
+
void record_parse_error() { parse_errors++; }
|
|
95
|
+
|
|
96
|
+
void record_validation_error() { validation_errors++; }
|
|
97
|
+
|
|
98
|
+
void record_latency(uint32_t latency_ms) {
|
|
99
|
+
min_latency_ms = std::min(min_latency_ms, latency_ms);
|
|
100
|
+
max_latency_ms = std::max(max_latency_ms, latency_ms);
|
|
101
|
+
total_latency_ms += latency_ms;
|
|
102
|
+
latency_samples++;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
uint32_t average_latency_ms() const {
|
|
106
|
+
return latency_samples > 0 ? total_latency_ms / latency_samples : 0;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
double throughput_bps() const {
|
|
110
|
+
#ifdef ARDUINO
|
|
111
|
+
uint32_t uptime_s = millis() / 1000;
|
|
112
|
+
#else
|
|
113
|
+
// For non-Arduino builds, use a simpler approach or disable this feature
|
|
114
|
+
uint32_t uptime_s = 1; // Avoid division by zero
|
|
115
|
+
#endif
|
|
116
|
+
return uptime_s > 0 ? (bytes_sent + bytes_received) * 8.0 / uptime_s : 0.0;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
double packet_loss_rate() const {
|
|
120
|
+
uint32_t total_attempted = messages_sent + messages_dropped;
|
|
121
|
+
return total_attempted > 0
|
|
122
|
+
? static_cast<double>(messages_dropped) / total_attempted
|
|
123
|
+
: 0.0;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
void reset() { *this = MessageStats{}; }
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Memory usage tracking
|
|
131
|
+
*/
|
|
132
|
+
struct MemoryStats {
|
|
133
|
+
uint32_t heap_free = 0;
|
|
134
|
+
uint32_t heap_max_alloc = 0;
|
|
135
|
+
uint32_t heap_min_free = UINT32_MAX;
|
|
136
|
+
uint32_t psram_free = 0;
|
|
137
|
+
uint32_t stack_high_water = 0;
|
|
138
|
+
|
|
139
|
+
void update() {
|
|
140
|
+
#ifdef ESP32
|
|
141
|
+
heap_free = ESP.getFreeHeap();
|
|
142
|
+
heap_max_alloc = ESP.getMaxAllocHeap();
|
|
143
|
+
heap_min_free = std::min(heap_min_free, heap_free);
|
|
144
|
+
|
|
145
|
+
#ifdef BOARD_HAS_PSRAM
|
|
146
|
+
psram_free = ESP.getFreePsram();
|
|
147
|
+
#endif
|
|
148
|
+
|
|
149
|
+
stack_high_water = uxTaskGetStackHighWaterMark(NULL);
|
|
150
|
+
#elif defined(ESP8266)
|
|
151
|
+
heap_free = ESP.getFreeHeap();
|
|
152
|
+
heap_max_alloc = ESP.getMaxFreeBlockSize();
|
|
153
|
+
heap_min_free = std::min(heap_min_free, heap_free);
|
|
154
|
+
#endif
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
bool is_memory_critical() const {
|
|
158
|
+
return heap_free < 10000; // Less than 10KB free is critical
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
bool is_memory_low() const {
|
|
162
|
+
return heap_free < 20000; // Less than 20KB free is low
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Network topology metrics
|
|
168
|
+
*/
|
|
169
|
+
struct TopologyStats {
|
|
170
|
+
uint32_t node_count = 0;
|
|
171
|
+
uint32_t connection_count = 0;
|
|
172
|
+
uint32_t max_hops = 0;
|
|
173
|
+
uint32_t connection_changes = 0;
|
|
174
|
+
uint32_t failed_connections = 0;
|
|
175
|
+
|
|
176
|
+
void record_topology_change(uint32_t nodes, uint32_t connections,
|
|
177
|
+
uint32_t hops) {
|
|
178
|
+
node_count = nodes;
|
|
179
|
+
connection_count = connections;
|
|
180
|
+
max_hops = hops;
|
|
181
|
+
connection_changes++;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
void record_failed_connection() { failed_connections++; }
|
|
185
|
+
|
|
186
|
+
double connection_stability() const {
|
|
187
|
+
return connection_changes > 0
|
|
188
|
+
? 1.0 - static_cast<double>(failed_connections) /
|
|
189
|
+
connection_changes
|
|
190
|
+
: 1.0;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Comprehensive metrics collector
|
|
196
|
+
*/
|
|
197
|
+
class MetricsCollector {
|
|
198
|
+
public:
|
|
199
|
+
MetricsCollector() : start_time_(get_current_time()) {}
|
|
200
|
+
|
|
201
|
+
MessageStats& message_stats() { return message_stats_; }
|
|
202
|
+
MemoryStats& memory_stats() { return memory_stats_; }
|
|
203
|
+
TopologyStats& topology_stats() { return topology_stats_; }
|
|
204
|
+
|
|
205
|
+
const MessageStats& message_stats() const { return message_stats_; }
|
|
206
|
+
const MemoryStats& memory_stats() const { return memory_stats_; }
|
|
207
|
+
const TopologyStats& topology_stats() const { return topology_stats_; }
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Update all metrics (call periodically)
|
|
211
|
+
*/
|
|
212
|
+
void update() {
|
|
213
|
+
memory_stats_.update();
|
|
214
|
+
last_update_ = get_current_time();
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Get uptime in seconds
|
|
219
|
+
*/
|
|
220
|
+
uint32_t uptime_seconds() const {
|
|
221
|
+
return (get_current_time() - start_time_) / 1000;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Generate JSON status report
|
|
226
|
+
*/
|
|
227
|
+
TSTRING generate_status_json() const {
|
|
228
|
+
TSTRING json = "{";
|
|
229
|
+
json += "\"uptime\":";
|
|
230
|
+
json += std::to_string(uptime_seconds());
|
|
231
|
+
json += ",";
|
|
232
|
+
json += "\"messages\":{";
|
|
233
|
+
json += "\"sent\":";
|
|
234
|
+
json += std::to_string(message_stats_.messages_sent);
|
|
235
|
+
json += ",";
|
|
236
|
+
json += "\"received\":";
|
|
237
|
+
json += std::to_string(message_stats_.messages_received);
|
|
238
|
+
json += ",";
|
|
239
|
+
json += "\"dropped\":";
|
|
240
|
+
json += std::to_string(message_stats_.messages_dropped);
|
|
241
|
+
json += ",";
|
|
242
|
+
json += "\"avg_latency\":";
|
|
243
|
+
json += std::to_string(message_stats_.average_latency_ms());
|
|
244
|
+
json += ",";
|
|
245
|
+
json += "\"throughput\":";
|
|
246
|
+
json += std::to_string(message_stats_.throughput_bps());
|
|
247
|
+
json += ",";
|
|
248
|
+
json += "\"packet_loss\":";
|
|
249
|
+
json += std::to_string(message_stats_.packet_loss_rate());
|
|
250
|
+
json += "},";
|
|
251
|
+
json += "\"memory\":{";
|
|
252
|
+
json += "\"heap_free\":";
|
|
253
|
+
json += std::to_string(memory_stats_.heap_free);
|
|
254
|
+
json += ",";
|
|
255
|
+
json += "\"heap_min\":";
|
|
256
|
+
json += std::to_string(memory_stats_.heap_min_free);
|
|
257
|
+
json += ",";
|
|
258
|
+
json += "\"critical\":";
|
|
259
|
+
json += (memory_stats_.is_memory_critical() ? "true" : "false");
|
|
260
|
+
json += "},";
|
|
261
|
+
json += "\"topology\":{";
|
|
262
|
+
json += "\"nodes\":";
|
|
263
|
+
json += std::to_string(topology_stats_.node_count);
|
|
264
|
+
json += ",";
|
|
265
|
+
json += "\"connections\":";
|
|
266
|
+
json += std::to_string(topology_stats_.connection_count);
|
|
267
|
+
json += ",";
|
|
268
|
+
json += "\"max_hops\":";
|
|
269
|
+
json += std::to_string(topology_stats_.max_hops);
|
|
270
|
+
json += ",";
|
|
271
|
+
json += "\"stability\":";
|
|
272
|
+
json += std::to_string(topology_stats_.connection_stability());
|
|
273
|
+
json += "}";
|
|
274
|
+
json += "}";
|
|
275
|
+
return json;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Reset all metrics
|
|
280
|
+
*/
|
|
281
|
+
void reset() {
|
|
282
|
+
message_stats_.reset();
|
|
283
|
+
topology_stats_ = TopologyStats{};
|
|
284
|
+
start_time_ = get_current_time();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Check if metrics indicate performance issues
|
|
289
|
+
*/
|
|
290
|
+
bool has_performance_issues() const {
|
|
291
|
+
return memory_stats_.is_memory_critical() ||
|
|
292
|
+
message_stats_.packet_loss_rate() > 0.1 || // > 10% packet loss
|
|
293
|
+
topology_stats_.connection_stability() < 0.8; // < 80% stability
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
private:
|
|
297
|
+
uint32_t get_current_time() const {
|
|
298
|
+
#ifdef ARDUINO
|
|
299
|
+
return millis();
|
|
300
|
+
#else
|
|
301
|
+
auto now = std::chrono::steady_clock::now();
|
|
302
|
+
auto duration = now.time_since_epoch();
|
|
303
|
+
return std::chrono::duration_cast<std::chrono::milliseconds>(duration)
|
|
304
|
+
.count();
|
|
305
|
+
#endif
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
MessageStats message_stats_;
|
|
309
|
+
MemoryStats memory_stats_;
|
|
310
|
+
TopologyStats topology_stats_;
|
|
311
|
+
uint32_t start_time_;
|
|
312
|
+
uint32_t last_update_ = 0;
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
// Global metrics instance (optional, can be disabled)
|
|
316
|
+
#ifdef PAINLESS_MESH_ENABLE_METRICS
|
|
317
|
+
extern MetricsCollector global_metrics;
|
|
318
|
+
#endif
|
|
319
|
+
|
|
320
|
+
} // namespace metrics
|
|
321
|
+
} // namespace painlessmesh
|
|
322
|
+
|
|
323
323
|
#endif // _PAINLESS_MESH_METRICS_HPP_
|