# 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 #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