feat: task manager

This commit is contained in:
2025-08-21 21:52:25 +02:00
parent 953768b681
commit f80b594d21
8 changed files with 495 additions and 35 deletions

View File

@@ -1,6 +1,6 @@
#include "ApiServer.h"
ApiServer::ApiServer(NodeContext& ctx, uint16_t port) : server(port), ctx(ctx) {}
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) {
serviceRegistry.push_back(std::make_tuple(uri, method));
@@ -37,6 +37,13 @@ void ApiServer::begin() {
);
addEndpoint("/api/node/restart", HTTP_POST,
std::bind(&ApiServer::onRestartRequest, this, std::placeholders::_1));
// Task management endpoints
addEndpoint("/api/tasks/status", HTTP_GET,
std::bind(&ApiServer::onTaskStatusRequest, this, std::placeholders::_1));
addEndpoint("/api/tasks/control", HTTP_POST,
std::bind(&ApiServer::onTaskControlRequest, this, std::placeholders::_1));
server.begin();
}
@@ -173,4 +180,71 @@ void ApiServer::onRestartRequest(AsyncWebServerRequest *request) {
delay(10);
ESP.restart();
});
}
void ApiServer::onTaskStatusRequest(AsyncWebServerRequest *request) {
JsonDocument doc;
JsonArray tasksArr = doc["tasks"].to<JsonArray>();
// This would need to be implemented in TaskManager to expose task status
// For now, we'll return a basic response
JsonObject taskObj = tasksArr.add<JsonObject>();
taskObj["message"] = "Task status endpoint - implementation pending";
taskObj["note"] = "Task status will be available in future versions";
String json;
serializeJson(doc, json);
request->send(200, "application/json", json);
}
void ApiServer::onTaskControlRequest(AsyncWebServerRequest *request) {
// Parse the request body for task control commands
if (request->hasParam("task", true) && request->hasParam("action", true)) {
String taskName = request->getParam("task", true)->value();
String action = request->getParam("action", true)->value();
bool success = false;
String message = "";
if (action == "enable") {
taskManager.enableTask(taskName.c_str());
success = true;
message = "Task enabled";
} else if (action == "disable") {
taskManager.disableTask(taskName.c_str());
success = true;
message = "Task disabled";
} else if (action == "start") {
taskManager.startTask(taskName.c_str());
success = true;
message = "Task started";
} else if (action == "stop") {
taskManager.stopTask(taskName.c_str());
success = true;
message = "Task stopped";
} else {
success = false;
message = "Invalid action. Use: enable, disable, start, or stop";
}
JsonDocument doc;
doc["success"] = success;
doc["message"] = message;
doc["task"] = taskName;
doc["action"] = action;
String json;
serializeJson(doc, json);
request->send(success ? 200 : 400, "application/json", json);
} else {
// Missing parameters
JsonDocument doc;
doc["success"] = false;
doc["message"] = "Missing parameters. Required: task, action";
doc["example"] = "{\"task\": \"discovery_send\", \"action\": \"disable\"}";
String json;
serializeJson(doc, json);
request->send(400, "application/json", json);
}
}

View File

@@ -9,13 +9,14 @@
#include "NodeContext.h"
#include "NodeInfo.h"
#include "TaskManager.h"
using namespace std;
using namespace std::placeholders;
class ApiServer {
public:
ApiServer(NodeContext& ctx, uint16_t port = 80);
ApiServer(NodeContext& ctx, TaskManager& taskMgr, uint16_t port = 80);
void begin();
void addEndpoint(const String& uri, int method, std::function<void(AsyncWebServerRequest*)> requestHandler);
void addEndpoint(const String& uri, int method, std::function<void(AsyncWebServerRequest*)> requestHandler,
@@ -23,6 +24,7 @@ public:
private:
AsyncWebServer server;
NodeContext& ctx;
TaskManager& taskManager;
std::vector<std::tuple<String, int>> serviceRegistry;
void onClusterMembersRequest(AsyncWebServerRequest *request);
void methodToStr(const std::tuple<String, int> &endpoint, ArduinoJson::V742PB22::JsonObject &apiObj);
@@ -30,4 +32,8 @@ private:
void onFirmwareUpdateRequest(AsyncWebServerRequest *request);
void onFirmwareUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final);
void onRestartRequest(AsyncWebServerRequest *request);
// Task management endpoints
void onTaskStatusRequest(AsyncWebServerRequest *request);
void onTaskControlRequest(AsyncWebServerRequest *request);
};

162
src/TaskManager.cpp Normal file
View File

@@ -0,0 +1,162 @@
#include "TaskManager.h"
#include <Arduino.h>
#include <TaskScheduler.h>
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, TaskCallback callback, bool enabled, bool autoStart) {
TaskDefinition taskDef(name, interval, callback, enabled, autoStart);
registerTask(taskDef);
}
void TaskManager::registerTask(const TaskDefinition& taskDef) {
taskDefinitions.push_back(taskDef);
}
void TaskManager::initialize() {
// Initialize the scheduler
ctx.scheduler->init();
// Create all registered tasks
for (const auto& taskDef : taskDefinitions) {
createTask(taskDef);
}
// Enable tasks that should auto-start
for (const auto& taskDef : taskDefinitions) {
if (taskDef.autoStart && taskDef.enabled) {
enableTask(taskDef.name);
}
}
}
void TaskManager::createTask(const TaskDefinition& taskDef) {
// Create new task
Task* task = new Task(0, TASK_FOREVER, taskDef.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();
Serial.printf("[TaskManager] Enabled task: %s\n", name.c_str());
} else {
Serial.printf("[TaskManager] Warning: Task not found: %s\n", name.c_str());
}
}
void TaskManager::disableTask(const std::string& name) {
Task* task = findTask(name);
if (task) {
task->disable();
Serial.printf("[TaskManager] Disabled task: %s\n", name.c_str());
} else {
Serial.printf("[TaskManager] Warning: Task not found: %s\n", name.c_str());
}
}
void TaskManager::setTaskInterval(const std::string& name, unsigned long interval) {
Task* task = findTask(name);
if (task) {
task->setInterval(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());
}
}
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());
}
}
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());
}
}
bool TaskManager::isTaskEnabled(const std::string& name) const {
Task* task = findTask(name);
return task ? task->isEnabled() : false;
}
bool TaskManager::isTaskRunning(const std::string& name) const {
Task* task = findTask(name);
return task ? task->isEnabled() : false;
}
unsigned long TaskManager::getTaskInterval(const std::string& name) const {
Task* task = findTask(name);
return task ? task->getInterval() : 0;
}
void TaskManager::enableAllTasks() {
for (auto task : tasks) {
task->enable();
}
Serial.println("[TaskManager] Enabled all tasks");
}
void TaskManager::disableAllTasks() {
for (auto task : tasks) {
task->disable();
}
Serial.println("[TaskManager] Disabled all tasks");
}
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];
Serial.printf(" %s: %s (interval: %lu ms)\n",
taskDef.name.c_str(),
task->isEnabled() ? "ENABLED" : "DISABLED",
task->getInterval());
}
Serial.println("==========================\n");
}
void TaskManager::execute() {
ctx.scheduler->execute();
}
Task* TaskManager::findTask(const std::string& name) const {
for (size_t i = 0; i < tasks.size() && i < taskDefinitions.size(); ++i) {
if (taskDefinitions[i].name == name) {
return tasks[i];
}
}
return nullptr;
}

View File

@@ -1,5 +0,0 @@
/*
* https://github.com/arkhipenko/TaskScheduler/tree/master/examples/Scheduler_example16_Multitab
*/
#include <TaskScheduler.h>

View File

@@ -4,47 +4,45 @@
#include "NetworkManager.h"
#include "ClusterManager.h"
#include "ApiServer.h"
#include "TaskManager.h"
NodeContext ctx;
NetworkManager network(ctx);
ClusterManager cluster(ctx);
ApiServer apiServer(ctx, ctx.config.api_server_port);
TaskManager taskManager(ctx);
ApiServer apiServer(ctx, taskManager, ctx.config.api_server_port);
Task tSendDiscovery(0, TASK_FOREVER, [](){ cluster.sendDiscovery(); });
Task tListenForDiscovery(0, TASK_FOREVER, [](){ cluster.listenForDiscovery(); });
Task tUpdateStatus(0, TASK_FOREVER, [](){ cluster.updateAllNodeStatuses(); cluster.removeDeadNodes(); });
Task tPrintMemberList(0, TASK_FOREVER, [](){ cluster.printMemberList(); });
Task tHeartbeat(0, TASK_FOREVER, [](){ cluster.heartbeatTaskCallback(); });
Task tUpdateAllMembersInfo(0, TASK_FOREVER, [](){ cluster.updateAllMembersInfoTaskCallback(); });
// Task callback wrapper functions
void discoverySendTask() { cluster.sendDiscovery(); }
void discoveryListenTask() { cluster.listenForDiscovery(); }
void statusUpdateTask() { cluster.updateAllNodeStatuses(); cluster.removeDeadNodes(); }
void printMembersTask() { cluster.printMemberList(); }
void heartbeatTask() { cluster.heartbeatTaskCallback(); }
void updateMembersInfoTask() { cluster.updateAllMembersInfoTaskCallback(); }
void setup() {
// Setup WiFi first
network.setupWiFi();
ctx.scheduler->init();
// Set task intervals from config
tSendDiscovery.setInterval(ctx.config.discovery_interval_ms);
tListenForDiscovery.setInterval(ctx.config.discovery_interval_ms / 10); // Listen more frequently
tUpdateStatus.setInterval(ctx.config.status_update_interval_ms);
tPrintMemberList.setInterval(ctx.config.print_interval_ms);
tHeartbeat.setInterval(ctx.config.heartbeat_interval_ms);
tUpdateAllMembersInfo.setInterval(ctx.config.member_info_update_interval_ms);
// Register all system tasks
taskManager.registerTask("discovery_send", ctx.config.discovery_interval_ms, discoverySendTask);
taskManager.registerTask("discovery_listen", ctx.config.discovery_interval_ms / 10, discoveryListenTask);
taskManager.registerTask("status_update", ctx.config.status_update_interval_ms, statusUpdateTask);
taskManager.registerTask("print_members", ctx.config.print_interval_ms, printMembersTask);
taskManager.registerTask("heartbeat", ctx.config.heartbeat_interval_ms, heartbeatTask);
taskManager.registerTask("update_members_info", ctx.config.member_info_update_interval_ms, updateMembersInfoTask);
ctx.scheduler->addTask(tSendDiscovery);
ctx.scheduler->addTask(tListenForDiscovery);
ctx.scheduler->addTask(tUpdateStatus);
ctx.scheduler->addTask(tPrintMemberList);
ctx.scheduler->addTask(tHeartbeat);
ctx.scheduler->addTask(tUpdateAllMembersInfo);
tSendDiscovery.enable();
tListenForDiscovery.enable();
tUpdateStatus.enable();
tPrintMemberList.enable();
tHeartbeat.enable();
tUpdateAllMembersInfo.enable();
// Initialize and start all tasks
taskManager.initialize();
// Start the API server
apiServer.begin();
// Print initial task status
taskManager.printTaskStatus();
}
void loop() {
ctx.scheduler->execute();
taskManager.execute();
yield();
}