510 lines
14 KiB
Markdown
510 lines
14 KiB
Markdown
# Task Management System
|
|
|
|
The SPORE system includes a comprehensive TaskManager that provides a clean interface for managing system tasks. This makes it easy to add, configure, and control background tasks without cluttering the main application code.
|
|
|
|
## Overview
|
|
|
|
The TaskManager system provides:
|
|
- **Easy Task Registration**: Simple API for adding new tasks with configurable intervals
|
|
- **Dynamic Control**: Enable/disable tasks at runtime
|
|
- **Interval Management**: Change task execution frequency on the fly
|
|
- **Status Monitoring**: View task status and configuration
|
|
- **Automatic Lifecycle**: Tasks are automatically managed and executed
|
|
|
|
## Service Interface Integration
|
|
|
|
Services now implement a unified interface for both endpoint and task registration:
|
|
|
|
```cpp
|
|
class MyService : public Service {
|
|
public:
|
|
void registerEndpoints(ApiServer& api) override {
|
|
// Register HTTP endpoints
|
|
api.registerEndpoint("/api/my/status", HTTP_GET,
|
|
[this](AsyncWebServerRequest* request) { handleStatus(request); });
|
|
}
|
|
|
|
void registerTasks(TaskManager& taskManager) override {
|
|
// Register background tasks
|
|
taskManager.registerTask("my_heartbeat", 2000,
|
|
[this]() { sendHeartbeat(); });
|
|
taskManager.registerTask("my_maintenance", 30000,
|
|
[this]() { performMaintenance(); });
|
|
}
|
|
|
|
const char* getName() const override { return "MyService"; }
|
|
};
|
|
```
|
|
|
|
## Basic Usage
|
|
|
|
```cpp
|
|
#include "TaskManager.h"
|
|
|
|
// Create task manager
|
|
TaskManager taskManager(ctx);
|
|
|
|
// Register tasks
|
|
taskManager.registerTask("heartbeat", 2000, heartbeatFunction);
|
|
taskManager.registerTask("maintenance", 30000, maintenanceFunction);
|
|
|
|
// Initialize and start all tasks
|
|
taskManager.initialize();
|
|
```
|
|
|
|
## Service Lifecycle
|
|
|
|
The Spore framework automatically manages service registration and task lifecycle:
|
|
|
|
### Service Registration Process
|
|
|
|
1. **Service Creation**: Services are created with required dependencies (NodeContext, TaskManager, etc.)
|
|
2. **Service Registration**: Services are registered with the Spore framework via `spore.registerService()`
|
|
3. **Endpoint Registration**: When `spore.begin()` is called, `registerEndpoints()` is called for each service
|
|
4. **Task Registration**: Simultaneously, `registerTasks()` is called for each service
|
|
5. **Task Initialization**: The TaskManager initializes all registered tasks
|
|
6. **Execution**: Tasks run in the main loop when their intervals elapse
|
|
|
|
### Framework Integration
|
|
|
|
```cpp
|
|
void setup() {
|
|
spore.setup();
|
|
|
|
// Create service with dependencies
|
|
MyService* service = new MyService(spore.getContext(), spore.getTaskManager());
|
|
|
|
// Register service (endpoints and tasks will be registered when begin() is called)
|
|
spore.registerService(service);
|
|
|
|
// This triggers registerEndpoints() and registerTasks() for all services
|
|
spore.begin();
|
|
}
|
|
```
|
|
|
|
### Dynamic Service Addition
|
|
|
|
Services can be added after the framework has started:
|
|
|
|
```cpp
|
|
// Add service to running framework
|
|
MyService* newService = new MyService(spore.getContext(), spore.getTaskManager());
|
|
spore.registerService(newService); // Immediately registers endpoints and tasks
|
|
```
|
|
|
|
## Task Registration Methods
|
|
|
|
### Using std::bind with Member Functions (Recommended)
|
|
|
|
```cpp
|
|
#include <functional>
|
|
#include "TaskManager.h"
|
|
|
|
class MyService {
|
|
public:
|
|
void sendHeartbeat() {
|
|
Serial.println("Service heartbeat");
|
|
}
|
|
|
|
void performMaintenance() {
|
|
Serial.println("Running maintenance");
|
|
}
|
|
};
|
|
|
|
MyService service;
|
|
TaskManager taskManager(ctx);
|
|
|
|
// Register member functions using std::bind
|
|
taskManager.registerTask("heartbeat", 2000,
|
|
std::bind(&MyService::sendHeartbeat, &service));
|
|
taskManager.registerTask("maintenance", 30000,
|
|
std::bind(&MyService::performMaintenance, &service));
|
|
|
|
// Initialize and start all tasks
|
|
taskManager.initialize();
|
|
```
|
|
|
|
### Using Lambda Functions
|
|
|
|
```cpp
|
|
// Register lambda functions directly
|
|
taskManager.registerTask("counter", 1000, []() {
|
|
static int count = 0;
|
|
Serial.printf("Count: %d\n", ++count);
|
|
});
|
|
|
|
// Lambda with capture
|
|
int threshold = 100;
|
|
taskManager.registerTask("monitor", 5000, [&threshold]() {
|
|
if (ESP.getFreeHeap() < threshold) {
|
|
Serial.println("Low memory warning!");
|
|
}
|
|
});
|
|
```
|
|
|
|
### Complex Task Registration
|
|
|
|
```cpp
|
|
class NetworkManager {
|
|
public:
|
|
void checkConnection() { /* ... */ }
|
|
void sendData(String data) { /* ... */ }
|
|
};
|
|
|
|
NetworkManager network;
|
|
|
|
// Multiple operations in one task
|
|
taskManager.registerTask("network_ops", 3000,
|
|
std::bind([](NetworkManager* net) {
|
|
net->checkConnection();
|
|
net->sendData("status_update");
|
|
}, &network));
|
|
```
|
|
|
|
## Task Control API
|
|
|
|
### Basic Operations
|
|
|
|
```cpp
|
|
// Enable/disable tasks
|
|
taskManager.enableTask("heartbeat");
|
|
taskManager.disableTask("maintenance");
|
|
|
|
// Change intervals
|
|
taskManager.setTaskInterval("heartbeat", 5000); // 5 seconds
|
|
|
|
// Check status
|
|
bool isRunning = taskManager.isTaskEnabled("heartbeat");
|
|
unsigned long interval = taskManager.getTaskInterval("heartbeat");
|
|
|
|
// Print all task statuses
|
|
taskManager.printTaskStatus();
|
|
```
|
|
|
|
### Task Lifecycle Management
|
|
|
|
```cpp
|
|
// Start/stop tasks
|
|
taskManager.startTask("heartbeat");
|
|
taskManager.stopTask("discovery");
|
|
|
|
// Bulk operations
|
|
taskManager.enableAllTasks();
|
|
taskManager.disableAllTasks();
|
|
```
|
|
|
|
## Task Configuration Options
|
|
|
|
When registering tasks, you can specify:
|
|
|
|
- **Name**: Unique identifier for the task
|
|
- **Interval**: Execution frequency in milliseconds
|
|
- **Callback**: Function, bound method, or lambda to execute
|
|
- **Enabled**: Whether the task starts enabled (default: true)
|
|
- **AutoStart**: Whether to start automatically (default: true)
|
|
|
|
```cpp
|
|
// Traditional function
|
|
taskManager.registerTask("delayed_task", 5000, taskFunction, true, false);
|
|
|
|
// Member function with std::bind
|
|
taskManager.registerTask("service_task", 3000,
|
|
std::bind(&Service::method, &instance), true, false);
|
|
|
|
// Lambda function
|
|
taskManager.registerTask("lambda_task", 2000,
|
|
[]() { Serial.println("Lambda!"); }, true, false);
|
|
```
|
|
|
|
## Adding Custom Tasks
|
|
|
|
### Method 1: Service Interface (Recommended)
|
|
|
|
1. **Create your service class implementing the Service interface**:
|
|
```cpp
|
|
class SensorService : public Service {
|
|
public:
|
|
SensorService(NodeContext& ctx, TaskManager& taskManager)
|
|
: ctx(ctx), taskManager(taskManager) {}
|
|
|
|
void registerEndpoints(ApiServer& api) override {
|
|
api.registerEndpoint("/api/sensor/status", HTTP_GET,
|
|
[this](AsyncWebServerRequest* request) { handleStatus(request); });
|
|
}
|
|
|
|
void registerTasks(TaskManager& taskManager) override {
|
|
taskManager.registerTask("temp_read", 1000,
|
|
[this]() { readTemperature(); });
|
|
taskManager.registerTask("calibrate", 60000,
|
|
[this]() { calibrateSensors(); });
|
|
}
|
|
|
|
const char* getName() const override { return "SensorService"; }
|
|
|
|
private:
|
|
NodeContext& ctx;
|
|
TaskManager& taskManager;
|
|
|
|
void readTemperature() {
|
|
// Read sensor logic
|
|
Serial.println("Reading temperature");
|
|
}
|
|
|
|
void calibrateSensors() {
|
|
// Calibration logic
|
|
Serial.println("Calibrating sensors");
|
|
}
|
|
|
|
void handleStatus(AsyncWebServerRequest* request) {
|
|
// Handle status request
|
|
}
|
|
};
|
|
```
|
|
|
|
2. **Register with Spore framework**:
|
|
```cpp
|
|
void setup() {
|
|
spore.setup();
|
|
|
|
SensorService* sensorService = new SensorService(spore.getContext(), spore.getTaskManager());
|
|
spore.registerService(sensorService);
|
|
|
|
spore.begin(); // This will call registerTasks() automatically
|
|
}
|
|
```
|
|
|
|
### Method 2: Direct TaskManager Registration
|
|
|
|
1. **Create your service class**:
|
|
```cpp
|
|
class SensorService {
|
|
public:
|
|
void readTemperature() {
|
|
// Read sensor logic
|
|
Serial.println("Reading temperature");
|
|
}
|
|
|
|
void calibrateSensors() {
|
|
// Calibration logic
|
|
Serial.println("Calibrating sensors");
|
|
}
|
|
};
|
|
```
|
|
|
|
2. **Register with TaskManager**:
|
|
```cpp
|
|
SensorService sensors;
|
|
|
|
taskManager.registerTask("temp_read", 1000,
|
|
std::bind(&SensorService::readTemperature, &sensors));
|
|
taskManager.registerTask("calibrate", 60000,
|
|
std::bind(&SensorService::calibrateSensors, &sensors));
|
|
```
|
|
|
|
### Method 3: Traditional Functions
|
|
|
|
1. **Define your task function**:
|
|
```cpp
|
|
void myCustomTask() {
|
|
// Your task logic here
|
|
Serial.println("Custom task executed");
|
|
}
|
|
```
|
|
|
|
2. **Register with TaskManager**:
|
|
```cpp
|
|
taskManager.registerTask("my_task", 10000, myCustomTask);
|
|
```
|
|
|
|
## Enhanced TaskManager Capabilities
|
|
|
|
### Task Status Monitoring
|
|
- **Real-time Status**: Check enabled/disabled state and running status
|
|
- **Performance Metrics**: Monitor execution intervals and timing
|
|
- **System Integration**: View task status alongside system resources
|
|
- **Bulk Operations**: Get status of all tasks at once
|
|
|
|
### Task Control Features
|
|
- **Runtime Control**: Enable/disable tasks without restart
|
|
- **Dynamic Intervals**: Change task execution frequency on-the-fly
|
|
- **Individual Status**: Get detailed information about specific tasks
|
|
- **Health Monitoring**: Track task health and system resources
|
|
|
|
## Remote Task Management
|
|
|
|
The TaskManager integrates with the API server to provide comprehensive remote task control and monitoring.
|
|
|
|
### Task Status Overview
|
|
|
|
Get a complete overview of all tasks and system status:
|
|
|
|
```bash
|
|
# Get comprehensive task status
|
|
curl http://192.168.1.100/api/tasks/status
|
|
```
|
|
|
|
**Response includes:**
|
|
- **Summary**: Total task count and active task count
|
|
- **Task Details**: Individual status for each task (name, interval, enabled, running, auto-start)
|
|
- **System Info**: Free heap memory and uptime
|
|
|
|
**Example Response:**
|
|
```json
|
|
{
|
|
"summary": {
|
|
"totalTasks": 6,
|
|
"activeTasks": 5
|
|
},
|
|
"tasks": [
|
|
{
|
|
"name": "discovery_send",
|
|
"interval": 1000,
|
|
"enabled": true,
|
|
"running": true,
|
|
"autoStart": true
|
|
},
|
|
{
|
|
"name": "heartbeat",
|
|
"interval": 2000,
|
|
"enabled": true,
|
|
"running": true,
|
|
"autoStart": true
|
|
}
|
|
],
|
|
"system": {
|
|
"freeHeap": 48748,
|
|
"uptime": 12345
|
|
}
|
|
}
|
|
```
|
|
|
|
### Individual Task Control
|
|
|
|
Control individual tasks with various actions:
|
|
|
|
```bash
|
|
# Control tasks
|
|
curl -X POST http://192.168.1.100/api/tasks/control \
|
|
-d "task=heartbeat&action=disable"
|
|
|
|
# Get detailed status for a specific task
|
|
curl -X POST http://192.168.1.100/api/tasks/control \
|
|
-d "task=discovery_send&action=status"
|
|
```
|
|
|
|
**Available Actions:**
|
|
- `enable` - Enable a task
|
|
- `disable` - Disable a task
|
|
- `start` - Start a task
|
|
- `stop` - Stop a task
|
|
- `status` - Get detailed status for a specific task
|
|
|
|
**Task Status Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"message": "Task status retrieved",
|
|
"task": "discovery_send",
|
|
"action": "status",
|
|
"taskDetails": {
|
|
"name": "discovery_send",
|
|
"enabled": true,
|
|
"running": true,
|
|
"interval": 1000,
|
|
"system": {
|
|
"freeHeap": 48748,
|
|
"uptime": 12345
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Performance Considerations
|
|
|
|
- `std::bind` creates a callable object that may have a small overhead compared to direct function pointers
|
|
- For high-frequency tasks, consider the performance impact
|
|
- The overhead is typically negligible for most embedded applications
|
|
- The TaskManager stores bound functions efficiently in a registry
|
|
|
|
## Best Practices
|
|
|
|
1. **Use Service Interface**: Implement the Service interface for clean integration with the framework
|
|
2. **Group related tasks**: Register multiple related operations in a single service
|
|
3. **Monitor task health**: Use the status API to monitor task performance
|
|
4. **Plan intervals carefully**: Balance responsiveness with system resources
|
|
5. **Use descriptive names**: Make task names clear and meaningful
|
|
6. **Separate concerns**: Use registerEndpoints() for HTTP API and registerTasks() for background work
|
|
7. **Dependency injection**: Pass required dependencies (NodeContext, TaskManager) to service constructors
|
|
|
|
## Migration to Service Interface
|
|
|
|
### Before (manual task registration in constructor):
|
|
```cpp
|
|
class MyService : public Service {
|
|
public:
|
|
MyService(TaskManager& taskManager) : taskManager(taskManager) {
|
|
// Tasks registered in constructor
|
|
taskManager.registerTask("heartbeat", 2000, [this]() { sendHeartbeat(); });
|
|
}
|
|
|
|
void registerEndpoints(ApiServer& api) override {
|
|
// Only endpoints registered here
|
|
}
|
|
};
|
|
```
|
|
|
|
### After (using Service interface):
|
|
```cpp
|
|
class MyService : public Service {
|
|
public:
|
|
MyService(TaskManager& taskManager) : taskManager(taskManager) {
|
|
// No task registration in constructor
|
|
}
|
|
|
|
void registerEndpoints(ApiServer& api) override {
|
|
// Register HTTP endpoints
|
|
api.registerEndpoint("/api/my/status", HTTP_GET,
|
|
[this](AsyncWebServerRequest* request) { handleStatus(request); });
|
|
}
|
|
|
|
void registerTasks(TaskManager& taskManager) override {
|
|
// Register background tasks
|
|
taskManager.registerTask("heartbeat", 2000, [this]() { sendHeartbeat(); });
|
|
}
|
|
|
|
const char* getName() const override { return "MyService"; }
|
|
};
|
|
```
|
|
|
|
### Migration from Wrapper Functions
|
|
|
|
### Before (with wrapper functions):
|
|
```cpp
|
|
void discoverySendTask() { cluster.sendDiscovery(); }
|
|
void clusterListenTask() { cluster.listen(); }
|
|
|
|
taskManager.registerTask("discovery_send", interval, discoverySendTask);
|
|
taskManager.registerTask("cluster_listen", interval, clusterListenTask);
|
|
```
|
|
|
|
### After (with std::bind):
|
|
```cpp
|
|
taskManager.registerTask("discovery_send", interval,
|
|
std::bind(&ClusterManager::sendDiscovery, &cluster));
|
|
taskManager.registerTask("cluster_listen", interval,
|
|
std::bind(&ClusterManager::listen, &cluster));
|
|
```
|
|
|
|
## Compatibility
|
|
|
|
- The new Service interface is fully backward compatible
|
|
- Existing code using direct TaskManager registration will continue to work
|
|
- You can mix Service interface and direct registration in the same project
|
|
- All existing TaskManager methods remain unchanged
|
|
- The Service interface provides a cleaner, more organized approach for framework integration
|
|
|
|
## Related Documentation
|
|
|
|
- **[TaskManager API Reference](./TaskManager.md)** - Detailed API documentation
|
|
- **[API Reference](./API.md)** - REST API for remote task management
|
|
- **[OpenAPI Specification](../api/)** - Machine-readable API specification |