feat: improve task handling, refactoring

This commit is contained in:
2025-08-28 20:46:27 +02:00
parent d7e98a41fa
commit 4b63d1011f
8 changed files with 83 additions and 183 deletions

View File

@@ -11,9 +11,6 @@
#include "NodeInfo.h"
#include "TaskManager.h"
using namespace std;
using namespace std::placeholders;
class ApiServer {
public:
ApiServer(NodeContext& ctx, TaskManager& taskMgr, uint16_t port = 80);
@@ -47,7 +44,7 @@ private:
std::vector<std::tuple<String, int>> serviceRegistry;
std::vector<EndpointCapability> capabilityRegistry;
void onClusterMembersRequest(AsyncWebServerRequest *request);
void methodToStr(const std::tuple<String, int> &endpoint, ArduinoJson::V742PB22::JsonObject &apiObj);
void methodToStr(const std::tuple<String, int> &endpoint, JsonObject &apiObj);
void onSystemStatusRequest(AsyncWebServerRequest *request);
void onFirmwareUpdateRequest(AsyncWebServerRequest *request);
void onFirmwareUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final);
@@ -59,4 +56,7 @@ private:
// Capabilities endpoint
void onCapabilitiesRequest(AsyncWebServerRequest *request);
// Internal helpers
void registerServiceForLocalNode(const String& uri, int method);
};

View File

@@ -1,5 +1,5 @@
#pragma once
#include <TaskSchedulerDeclarations.h>
#include <WiFiUdp.h>
#include <map>
#include "NodeInfo.h"
@@ -11,7 +11,6 @@ class NodeContext {
public:
NodeContext();
~NodeContext();
Scheduler* scheduler;
WiFiUDP* udp;
String hostname;
IPAddress localIP;

View File

@@ -7,10 +7,6 @@
#include "NodeContext.h"
#include <ArduinoJson.h>
// Forward declarations to avoid multiple definition errors
class Task;
class Scheduler;
// Define our own callback type to avoid conflict with TaskScheduler
using TaskFunction = std::function<void()>;
@@ -60,13 +56,8 @@ public:
private:
NodeContext& ctx;
std::vector<Task*> tasks;
std::vector<TaskDefinition> taskDefinitions;
std::vector<unsigned long> lastExecutionTimes;
Task* findTask(const std::string& name) const;
void createTask(const TaskDefinition& taskDef);
// Static callback registry for all TaskManager instances
static std::map<std::string, std::function<void()>> callbackRegistry;
static void executeCallback(const std::string& taskName);
int findTaskIndex(const std::string& name) const;
};

View File

@@ -17,7 +17,6 @@ monitor_speed = 115200
lib_deps =
esp32async/ESPAsyncWebServer@^3.8.0
bblanchon/ArduinoJson@^7.4.2
arkhipenko/TaskScheduler@^3.8.5
[env:esp01_1m]
platform = platformio/espressif8266@^4.2.1

View File

@@ -1,29 +1,38 @@
#include "ApiServer.h"
#include <algorithm>
// Shared helper for HTTP method to string
static const char* methodStrFromInt(int method) {
switch (method) {
case HTTP_GET: return "GET";
case HTTP_POST: return "POST";
case HTTP_PUT: return "PUT";
case HTTP_DELETE: return "DELETE";
case HTTP_PATCH: return "PATCH";
default: return "UNKNOWN";
}
}
ApiServer::ApiServer(NodeContext& ctx, TaskManager& taskMgr, uint16_t port) : server(port), ctx(ctx), taskManager(taskMgr) {}
void ApiServer::addEndpoint(const String& uri, int method, std::function<void(AsyncWebServerRequest*)> requestHandler) {
void ApiServer::registerServiceForLocalNode(const String& uri, int method) {
serviceRegistry.push_back(std::make_tuple(uri, method));
// Store in NodeInfo for local node
if (ctx.memberList && !ctx.memberList->empty()) {
auto it = ctx.memberList->find(ctx.hostname);
if (it != ctx.memberList->end()) {
it->second.apiEndpoints.push_back(std::make_tuple(uri, method));
}
}
}
void ApiServer::addEndpoint(const String& uri, int method, std::function<void(AsyncWebServerRequest*)> requestHandler) {
registerServiceForLocalNode(uri, method);
server.on(uri.c_str(), method, requestHandler);
}
void ApiServer::addEndpoint(const String& uri, int method, std::function<void(AsyncWebServerRequest*)> requestHandler,
std::function<void(AsyncWebServerRequest*, const String&, size_t, uint8_t*, size_t, bool)> uploadHandler) {
serviceRegistry.push_back(std::make_tuple(uri, method));
if (ctx.memberList && !ctx.memberList->empty()) {
auto it = ctx.memberList->find(ctx.hostname);
if (it != ctx.memberList->end()) {
it->second.apiEndpoints.push_back(std::make_tuple(uri, method));
}
}
registerServiceForLocalNode(uri, method);
server.on(uri.c_str(), method, requestHandler, uploadHandler);
}
@@ -31,13 +40,7 @@ void ApiServer::addEndpoint(const String& uri, int method, std::function<void(As
void ApiServer::addEndpoint(const String& uri, int method, std::function<void(AsyncWebServerRequest*)> requestHandler,
const std::vector<ParamSpec>& params) {
capabilityRegistry.push_back(EndpointCapability{uri, method, params});
serviceRegistry.push_back(std::make_tuple(uri, method));
if (ctx.memberList && !ctx.memberList->empty()) {
auto it = ctx.memberList->find(ctx.hostname);
if (it != ctx.memberList->end()) {
it->second.apiEndpoints.push_back(std::make_tuple(uri, method));
}
}
registerServiceForLocalNode(uri, method);
server.on(uri.c_str(), method, requestHandler);
}
@@ -45,13 +48,7 @@ void ApiServer::addEndpoint(const String& uri, int method, std::function<void(As
std::function<void(AsyncWebServerRequest*, const String&, size_t, uint8_t*, size_t, bool)> uploadHandler,
const std::vector<ParamSpec>& params) {
capabilityRegistry.push_back(EndpointCapability{uri, method, params});
serviceRegistry.push_back(std::make_tuple(uri, method));
if (ctx.memberList && !ctx.memberList->empty()) {
auto it = ctx.memberList->find(ctx.hostname);
if (it != ctx.memberList->end()) {
it->second.apiEndpoints.push_back(std::make_tuple(uri, method));
}
}
registerServiceForLocalNode(uri, method);
server.on(uri.c_str(), method, requestHandler, uploadHandler);
}
@@ -131,37 +128,15 @@ void ApiServer::onClusterMembersRequest(AsyncWebServerRequest *request) {
request->send(200, "application/json", json);
}
void ApiServer::methodToStr(const std::tuple<String, int> &endpoint, ArduinoJson::V742PB22::JsonObject &apiObj)
void ApiServer::methodToStr(const std::tuple<String, int> &endpoint, JsonObject &apiObj)
{
int method = std::get<1>(endpoint);
const char *methodStr = nullptr;
switch (method)
{
case HTTP_GET:
methodStr = "GET";
break;
case HTTP_POST:
methodStr = "POST";
break;
case HTTP_PUT:
methodStr = "PUT";
break;
case HTTP_DELETE:
methodStr = "DELETE";
break;
case HTTP_PATCH:
methodStr = "PATCH";
break;
default:
methodStr = "UNKNOWN";
break;
}
apiObj["method"] = methodStr;
apiObj["method"] = methodStrFromInt(method);
}
void ApiServer::onFirmwareUpdateRequest(AsyncWebServerRequest *request) {
bool hasError = !Update.hasError();
AsyncWebServerResponse *response = request->beginResponse(200, "application/json", hasError ? "{\"status\": \"OK\"}" : "{\"status\": \"FAIL\"}");
bool success = !Update.hasError();
AsyncWebServerResponse *response = request->beginResponse(200, "application/json", success ? "{\"status\": \"OK\"}" : "{\"status\": \"FAIL\"}");
response->addHeader("Connection", "close");
request->send(response);
request->onDisconnect([]() {
@@ -183,8 +158,6 @@ void ApiServer::onFirmwareUpload(AsyncWebServerRequest *request, const String &f
response->addHeader("Connection", "close");
request->send(response);
return;
} else {
Update.printError(Serial);
}
}
if (!Update.hasError()){
@@ -221,10 +194,14 @@ void ApiServer::onRestartRequest(AsyncWebServerRequest *request) {
}
void ApiServer::onTaskStatusRequest(AsyncWebServerRequest *request) {
JsonDocument doc;
// Use a separate document as scratch space for task statuses to avoid interfering with the response root
JsonDocument scratch;
// Get comprehensive task status from TaskManager
auto taskStatuses = taskManager.getAllTaskStatuses(doc);
auto taskStatuses = taskManager.getAllTaskStatuses(scratch);
// Build response document
JsonDocument doc;
// Add summary information
JsonObject summaryObj = doc["summary"].to<JsonObject>();
@@ -344,16 +321,6 @@ void ApiServer::onCapabilitiesRequest(AsyncWebServerRequest *request) {
auto makeKey = [](const String& uri, int method) {
String k = uri; k += "|"; k += method; return k;
};
auto methodStrFromInt = [](int method) -> const char* {
switch (method) {
case HTTP_GET: return "GET";
case HTTP_POST: return "POST";
case HTTP_PUT: return "PUT";
case HTTP_DELETE: return "DELETE";
case HTTP_PATCH: return "PATCH";
default: return "UNKNOWN";
}
};
// Rich entries first
for (const auto& cap : capabilityRegistry) {

View File

@@ -1,14 +1,12 @@
#include "NodeContext.h"
NodeContext::NodeContext() {
scheduler = new Scheduler();
udp = new WiFiUDP();
memberList = new std::map<String, NodeInfo>();
hostname = "";
}
NodeContext::~NodeContext() {
delete scheduler;
delete udp;
delete memberList;
}

View File

@@ -14,7 +14,7 @@ void updateNodeStatus(NodeInfo &node, unsigned long now, unsigned long inactive_
if (diff < inactive_threshold) {
node.status = NodeInfo::ACTIVE;
} else if (diff < dead_threshold) {
node.status = NodeInfo::DEAD;
node.status = NodeInfo::INACTIVE;
} else {
node.status = NodeInfo::DEAD;
}

View File

@@ -1,18 +1,9 @@
#include "TaskManager.h"
#include <Arduino.h>
#include <TaskScheduler.h>
// Define static members
std::map<std::string, std::function<void()>> TaskManager::callbackRegistry;
TaskManager::TaskManager(NodeContext& ctx) : ctx(ctx) {}
TaskManager::~TaskManager() {
// Clean up tasks
for (auto task : tasks) {
delete task;
}
tasks.clear();
}
void TaskManager::registerTask(const std::string& name, unsigned long interval, TaskFunction callback, bool enabled, bool autoStart) {
@@ -25,44 +16,21 @@ void TaskManager::registerTask(const TaskDefinition& taskDef) {
}
void TaskManager::initialize() {
// Initialize the scheduler
ctx.scheduler->init();
// Create all registered tasks
for (const auto& taskDef : taskDefinitions) {
createTask(taskDef);
}
// Ensure timing vector matches number of tasks
lastExecutionTimes.assign(taskDefinitions.size(), 0UL);
// Enable tasks that should auto-start
for (const auto& taskDef : taskDefinitions) {
for (auto& taskDef : taskDefinitions) {
if (taskDef.autoStart && taskDef.enabled) {
enableTask(taskDef.name);
taskDef.enabled = true;
}
}
}
void TaskManager::createTask(const TaskDefinition& taskDef) {
// Store the callback in the static registry
callbackRegistry[taskDef.name] = taskDef.callback;
// Create a dummy task - we'll handle execution ourselves
Task* task = new Task(0, TASK_FOREVER, []() { /* Dummy callback */ });
task->setInterval(taskDef.interval);
// Add to scheduler
ctx.scheduler->addTask(*task);
// Store task pointer
tasks.push_back(task);
Serial.printf("[TaskManager] Created task: %s (interval: %lu ms)\n",
taskDef.name.c_str(), taskDef.interval);
}
void TaskManager::enableTask(const std::string& name) {
Task* task = findTask(name);
if (task) {
task->enable();
int idx = findTaskIndex(name);
if (idx >= 0) {
taskDefinitions[idx].enabled = true;
Serial.printf("[TaskManager] Enabled task: %s\n", name.c_str());
} else {
Serial.printf("[TaskManager] Warning: Task not found: %s\n", name.c_str());
@@ -70,9 +38,9 @@ void TaskManager::enableTask(const std::string& name) {
}
void TaskManager::disableTask(const std::string& name) {
Task* task = findTask(name);
if (task) {
task->disable();
int idx = findTaskIndex(name);
if (idx >= 0) {
taskDefinitions[idx].enabled = false;
Serial.printf("[TaskManager] Disabled task: %s\n", name.c_str());
} else {
Serial.printf("[TaskManager] Warning: Task not found: %s\n", name.c_str());
@@ -80,9 +48,9 @@ void TaskManager::disableTask(const std::string& name) {
}
void TaskManager::setTaskInterval(const std::string& name, unsigned long interval) {
Task* task = findTask(name);
if (task) {
task->setInterval(interval);
int idx = findTaskIndex(name);
if (idx >= 0) {
taskDefinitions[idx].interval = interval;
Serial.printf("[TaskManager] Set interval for task %s: %lu ms\n", name.c_str(), interval);
} else {
Serial.printf("[TaskManager] Warning: Task not found: %s\n", name.c_str());
@@ -90,50 +58,38 @@ void TaskManager::setTaskInterval(const std::string& name, unsigned long interva
}
void TaskManager::startTask(const std::string& name) {
Task* task = findTask(name);
if (task) {
task->enable();
Serial.printf("[TaskManager] Started task: %s\n", name.c_str());
} else {
Serial.printf("[TaskManager] Warning: Task not found: %s\n", name.c_str());
}
enableTask(name);
}
void TaskManager::stopTask(const std::string& name) {
Task* task = findTask(name);
if (task) {
task->disable();
Serial.printf("[TaskManager] Stopped task: %s\n", name.c_str());
} else {
Serial.printf("[TaskManager] Warning: Task not found: %s\n", name.c_str());
}
disableTask(name);
}
bool TaskManager::isTaskEnabled(const std::string& name) const {
Task* task = findTask(name);
return task ? task->isEnabled() : false;
int idx = findTaskIndex(name);
return idx >= 0 ? taskDefinitions[idx].enabled : false;
}
bool TaskManager::isTaskRunning(const std::string& name) const {
Task* task = findTask(name);
return task ? task->isEnabled() : false;
int idx = findTaskIndex(name);
return idx >= 0 ? taskDefinitions[idx].enabled : false;
}
unsigned long TaskManager::getTaskInterval(const std::string& name) const {
Task* task = findTask(name);
return task ? task->getInterval() : 0;
int idx = findTaskIndex(name);
return idx >= 0 ? taskDefinitions[idx].interval : 0;
}
void TaskManager::enableAllTasks() {
for (auto task : tasks) {
task->enable();
for (auto& taskDef : taskDefinitions) {
taskDef.enabled = true;
}
Serial.println("[TaskManager] Enabled all tasks");
}
void TaskManager::disableAllTasks() {
for (auto task : tasks) {
task->disable();
for (auto& taskDef : taskDefinitions) {
taskDef.enabled = false;
}
Serial.println("[TaskManager] Disabled all tasks");
}
@@ -142,41 +98,32 @@ void TaskManager::printTaskStatus() const {
Serial.println("\n[TaskManager] Task Status:");
Serial.println("==========================");
for (size_t i = 0; i < tasks.size() && i < taskDefinitions.size(); ++i) {
const auto& taskDef = taskDefinitions[i];
const auto& task = tasks[i];
for (const auto& taskDef : taskDefinitions) {
Serial.printf(" %s: %s (interval: %lu ms)\n",
taskDef.name.c_str(),
task->isEnabled() ? "ENABLED" : "DISABLED",
task->getInterval());
taskDef.enabled ? "ENABLED" : "DISABLED",
taskDef.interval);
}
Serial.println("==========================\n");
}
void TaskManager::execute() {
// Execute all enabled tasks by calling their stored callbacks
static unsigned long lastExecutionTimes[100] = {0}; // Simple array for timing
static int taskCount = 0;
if (taskCount == 0) {
taskCount = tasks.size();
// Ensure timing vector matches number of tasks
if (lastExecutionTimes.size() != taskDefinitions.size()) {
lastExecutionTimes.assign(taskDefinitions.size(), 0UL);
}
unsigned long currentTime = millis();
for (size_t i = 0; i < tasks.size() && i < taskDefinitions.size(); ++i) {
Task* task = tasks[i];
const std::string& taskName = taskDefinitions[i].name;
for (size_t i = 0; i < taskDefinitions.size(); ++i) {
auto& taskDef = taskDefinitions[i];
if (task->isEnabled()) {
if (taskDef.enabled) {
// Check if it's time to run this task
if (currentTime - lastExecutionTimes[i] >= task->getInterval()) {
// Execute the stored callback
if (callbackRegistry.find(taskName) != callbackRegistry.end()) {
callbackRegistry[taskName]();
if (currentTime - lastExecutionTimes[i] >= taskDef.interval) {
if (taskDef.callback) {
taskDef.callback();
}
// Update the last execution time
lastExecutionTimes[i] = currentTime;
}
@@ -184,27 +131,26 @@ void TaskManager::execute() {
}
}
Task* TaskManager::findTask(const std::string& name) const {
for (size_t i = 0; i < tasks.size() && i < taskDefinitions.size(); ++i) {
int TaskManager::findTaskIndex(const std::string& name) const {
for (size_t i = 0; i < taskDefinitions.size(); ++i) {
if (taskDefinitions[i].name == name) {
return tasks[i];
return static_cast<int>(i);
}
}
return nullptr;
return -1;
}
std::vector<std::pair<std::string, JsonObject>> TaskManager::getAllTaskStatuses(JsonDocument& doc) const {
std::vector<std::pair<std::string, JsonObject>> taskStatuses;
for (size_t i = 0; i < tasks.size() && i < taskDefinitions.size(); ++i) {
for (size_t i = 0; i < taskDefinitions.size(); ++i) {
const auto& taskDef = taskDefinitions[i];
const auto& task = tasks[i];
JsonObject taskStatus = doc.add<JsonObject>();
taskStatus["name"] = taskDef.name;
taskStatus["interval"] = task->getInterval();
taskStatus["enabled"] = task->isEnabled();
taskStatus["running"] = task->isEnabled(); // For now, enabled = running
taskStatus["interval"] = taskDef.interval;
taskStatus["enabled"] = taskDef.enabled;
taskStatus["running"] = taskDef.enabled; // For now, enabled = running
taskStatus["autoStart"] = taskDef.autoStart;
taskStatuses.push_back(std::make_pair(taskDef.name, taskStatus));