Files
spore/docs/TaskManagement.md

9.3 KiB

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

Basic Usage

#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();

Task Registration Methods

#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

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

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

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

// 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)
// 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

  1. Create your service class:

    class SensorService {
    public:
        void readTemperature() {
            // Read sensor logic
            Serial.println("Reading temperature");
        }
    
        void calibrateSensors() {
            // Calibration logic
            Serial.println("Calibrating sensors");
        }
    };
    
  2. Register with TaskManager:

    SensorService sensors;
    
    taskManager.registerTask("temp_read", 1000, 
                           std::bind(&SensorService::readTemperature, &sensors));
    taskManager.registerTask("calibrate", 60000, 
                           std::bind(&SensorService::calibrateSensors, &sensors));
    

Method 2: Traditional Functions

  1. Define your task function:

    void myCustomTask() {
        // Your task logic here
        Serial.println("Custom task executed");
    }
    
  2. Register with TaskManager:

    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:

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

{
  "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:

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

{
  "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 std::bind for member functions: Cleaner than wrapper functions
  2. Group related tasks: Register multiple related operations in a single task
  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

Migration from Wrapper Functions

Before (with wrapper functions):

void discoverySendTask() { cluster.sendDiscovery(); }
void discoveryListenTask() { cluster.listenForDiscovery(); }

taskManager.registerTask("discovery_send", interval, discoverySendTask);
taskManager.registerTask("discovery_listen", interval, discoveryListenTask);

After (with std::bind):

taskManager.registerTask("discovery_send", interval, 
                       std::bind(&ClusterManager::sendDiscovery, &cluster));
taskManager.registerTask("discovery_listen", interval, 
                       std::bind(&ClusterManager::listenForDiscovery, &cluster));

Compatibility

  • The new std::bind support is fully backward compatible
  • Existing code using function pointers will continue to work
  • You can mix both approaches in the same project
  • All existing TaskManager methods remain unchanged
  • New status monitoring methods are additive and don't break existing functionality