feat: unified monitoring service
This commit is contained in:
@@ -11,6 +11,7 @@
|
|||||||
#include "core/TaskManager.h"
|
#include "core/TaskManager.h"
|
||||||
#include "Service.h"
|
#include "Service.h"
|
||||||
#include "util/Logging.h"
|
#include "util/Logging.h"
|
||||||
|
#include "util/CpuUsage.h"
|
||||||
|
|
||||||
class Spore {
|
class Spore {
|
||||||
public:
|
public:
|
||||||
@@ -33,6 +34,11 @@ public:
|
|||||||
TaskManager& getTaskManager() { return taskManager; }
|
TaskManager& getTaskManager() { return taskManager; }
|
||||||
ClusterManager& getCluster() { return cluster; }
|
ClusterManager& getCluster() { return cluster; }
|
||||||
ApiServer& getApiServer() { return apiServer; }
|
ApiServer& getApiServer() { return apiServer; }
|
||||||
|
|
||||||
|
// CPU usage monitoring
|
||||||
|
CpuUsage& getCpuUsage() { return cpuUsage; }
|
||||||
|
float getCurrentCpuUsage() const { return cpuUsage.getCpuUsage(); }
|
||||||
|
float getAverageCpuUsage() const { return cpuUsage.getAverageCpuUsage(); }
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -45,6 +51,7 @@ private:
|
|||||||
TaskManager taskManager;
|
TaskManager taskManager;
|
||||||
ClusterManager cluster;
|
ClusterManager cluster;
|
||||||
ApiServer apiServer;
|
ApiServer apiServer;
|
||||||
|
CpuUsage cpuUsage;
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Service>> services;
|
std::vector<std::shared_ptr<Service>> services;
|
||||||
bool initialized;
|
bool initialized;
|
||||||
|
|||||||
51
include/spore/services/MonitoringService.h
Normal file
51
include/spore/services/MonitoringService.h
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "spore/Service.h"
|
||||||
|
#include "spore/util/CpuUsage.h"
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
class MonitoringService : public Service {
|
||||||
|
public:
|
||||||
|
MonitoringService(CpuUsage& cpuUsage);
|
||||||
|
void registerEndpoints(ApiServer& api) override;
|
||||||
|
const char* getName() const override { return "Monitoring"; }
|
||||||
|
|
||||||
|
// System resource information
|
||||||
|
struct SystemResources {
|
||||||
|
// CPU information
|
||||||
|
float currentCpuUsage;
|
||||||
|
float averageCpuUsage;
|
||||||
|
float maxCpuUsage;
|
||||||
|
float minCpuUsage;
|
||||||
|
unsigned long measurementCount;
|
||||||
|
bool isMeasuring;
|
||||||
|
|
||||||
|
// Memory information
|
||||||
|
size_t freeHeap;
|
||||||
|
size_t totalHeap;
|
||||||
|
size_t minFreeHeap;
|
||||||
|
size_t maxAllocHeap;
|
||||||
|
size_t heapFragmentation;
|
||||||
|
|
||||||
|
// Filesystem information
|
||||||
|
size_t totalBytes;
|
||||||
|
size_t usedBytes;
|
||||||
|
size_t freeBytes;
|
||||||
|
float usagePercent;
|
||||||
|
|
||||||
|
// System uptime
|
||||||
|
unsigned long uptimeMs;
|
||||||
|
unsigned long uptimeSeconds;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get current system resources
|
||||||
|
SystemResources getSystemResources() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void handleResourcesRequest(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
|
// Helper methods
|
||||||
|
size_t calculateHeapFragmentation() const;
|
||||||
|
void getFilesystemInfo(size_t& totalBytes, size_t& usedBytes) const;
|
||||||
|
|
||||||
|
CpuUsage& cpuUsage;
|
||||||
|
};
|
||||||
118
include/spore/util/CpuUsage.h
Normal file
118
include/spore/util/CpuUsage.h
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief CPU usage measurement utility for ESP32/ESP8266
|
||||||
|
*
|
||||||
|
* This class provides methods to measure CPU usage by tracking idle time
|
||||||
|
* and calculating the percentage of time the CPU is busy vs idle.
|
||||||
|
*/
|
||||||
|
class CpuUsage {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct a new CpuUsage object
|
||||||
|
*/
|
||||||
|
CpuUsage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Destructor
|
||||||
|
*/
|
||||||
|
~CpuUsage() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the CPU usage measurement
|
||||||
|
* Call this once during setup
|
||||||
|
*/
|
||||||
|
void begin();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start measuring CPU usage for the current cycle
|
||||||
|
* Call this at the beginning of your main loop
|
||||||
|
*/
|
||||||
|
void startMeasurement();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief End measuring CPU usage for the current cycle
|
||||||
|
* Call this at the end of your main loop
|
||||||
|
*/
|
||||||
|
void endMeasurement();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the current CPU usage percentage
|
||||||
|
* @return float CPU usage percentage (0.0 to 100.0)
|
||||||
|
*/
|
||||||
|
float getCpuUsage() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the average CPU usage over the measurement window
|
||||||
|
* @return float Average CPU usage percentage (0.0 to 100.0)
|
||||||
|
*/
|
||||||
|
float getAverageCpuUsage() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the maximum CPU usage recorded
|
||||||
|
* @return float Maximum CPU usage percentage (0.0 to 100.0)
|
||||||
|
*/
|
||||||
|
float getMaxCpuUsage() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the minimum CPU usage recorded
|
||||||
|
* @return float Minimum CPU usage percentage (0.0 to 100.0)
|
||||||
|
*/
|
||||||
|
float getMinCpuUsage() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset all CPU usage statistics
|
||||||
|
*/
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if measurement is currently active
|
||||||
|
* @return true if measurement is active, false otherwise
|
||||||
|
*/
|
||||||
|
bool isMeasuring() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the number of measurements taken
|
||||||
|
* @return unsigned long Number of measurements
|
||||||
|
*/
|
||||||
|
unsigned long getMeasurementCount() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Measurement state
|
||||||
|
bool _initialized;
|
||||||
|
bool _measuring;
|
||||||
|
unsigned long _measurementCount;
|
||||||
|
|
||||||
|
// Timing variables
|
||||||
|
unsigned long _cycleStartTime;
|
||||||
|
unsigned long _idleStartTime;
|
||||||
|
unsigned long _totalIdleTime;
|
||||||
|
unsigned long _totalCycleTime;
|
||||||
|
|
||||||
|
// Statistics
|
||||||
|
float _currentCpuUsage;
|
||||||
|
float _averageCpuUsage;
|
||||||
|
float _maxCpuUsage;
|
||||||
|
float _minCpuUsage;
|
||||||
|
unsigned long _totalCpuTime;
|
||||||
|
|
||||||
|
// Rolling average window
|
||||||
|
static constexpr size_t ROLLING_WINDOW_SIZE = 10;
|
||||||
|
float _rollingWindow[ROLLING_WINDOW_SIZE];
|
||||||
|
size_t _rollingIndex;
|
||||||
|
bool _rollingWindowFull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update rolling average calculation
|
||||||
|
* @param value New value to add to rolling average
|
||||||
|
*/
|
||||||
|
void updateRollingAverage(float value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update min/max statistics
|
||||||
|
* @param value New value to check against min/max
|
||||||
|
*/
|
||||||
|
void updateMinMax(float value);
|
||||||
|
};
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
; https://docs.platformio.org/page/projectconf.html
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
[platformio]
|
[platformio]
|
||||||
;default_envs = esp01_1m
|
default_envs = base
|
||||||
src_dir = .
|
src_dir = .
|
||||||
data_dir = ${PROJECT_DIR}/examples/${PIOENV}/data
|
data_dir = ${PROJECT_DIR}/examples/${PIOENV}/data
|
||||||
|
|
||||||
@@ -203,3 +203,42 @@ build_src_filter =
|
|||||||
+<src/spore/types/*.cpp>
|
+<src/spore/types/*.cpp>
|
||||||
+<src/spore/util/*.cpp>
|
+<src/spore/util/*.cpp>
|
||||||
+<src/internal/*.cpp>
|
+<src/internal/*.cpp>
|
||||||
|
|
||||||
|
[env:esp01_1m_cpu_monitor]
|
||||||
|
platform = platformio/espressif8266@^4.2.1
|
||||||
|
board = esp01_1m
|
||||||
|
framework = arduino
|
||||||
|
upload_speed = 115200
|
||||||
|
monitor_speed = 115200
|
||||||
|
board_build.filesystem = littlefs
|
||||||
|
board_build.flash_mode = dout
|
||||||
|
board_build.ldscript = eagle.flash.1m64.ld
|
||||||
|
lib_deps = ${common.lib_deps}
|
||||||
|
build_src_filter =
|
||||||
|
+<examples/cpu_monitor/*.cpp>
|
||||||
|
+<src/spore/*.cpp>
|
||||||
|
+<src/spore/core/*.cpp>
|
||||||
|
+<src/spore/services/*.cpp>
|
||||||
|
+<src/spore/types/*.cpp>
|
||||||
|
+<src/spore/util/*.cpp>
|
||||||
|
+<src/internal/*.cpp>
|
||||||
|
|
||||||
|
[env:d1_mini_cpu_monitor]
|
||||||
|
platform = platformio/espressif8266@^4.2.1
|
||||||
|
board = d1_mini
|
||||||
|
framework = arduino
|
||||||
|
upload_speed = 115200
|
||||||
|
monitor_speed = 115200
|
||||||
|
board_build.filesystem = littlefs
|
||||||
|
board_build.flash_mode = dio
|
||||||
|
board_build.flash_size = 4M
|
||||||
|
board_build.ldscript = eagle.flash.4m1m.ld
|
||||||
|
lib_deps = ${common.lib_deps}
|
||||||
|
build_src_filter =
|
||||||
|
+<examples/cpu_monitor/*.cpp>
|
||||||
|
+<src/spore/*.cpp>
|
||||||
|
+<src/spore/core/*.cpp>
|
||||||
|
+<src/spore/services/*.cpp>
|
||||||
|
+<src/spore/types/*.cpp>
|
||||||
|
+<src/spore/util/*.cpp>
|
||||||
|
+<src/internal/*.cpp>
|
||||||
|
|||||||
@@ -4,17 +4,18 @@
|
|||||||
#include "spore/services/ClusterService.h"
|
#include "spore/services/ClusterService.h"
|
||||||
#include "spore/services/TaskService.h"
|
#include "spore/services/TaskService.h"
|
||||||
#include "spore/services/StaticFileService.h"
|
#include "spore/services/StaticFileService.h"
|
||||||
|
#include "spore/services/MonitoringService.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
Spore::Spore() : ctx(), network(ctx), taskManager(ctx), cluster(ctx, taskManager),
|
Spore::Spore() : ctx(), network(ctx), taskManager(ctx), cluster(ctx, taskManager),
|
||||||
apiServer(ctx, taskManager, ctx.config.api_server_port),
|
apiServer(ctx, taskManager, ctx.config.api_server_port),
|
||||||
initialized(false), apiServerStarted(false) {
|
cpuUsage(), initialized(false), apiServerStarted(false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Spore::Spore(std::initializer_list<std::pair<String, String>> initialLabels)
|
Spore::Spore(std::initializer_list<std::pair<String, String>> initialLabels)
|
||||||
: ctx(initialLabels), network(ctx), taskManager(ctx), cluster(ctx, taskManager),
|
: ctx(initialLabels), network(ctx), taskManager(ctx), cluster(ctx, taskManager),
|
||||||
apiServer(ctx, taskManager, ctx.config.api_server_port),
|
apiServer(ctx, taskManager, ctx.config.api_server_port),
|
||||||
initialized(false), apiServerStarted(false) {
|
cpuUsage(), initialized(false), apiServerStarted(false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Spore::~Spore() {
|
Spore::~Spore() {
|
||||||
@@ -33,6 +34,9 @@ void Spore::setup() {
|
|||||||
// Initialize core components
|
// Initialize core components
|
||||||
initializeCore();
|
initializeCore();
|
||||||
|
|
||||||
|
// Initialize CPU usage monitoring
|
||||||
|
cpuUsage.begin();
|
||||||
|
|
||||||
// Register core services
|
// Register core services
|
||||||
registerCoreServices();
|
registerCoreServices();
|
||||||
|
|
||||||
@@ -68,7 +72,16 @@ void Spore::loop() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start CPU usage measurement
|
||||||
|
cpuUsage.startMeasurement();
|
||||||
|
|
||||||
|
// Execute main tasks
|
||||||
taskManager.execute();
|
taskManager.execute();
|
||||||
|
|
||||||
|
// End CPU usage measurement before yield
|
||||||
|
cpuUsage.endMeasurement();
|
||||||
|
|
||||||
|
// Yield to allow other tasks to run
|
||||||
yield();
|
yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,6 +134,7 @@ void Spore::registerCoreServices() {
|
|||||||
auto clusterService = std::make_shared<ClusterService>(ctx);
|
auto clusterService = std::make_shared<ClusterService>(ctx);
|
||||||
auto taskService = std::make_shared<TaskService>(taskManager);
|
auto taskService = std::make_shared<TaskService>(taskManager);
|
||||||
auto staticFileService = std::make_shared<StaticFileService>(ctx, apiServer);
|
auto staticFileService = std::make_shared<StaticFileService>(ctx, apiServer);
|
||||||
|
auto monitoringService = std::make_shared<MonitoringService>(cpuUsage);
|
||||||
|
|
||||||
// Add to services list
|
// Add to services list
|
||||||
services.push_back(nodeService);
|
services.push_back(nodeService);
|
||||||
@@ -128,6 +142,7 @@ void Spore::registerCoreServices() {
|
|||||||
services.push_back(clusterService);
|
services.push_back(clusterService);
|
||||||
services.push_back(taskService);
|
services.push_back(taskService);
|
||||||
services.push_back(staticFileService);
|
services.push_back(staticFileService);
|
||||||
|
services.push_back(monitoringService);
|
||||||
|
|
||||||
LOG_INFO("Spore", "Core services registered");
|
LOG_INFO("Spore", "Core services registered");
|
||||||
}
|
}
|
||||||
|
|||||||
115
src/spore/services/MonitoringService.cpp
Normal file
115
src/spore/services/MonitoringService.cpp
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
#include "spore/services/MonitoringService.h"
|
||||||
|
#include "spore/core/ApiServer.h"
|
||||||
|
#include "spore/util/Logging.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <FS.h>
|
||||||
|
#include <LittleFS.h>
|
||||||
|
|
||||||
|
MonitoringService::MonitoringService(CpuUsage& cpuUsage)
|
||||||
|
: cpuUsage(cpuUsage) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void MonitoringService::registerEndpoints(ApiServer& api) {
|
||||||
|
api.addEndpoint("/api/monitoring/resources", HTTP_GET,
|
||||||
|
[this](AsyncWebServerRequest* request) { handleResourcesRequest(request); },
|
||||||
|
std::vector<ParamSpec>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
MonitoringService::SystemResources MonitoringService::getSystemResources() const {
|
||||||
|
SystemResources resources;
|
||||||
|
|
||||||
|
// CPU information
|
||||||
|
resources.currentCpuUsage = cpuUsage.getCpuUsage();
|
||||||
|
resources.averageCpuUsage = cpuUsage.getAverageCpuUsage();
|
||||||
|
resources.maxCpuUsage = cpuUsage.getMaxCpuUsage();
|
||||||
|
resources.minCpuUsage = cpuUsage.getMinCpuUsage();
|
||||||
|
resources.measurementCount = cpuUsage.getMeasurementCount();
|
||||||
|
resources.isMeasuring = cpuUsage.isMeasuring();
|
||||||
|
|
||||||
|
// Memory information - ESP8266 compatible
|
||||||
|
resources.freeHeap = ESP.getFreeHeap();
|
||||||
|
resources.totalHeap = 81920; // ESP8266 has ~80KB RAM
|
||||||
|
resources.minFreeHeap = 0; // Not available on ESP8266
|
||||||
|
resources.maxAllocHeap = 0; // Not available on ESP8266
|
||||||
|
resources.heapFragmentation = calculateHeapFragmentation();
|
||||||
|
|
||||||
|
// Filesystem information
|
||||||
|
getFilesystemInfo(resources.totalBytes, resources.usedBytes);
|
||||||
|
resources.freeBytes = resources.totalBytes - resources.usedBytes;
|
||||||
|
resources.usagePercent = resources.totalBytes > 0 ?
|
||||||
|
(float)resources.usedBytes / (float)resources.totalBytes * 100.0f : 0.0f;
|
||||||
|
|
||||||
|
// System uptime
|
||||||
|
resources.uptimeMs = millis();
|
||||||
|
resources.uptimeSeconds = resources.uptimeMs / 1000;
|
||||||
|
|
||||||
|
return resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MonitoringService::handleResourcesRequest(AsyncWebServerRequest* request) {
|
||||||
|
SystemResources resources = getSystemResources();
|
||||||
|
|
||||||
|
JsonDocument doc;
|
||||||
|
|
||||||
|
// CPU section
|
||||||
|
JsonObject cpu = doc["cpu"].to<JsonObject>();
|
||||||
|
cpu["current_usage"] = resources.currentCpuUsage;
|
||||||
|
cpu["average_usage"] = resources.averageCpuUsage;
|
||||||
|
cpu["max_usage"] = resources.maxCpuUsage;
|
||||||
|
cpu["min_usage"] = resources.minCpuUsage;
|
||||||
|
cpu["measurement_count"] = resources.measurementCount;
|
||||||
|
cpu["is_measuring"] = resources.isMeasuring;
|
||||||
|
|
||||||
|
// Memory section
|
||||||
|
JsonObject memory = doc["memory"].to<JsonObject>();
|
||||||
|
memory["free_heap"] = resources.freeHeap;
|
||||||
|
memory["total_heap"] = resources.totalHeap;
|
||||||
|
memory["min_free_heap"] = resources.minFreeHeap;
|
||||||
|
memory["max_alloc_heap"] = resources.maxAllocHeap;
|
||||||
|
memory["heap_fragmentation"] = resources.heapFragmentation;
|
||||||
|
memory["heap_usage_percent"] = resources.totalHeap > 0 ?
|
||||||
|
(float)(resources.totalHeap - resources.freeHeap) / (float)resources.totalHeap * 100.0f : 0.0f;
|
||||||
|
|
||||||
|
// Filesystem section
|
||||||
|
JsonObject filesystem = doc["filesystem"].to<JsonObject>();
|
||||||
|
filesystem["total_bytes"] = resources.totalBytes;
|
||||||
|
filesystem["used_bytes"] = resources.usedBytes;
|
||||||
|
filesystem["free_bytes"] = resources.freeBytes;
|
||||||
|
filesystem["usage_percent"] = resources.usagePercent;
|
||||||
|
|
||||||
|
// System section
|
||||||
|
JsonObject system = doc["system"].to<JsonObject>();
|
||||||
|
system["uptime_ms"] = resources.uptimeMs;
|
||||||
|
system["uptime_seconds"] = resources.uptimeSeconds;
|
||||||
|
system["uptime_formatted"] = String(resources.uptimeSeconds / 3600) + "h " +
|
||||||
|
String((resources.uptimeSeconds % 3600) / 60) + "m " +
|
||||||
|
String(resources.uptimeSeconds % 60) + "s";
|
||||||
|
|
||||||
|
String json;
|
||||||
|
serializeJson(doc, json);
|
||||||
|
request->send(200, "application/json", json);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MonitoringService::calculateHeapFragmentation() const {
|
||||||
|
size_t freeHeap = ESP.getFreeHeap();
|
||||||
|
size_t maxAllocHeap = 0; // Not available on ESP8266
|
||||||
|
|
||||||
|
if (maxAllocHeap == 0) return 0;
|
||||||
|
|
||||||
|
// Calculate fragmentation as percentage of free heap that can't be allocated in one block
|
||||||
|
return (freeHeap - maxAllocHeap) * 100 / freeHeap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MonitoringService::getFilesystemInfo(size_t& totalBytes, size_t& usedBytes) const {
|
||||||
|
totalBytes = 0;
|
||||||
|
usedBytes = 0;
|
||||||
|
|
||||||
|
if (LittleFS.begin()) {
|
||||||
|
FSInfo fsInfo;
|
||||||
|
if (LittleFS.info(fsInfo)) {
|
||||||
|
totalBytes = fsInfo.totalBytes;
|
||||||
|
usedBytes = fsInfo.usedBytes;
|
||||||
|
}
|
||||||
|
LittleFS.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
185
src/spore/util/CpuUsage.cpp
Normal file
185
src/spore/util/CpuUsage.cpp
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
#include "spore/util/CpuUsage.h"
|
||||||
|
|
||||||
|
CpuUsage::CpuUsage()
|
||||||
|
: _initialized(false)
|
||||||
|
, _measuring(false)
|
||||||
|
, _measurementCount(0)
|
||||||
|
, _cycleStartTime(0)
|
||||||
|
, _idleStartTime(0)
|
||||||
|
, _totalIdleTime(0)
|
||||||
|
, _totalCycleTime(0)
|
||||||
|
, _currentCpuUsage(0.0f)
|
||||||
|
, _averageCpuUsage(0.0f)
|
||||||
|
, _maxCpuUsage(0.0f)
|
||||||
|
, _minCpuUsage(100.0f)
|
||||||
|
, _totalCpuTime(0)
|
||||||
|
, _rollingIndex(0)
|
||||||
|
, _rollingWindowFull(false) {
|
||||||
|
|
||||||
|
// Initialize rolling window
|
||||||
|
for (size_t i = 0; i < ROLLING_WINDOW_SIZE; ++i) {
|
||||||
|
_rollingWindow[i] = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CpuUsage::begin() {
|
||||||
|
if (_initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_initialized = true;
|
||||||
|
_measurementCount = 0;
|
||||||
|
_totalIdleTime = 0;
|
||||||
|
_totalCycleTime = 0;
|
||||||
|
_totalCpuTime = 0;
|
||||||
|
_currentCpuUsage = 0.0f;
|
||||||
|
_averageCpuUsage = 0.0f;
|
||||||
|
_maxCpuUsage = 0.0f;
|
||||||
|
_minCpuUsage = 100.0f;
|
||||||
|
_rollingIndex = 0;
|
||||||
|
_rollingWindowFull = false;
|
||||||
|
|
||||||
|
// Initialize rolling window
|
||||||
|
for (size_t i = 0; i < ROLLING_WINDOW_SIZE; ++i) {
|
||||||
|
_rollingWindow[i] = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CpuUsage::startMeasurement() {
|
||||||
|
if (!_initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_measuring) {
|
||||||
|
// If already measuring, end the previous measurement first
|
||||||
|
endMeasurement();
|
||||||
|
}
|
||||||
|
|
||||||
|
_measuring = true;
|
||||||
|
_cycleStartTime = millis();
|
||||||
|
_idleStartTime = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CpuUsage::endMeasurement() {
|
||||||
|
if (!_initialized || !_measuring) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long cycleEndTime = millis();
|
||||||
|
unsigned long cycleDuration = cycleEndTime - _cycleStartTime;
|
||||||
|
|
||||||
|
// Calculate idle time (time spent in yield() calls)
|
||||||
|
unsigned long idleTime = cycleEndTime - _idleStartTime;
|
||||||
|
|
||||||
|
// Calculate CPU usage
|
||||||
|
if (cycleDuration > 0) {
|
||||||
|
_currentCpuUsage = ((float)(cycleDuration - idleTime) / (float)cycleDuration) * 100.0f;
|
||||||
|
|
||||||
|
// Clamp to valid range
|
||||||
|
if (_currentCpuUsage < 0.0f) {
|
||||||
|
_currentCpuUsage = 0.0f;
|
||||||
|
} else if (_currentCpuUsage > 100.0f) {
|
||||||
|
_currentCpuUsage = 100.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update statistics
|
||||||
|
_totalCycleTime += cycleDuration;
|
||||||
|
_totalIdleTime += idleTime;
|
||||||
|
_totalCpuTime += (cycleDuration - idleTime);
|
||||||
|
_measurementCount++;
|
||||||
|
|
||||||
|
// Update rolling average
|
||||||
|
updateRollingAverage(_currentCpuUsage);
|
||||||
|
|
||||||
|
// Update min/max
|
||||||
|
updateMinMax(_currentCpuUsage);
|
||||||
|
|
||||||
|
// Calculate overall average
|
||||||
|
if (_measurementCount > 0) {
|
||||||
|
_averageCpuUsage = ((float)_totalCpuTime / (float)_totalCycleTime) * 100.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_measuring = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float CpuUsage::getCpuUsage() const {
|
||||||
|
return _currentCpuUsage;
|
||||||
|
}
|
||||||
|
|
||||||
|
float CpuUsage::getAverageCpuUsage() const {
|
||||||
|
if (_rollingWindowFull) {
|
||||||
|
return _averageCpuUsage;
|
||||||
|
} else if (_measurementCount > 0) {
|
||||||
|
// Calculate average from rolling window
|
||||||
|
float sum = 0.0f;
|
||||||
|
for (size_t i = 0; i < _rollingIndex; ++i) {
|
||||||
|
sum += _rollingWindow[i];
|
||||||
|
}
|
||||||
|
return sum / (float)_rollingIndex;
|
||||||
|
}
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float CpuUsage::getMaxCpuUsage() const {
|
||||||
|
return _maxCpuUsage;
|
||||||
|
}
|
||||||
|
|
||||||
|
float CpuUsage::getMinCpuUsage() const {
|
||||||
|
return _minCpuUsage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CpuUsage::reset() {
|
||||||
|
_measurementCount = 0;
|
||||||
|
_totalIdleTime = 0;
|
||||||
|
_totalCycleTime = 0;
|
||||||
|
_totalCpuTime = 0;
|
||||||
|
_currentCpuUsage = 0.0f;
|
||||||
|
_averageCpuUsage = 0.0f;
|
||||||
|
_maxCpuUsage = 0.0f;
|
||||||
|
_minCpuUsage = 100.0f;
|
||||||
|
_rollingIndex = 0;
|
||||||
|
_rollingWindowFull = false;
|
||||||
|
|
||||||
|
// Reset rolling window
|
||||||
|
for (size_t i = 0; i < ROLLING_WINDOW_SIZE; ++i) {
|
||||||
|
_rollingWindow[i] = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CpuUsage::isMeasuring() const {
|
||||||
|
return _measuring;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long CpuUsage::getMeasurementCount() const {
|
||||||
|
return _measurementCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CpuUsage::updateRollingAverage(float value) {
|
||||||
|
_rollingWindow[_rollingIndex] = value;
|
||||||
|
_rollingIndex++;
|
||||||
|
|
||||||
|
if (_rollingIndex >= ROLLING_WINDOW_SIZE) {
|
||||||
|
_rollingIndex = 0;
|
||||||
|
_rollingWindowFull = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate rolling average
|
||||||
|
float sum = 0.0f;
|
||||||
|
size_t count = _rollingWindowFull ? ROLLING_WINDOW_SIZE : _rollingIndex;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
sum += _rollingWindow[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
_averageCpuUsage = sum / (float)count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CpuUsage::updateMinMax(float value) {
|
||||||
|
if (value > _maxCpuUsage) {
|
||||||
|
_maxCpuUsage = value;
|
||||||
|
}
|
||||||
|
if (value < _minCpuUsage) {
|
||||||
|
_minCpuUsage = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user