feat: improve task handling, refactoring
This commit is contained in:
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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,11 +194,15 @@ 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>();
|
||||
summaryObj["totalTasks"] = taskStatuses.size();
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user