From 30a5f8b8cb6eb447261422e647673b0d06c82366 Mon Sep 17 00:00:00 2001 From: Patrick Balsiger Date: Fri, 22 Aug 2025 15:47:08 +0200 Subject: [PATCH] feat: task manager endpoint, updated documentation --- README.md | 444 ++++++--------------------- api/README.md | 201 ++++++++++++ api/openapi.yaml | 673 +++++++++++++++++++++++++++++++++++++++++ docs/API.md | 279 +++++++++++++++++ docs/Architecture.md | 358 ++++++++++++++++++++++ docs/Development.md | 437 ++++++++++++++++++++++++++ docs/README.md | 85 ++++++ docs/TaskManagement.md | 348 +++++++++++++++++++++ docs/TaskManager.md | 180 ----------- include/TaskManager.h | 4 + src/ApiServer.cpp | 65 +++- src/ClusterManager.cpp | 2 + src/TaskManager.cpp | 20 ++ src/main.cpp | 5 +- 14 files changed, 2550 insertions(+), 551 deletions(-) create mode 100644 api/README.md create mode 100644 api/openapi.yaml create mode 100644 docs/API.md create mode 100644 docs/Architecture.md create mode 100644 docs/Development.md create mode 100644 docs/README.md create mode 100644 docs/TaskManagement.md delete mode 100644 docs/TaskManager.md diff --git a/README.md b/README.md index 6a5b94c..0db78d0 100644 --- a/README.md +++ b/README.md @@ -16,420 +16,136 @@ SPORE is a cluster engine for ESP8266 microcontrollers that provides automatic n ## Supported Hardware -- **ESP-01** (1MB Flash) -- **ESP-01S** (1MB Flash) +- **ESP-01 / ESP-01S** (1MB Flash) +- **Wemos D1** (4MB Flash) - Other ESP8266 boards with 1MB+ flash ## Architecture -### Core Components - -The system architecture consists of several key components working together: +SPORE uses a modular architecture with automatic node discovery, health monitoring, and distributed task management. +**Core Components:** - **Network Manager**: WiFi connection handling and hostname configuration - **Cluster Manager**: Node discovery, member list management, and health monitoring - **API Server**: HTTP API server with dynamic endpoint registration - **Task Scheduler**: Cooperative multitasking system for background operations - **Node Context**: Central context providing event system and shared resources -### Auto Discovery Protocol +**Key Features:** +- **Auto Discovery**: UDP-based node detection on port 4210 +- **Health Monitoring**: Continuous status checking via HTTP API +- **Task Scheduling**: Background tasks at configurable intervals +- **Event System**: Local and cluster-wide event publishing/subscription -The cluster uses a UDP-based discovery protocol for automatic node detection: - -1. **Discovery Broadcast**: Nodes periodically send UDP packets on port 4210 -2. **Response Handling**: Nodes respond with their hostname and IP address -3. **Member Management**: Discovered nodes are automatically added to the cluster -4. **Health Monitoring**: Continuous status checking via HTTP API calls - -### Task Scheduling - -The system runs several background tasks at different intervals: - -- **Discovery Tasks**: Send/listen for discovery packets (1s/100ms) -- **Status Updates**: Monitor cluster member health (1s) -- **Heartbeat**: Maintain cluster connectivity (2s) -- **Member Info**: Update detailed node information (10s) -- **Debug Output**: Print cluster status (5s) +📖 **Detailed Architecture:** See [`docs/Architecture.md`](./docs/Architecture.md) for comprehensive system design and implementation details. ## API Endpoints -### Node Management +The system provides a comprehensive RESTful API for monitoring and controlling the embedded device. -| Endpoint | Method | Description | -|----------|--------|-------------| -| `/api/node/status` | GET | Get system resources and API endpoints | -| `/api/node/update` | POST | Upload and install firmware update | -| `/api/node/restart` | POST | Restart the node | +**Core Endpoints:** +- **`/api/node/status`** - System resources and API endpoint registry +- **`/api/cluster/members`** - Cluster membership and health status +- **`/api/node/update`** - OTA firmware updates +- **`/api/node/restart`** - System restart +- **`/api/tasks/status`** - Task management and monitoring +- **`/api/tasks/control`** - Task control operations -### Cluster Management +**Response Format:** All endpoints return JSON with standardized error handling and HTTP status codes. -| Endpoint | Method | Description | -|----------|--------|-------------| -| `/api/cluster/members` | GET | Get cluster membership and status | - -### Node Status Response - -```json -{ - "freeHeap": 12345, - "chipId": 12345678, - "sdkVersion": "2.2.2-dev(38a443e)", - "cpuFreqMHz": 80, - "flashChipSize": 1048576, - "api": [ - { - "uri": "/api/node/status", - "method": "GET" - } - ] -} -``` - -### Cluster Members Response - -```json -{ - "members": [ - { - "hostname": "esp_123456", - "ip": "192.168.1.100", - "lastSeen": 1234567890, - "latency": 5, - "status": "active", - "resources": { - "freeHeap": 12345, - "chipId": 12345678, - "sdkVersion": "2.2.2-dev(38a443e)", - "cpuFreqMHz": 80, - "flashChipSize": 1048576 - }, - "api": [ - { - "uri": "/api/node/status", - "method": "GET" - } - ] - } - ] -} -``` +📖 **Complete API Reference:** See [`docs/API.md`](./docs/API.md) for detailed endpoint documentation, examples, and integration guides. ## Configuration -### Environment Setup - -Create a `.env` file in your project root: - -```bash -# API node IP for cluster management -export API_NODE=192.168.1.100 -``` - -### PlatformIO Configuration - -The project uses PlatformIO with the following configuration: +The project uses PlatformIO with Arduino framework and supports multiple ESP8266 boards. +**Key Settings:** - **Framework**: Arduino -- **Board**: ESP-01 with 1MB flash +- **Board**: ESP-01 with 1MB flash (default) - **Upload Speed**: 115200 baud - **Flash Mode**: DOUT (required for ESP-01S) -### Dependencies +**Dependencies:** +- ESPAsyncWebServer, ArduinoJson, TaskScheduler +- ESP8266WiFi and HTTPClient libraries -The project requires the following libraries: -- `esp32async/ESPAsyncWebServer@^3.8.0` - HTTP API server -- `bblanchon/ArduinoJson@^7.4.2` - JSON processing -- `arkhipenko/TaskScheduler@^3.8.5` - Cooperative multitasking +**Environment Setup:** Create `.env` file for cluster configuration and API node settings. + +📖 **Development Guide:** See [`docs/Development.md`](./docs/Development.md) for comprehensive build, deployment, and configuration instructions. ## Development -### Prerequisites - -- PlatformIO Core or PlatformIO IDE -- ESP8266 development tools -- `jq` for JSON processing in scripts - -### Building - -Build the firmware for specific chip: - +**Quick Commands:** ```bash +# Build firmware ./ctl.sh build target esp01_1m -``` -### Flashing - -Flash firmware to a connected device: - -```bash +# Flash device ./ctl.sh flash target esp01_1m -``` -### Over-The-Air Updates - -Update a specific node: - -```bash +# OTA update ./ctl.sh ota update 192.168.1.100 esp01_1m -``` -Update all nodes in the cluster: - -```bash -./ctl.sh ota all esp01_1m -``` - -### Cluster Management - -View cluster members: - -```bash +# Cluster management ./ctl.sh cluster members ``` +**Prerequisites:** PlatformIO Core, ESP8266 tools, `jq` for JSON processing + +📖 **Complete Development Guide:** See [`docs/Development.md`](./docs/Development.md) for detailed build, deployment, and troubleshooting instructions. + +## API Reference + +The SPORE system provides a comprehensive RESTful API for monitoring and controlling the embedded device. All endpoints return JSON responses and support standard HTTP status codes. + +### Quick Reference + +| Endpoint | Method | Description | +|----------|--------|-------------| +| `/api/tasks/status` | GET | Get comprehensive task status | +| `/api/tasks/control` | POST | Control individual tasks | +| `/api/node/status` | GET | System resource information | +| `/api/cluster/members` | GET | Cluster membership | +| `/api/node/update` | POST | OTA firmware updates | +| `/api/node/restart` | POST | System restart | + +**Available Task Actions:** `enable`, `disable`, `start`, `stop`, `status` + +📖 **Detailed API Documentation:** See [`docs/API.md`](./docs/API.md) for complete API reference, examples, and integration guides. + +🔧 **OpenAPI Specification:** Machine-readable API spec available in [`api/`](./api/) folder for code generation and tooling integration. + ## Implementation Details -### Event System +The system uses an event-driven architecture with automatic resource monitoring and WiFi fallback capabilities. -The `NodeContext` provides an event-driven architecture: +**Key Systems:** +- **Event System**: Local and cluster-wide event publishing/subscription +- **Status Tracking**: Automatic node categorization (ACTIVE, INACTIVE, DEAD) +- **Resource Monitoring**: Memory, CPU, flash, and API endpoint tracking +- **WiFi Fallback**: Automatic access point creation if connection fails -```cpp -// Subscribe to events -ctx.on("node_discovered", [](void* data) { - NodeInfo* node = static_cast(data); - // Handle new node discovery -}); - -// Publish events -ctx.fire("node_discovered", &newNode); -``` - -### Node Status Tracking - -Nodes are automatically categorized by their activity: - -- **ACTIVE**: Responding within 10 seconds -- **INACTIVE**: No response for 10-60 seconds -- **DEAD**: No response for over 60 seconds - -### Resource Monitoring - -Each node tracks: -- Free heap memory -- Chip ID and SDK version -- CPU frequency -- Flash chip size -- API endpoint registry - -### WiFi Fallback - -The system includes automatic WiFi fallback: -1. Attempts to connect to configured WiFi network -2. If connection fails, creates an access point -3. Hostname is automatically generated from MAC address +📖 **Architecture Details:** See [`docs/Architecture.md`](./docs/Architecture.md) for comprehensive system design and implementation information. ## Task Management -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. +The SPORE system includes a comprehensive TaskManager for background task management with runtime control and monitoring. -### TaskManager Features +**Key Features:** +- **Easy Registration**: Simple API for adding tasks with configurable intervals +- **Dynamic Control**: Enable/disable tasks at runtime without restart +- **Status Monitoring**: Real-time task health and performance tracking +- **Remote Management**: REST API for cluster-wide task control -- **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 - -```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(); -``` - -#### Using std::bind with Member Functions - -```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 - -```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(); -``` - -### Remote Task Management - -The TaskManager integrates with the API server to provide remote task control: - -```bash -# Get task status -curl http://192.168.1.100/api/tasks/status - -# Control tasks -curl -X POST http://192.168.1.100/api/tasks/control \ - -d "task=heartbeat&action=disable" - -# Available actions: enable, disable, start, stop -``` - -### Adding Custom Tasks - -#### Method 1: Using std::bind (Recommended) - -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 2: 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); - ``` - -### 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); -``` +📖 **Complete Task Management Guide:** See [`docs/TaskManagement.md`](./docs/TaskManagement.md) for detailed usage examples, best practices, and advanced features. ## Current Limitations - WiFi credentials are hardcoded in `Config.cpp` (should be configurable) - Limited error handling for network failures - No persistent storage for configuration -- Basic health monitoring without advanced metrics +- Task monitoring and system health metrics +- Task execution history and performance analytics not yet implemented ## Troubleshooting @@ -448,6 +164,16 @@ Enable serial monitoring to see cluster activity: pio device monitor ``` +📖 **For detailed task management examples and workflows, see [`docs/API.md`](./docs/API.md).** + +## Documentation + +📚 **Comprehensive documentation is available in the [`docs/`](./docs/) folder:** + +- **[API Reference](./docs/API.md)** - Complete API documentation with examples +- **[TaskManager Guide](./docs/TaskManager.md)** - Background task management system +- **[OpenAPI Spec](./api/)** - Machine-readable API specification + ## Contributing 1. Fork the repository diff --git a/api/README.md b/api/README.md new file mode 100644 index 0000000..b77c03e --- /dev/null +++ b/api/README.md @@ -0,0 +1,201 @@ +# SPORE API Documentation + +This folder contains the OpenAPI specification for the SPORE embedded system API. + +## Files + +- **`openapi.yaml`** - OpenAPI 3.0 specification file +- **`README.md`** - This documentation file + +## OpenAPI Specification + +The `openapi.yaml` file provides a complete, machine-readable specification of the SPORE API. It can be used with various tools to: + +- Generate client libraries in multiple programming languages +- Create interactive API documentation +- Validate API requests and responses +- Generate mock servers for testing +- Integrate with API management platforms + +## Using the OpenAPI Specification + +### 1. View Interactive Documentation + +#### Option A: Swagger UI Online +1. Go to [Swagger Editor](https://editor.swagger.io/) +2. Copy and paste the contents of `openapi.yaml` +3. View the interactive documentation + +#### Option B: Local Swagger UI +```bash +# Install Swagger UI locally +npm install -g swagger-ui-express + +# Or use Docker +docker run -p 8080:8080 -e SWAGGER_JSON=/openapi.yaml -v $(pwd):/swagger swaggerapi/swagger-ui +``` + +### 2. Generate Client Libraries + +#### Using OpenAPI Generator +```bash +# Install OpenAPI Generator +npm install @openapitools/openapi-generator-cli -g + +# Generate Python client +openapi-generator generate -i openapi.yaml -g python -o python-client + +# Generate JavaScript client +openapi-generator generate -i openapi.yaml -g javascript -o js-client + +# Generate C# client +openapi-generator generate -i openapi.yaml -g csharp -o csharp-client +``` + +#### Using Swagger Codegen +```bash +# Install Swagger Codegen +npm install -g swagger-codegen + +# Generate Python client +swagger-codegen generate -i openapi.yaml -l python -o python-client + +# Generate Java client +swagger-codegen generate -i openapi.yaml -l java -o java-client +``` + +### 3. Validate API Responses + +#### Using OpenAPI Validator +```bash +# Install OpenAPI validator +npm install -g @apidevtools/swagger-parser + +# Validate the specification +swagger-parser validate openapi.yaml +``` + +### 4. Generate Mock Server + +#### Using Prism (Stoplight) +```bash +# Install Prism +npm install -g @stoplight/prism-cli + +# Start mock server +prism mock openapi.yaml + +# The mock server will be available at http://localhost:4010 +``` + +## API Endpoints Overview + +The SPORE API provides the following main categories of endpoints: + +### Task Management +- **`GET /api/tasks/status`** - Get comprehensive task status +- **`POST /api/tasks/control`** - Control individual tasks + +### System Status +- **`GET /api/node/status`** - Get system resource information +- **`GET /api/cluster/members`** - Get cluster membership + +### System Management +- **`POST /api/node/update`** - Handle OTA firmware updates +- **`POST /api/node/restart`** - Trigger system restart + +## Data Models + +The OpenAPI specification includes comprehensive schemas for: + +- **Task Information**: Task status, configuration, and execution details +- **System Resources**: Memory usage, chip information, and performance metrics +- **Cluster Management**: Node health, network topology, and resource sharing +- **API Responses**: Standardized response formats and error handling + +## Examples + +### Basic Task Status Check +```bash +curl -s http://192.168.1.100/api/tasks/status | jq '.' +``` + +### Task Control +```bash +# Disable a task +curl -X POST http://192.168.1.100/api/tasks/control \ + -d "task=heartbeat&action=disable" + +# Get detailed status +curl -X POST http://192.168.1.100/api/tasks/control \ + -d "task=discovery_send&action=status" +``` + +### System Monitoring +```bash +# Check system resources +curl -s http://192.168.1.100/api/node/status | jq '.freeHeap' + +# Monitor cluster health +curl -s http://192.168.1.100/api/cluster/members | jq '.members[].status' +``` + +## Integration Examples + +### Python Client +```python +import requests + +# Get task status +response = requests.get('http://192.168.1.100/api/tasks/status') +tasks = response.json() + +# Check active tasks +active_count = tasks['summary']['activeTasks'] +print(f"Active tasks: {active_count}") + +# Control a task +control_data = {'task': 'heartbeat', 'action': 'disable'} +response = requests.post('http://192.168.1.100/api/tasks/control', data=control_data) +``` + +### JavaScript Client +```javascript +// Get task status +fetch('http://192.168.1.100/api/tasks/status') + .then(response => response.json()) + .then(data => { + console.log(`Total tasks: ${data.summary.totalTasks}`); + console.log(`Active tasks: ${data.summary.activeTasks}`); + }); + +// Control a task +fetch('http://192.168.1.100/api/tasks/control', { + method: 'POST', + headers: {'Content-Type': 'application/x-www-form-urlencoded'}, + body: 'task=heartbeat&action=disable' +}); +``` + +## Contributing + +When adding new API endpoints or modifying existing ones: + +1. Update the `openapi.yaml` file +2. Add appropriate schemas for new data models +3. Include example responses +4. Update this README if needed +5. Test the specification with validation tools + +## Tools and Resources + +- [OpenAPI Specification](https://swagger.io/specification/) +- [OpenAPI Generator](https://openapi-generator.tech/) +- [Swagger Codegen](https://github.com/swagger-api/swagger-codegen) +- [Swagger UI](https://swagger.io/tools/swagger-ui/) +- [Prism Mock Server](https://stoplight.io/open-source/prism/) +- [OpenAPI Validator](https://apitools.dev/swagger-parser/) + +## License + +This API specification is part of the SPORE project and follows the same license terms. \ No newline at end of file diff --git a/api/openapi.yaml b/api/openapi.yaml new file mode 100644 index 0000000..09f5c89 --- /dev/null +++ b/api/openapi.yaml @@ -0,0 +1,673 @@ +openapi: 3.0.3 +info: + title: SPORE Embedded System API + description: | + RESTful API for monitoring and controlling the SPORE embedded system. + + The SPORE system provides comprehensive task management, system monitoring, + and cluster management capabilities for ESP8266-based embedded devices. + + ## Features + - **Task Management**: Monitor and control background tasks + - **System Monitoring**: Real-time system resource tracking + - **Cluster Management**: Multi-node cluster coordination + - **OTA Updates**: Over-the-air firmware updates + - **Health Monitoring**: System and task health metrics + + ## Authentication + Currently, no authentication is required. All endpoints are accessible + from the local network. + + ## Rate Limiting + No rate limiting is currently implemented. Use responsibly. + + ## Base URL + The API is accessible at `http://{device-ip}/` where `{device-ip}` is + the IP address of your SPORE device on the local network. + + version: 1.0.0 + contact: + name: SPORE Development Team + url: https://git.dcentral.systems/iot/spore + license: + name: MIT + url: https://opensource.org/licenses/MIT + +servers: + - url: http://{device-ip} + description: Local SPORE device + variables: + device-ip: + description: IP address of your SPORE device + default: "10.0.1.60" + +paths: + /api/tasks/status: + get: + summary: Get comprehensive task status + description: | + Returns detailed status information for all registered tasks, + including system resource metrics and task execution details. + + This endpoint provides a complete overview of the system's + task management status and can be used for monitoring and + debugging purposes. + + tags: + - Task Management + + responses: + '200': + description: Task status retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/TaskStatusResponse' + examples: + default: + summary: Default response + value: + 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 + - name: "status_update" + interval: 1000 + enabled: true + running: true + autoStart: true + system: + freeHeap: 48748 + uptime: 12345 + + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /api/tasks/control: + post: + summary: Control individual task operations + description: | + Controls the execution state of individual tasks. Supports + enabling, disabling, starting, stopping, and getting detailed + status for specific tasks. + + This endpoint is useful for dynamic task management without + requiring system restarts. + + tags: + - Task Management + + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + type: object + required: + - task + - action + properties: + task: + type: string + description: Name of the task to control + example: "heartbeat" + action: + type: string + description: Action to perform on the task + enum: [enable, disable, start, stop, status] + example: "disable" + + responses: + '200': + description: Task operation completed successfully + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/TaskControlResponse' + - $ref: '#/components/schemas/TaskStatusDetailResponse' + examples: + enable: + summary: Task enabled + value: + success: true + message: "Task enabled" + task: "heartbeat" + action: "enable" + status: + summary: Task status retrieved + value: + 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 + + '400': + description: Bad request - invalid parameters or action + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + missing_params: + summary: Missing required parameters + value: + success: false + message: "Missing parameters. Required: task, action" + example: "{\"task\": \"discovery_send\", \"action\": \"status\"}" + invalid_action: + summary: Invalid action specified + value: + success: false + message: "Invalid action. Use: enable, disable, start, stop, or status" + task: "heartbeat" + action: "invalid_action" + + /api/node/status: + get: + summary: Get system status and API information + description: | + Returns comprehensive system resource information including + memory usage, chip details, and a registry of all available + API endpoints. + + tags: + - System Status + + responses: + '200': + description: System status retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/SystemStatusResponse' + examples: + default: + summary: Default response + value: + freeHeap: 48748 + chipId: 12345678 + sdkVersion: "3.1.2" + cpuFreqMHz: 80 + flashChipSize: 1048576 + api: + - uri: "/api/node/status" + method: 1 + - uri: "/api/tasks/status" + method: 1 + - uri: "/api/tasks/control" + method: 3 + + /api/cluster/members: + get: + summary: Get cluster membership information + description: | + Returns information about all nodes in the cluster, + including their health status, resources, and API endpoints. + + tags: + - Cluster Management + + responses: + '200': + description: Cluster information retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ClusterMembersResponse' + examples: + default: + summary: Default response + value: + members: + - hostname: "spore-node-1" + ip: "192.168.1.100" + lastSeen: 1234567890 + latency: 5 + status: "ACTIVE" + resources: + freeHeap: 48748 + chipId: 12345678 + sdkVersion: "3.1.2" + cpuFreqMHz: 80 + flashChipSize: 1048576 + api: + - uri: "/api/node/status" + method: "GET" + - uri: "/api/tasks/status" + method: "GET" + + /api/node/update: + post: + summary: Handle firmware update via OTA + description: | + Initiates an over-the-air firmware update. The firmware + file should be uploaded as multipart/form-data. + + tags: + - System Management + + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + properties: + firmware: + type: string + format: binary + description: Firmware binary file (.bin) + + responses: + '200': + description: Firmware update initiated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateResponse' + examples: + default: + summary: Update initiated + value: + status: "updating" + message: "Firmware update in progress" + + '400': + description: Invalid firmware file or update failed + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /api/node/restart: + post: + summary: Restart the system + description: | + Triggers a system restart. The response will be sent + before the restart occurs. + + tags: + - System Management + + responses: + '200': + description: System restart initiated + content: + application/json: + schema: + $ref: '#/components/schemas/RestartResponse' + examples: + default: + summary: Restart initiated + value: + status: "restarting" + +components: + schemas: + TaskStatusResponse: + type: object + required: + - summary + - tasks + - system + properties: + summary: + $ref: '#/components/schemas/TaskSummary' + tasks: + type: array + items: + $ref: '#/components/schemas/TaskInfo' + system: + $ref: '#/components/schemas/SystemInfo' + + TaskSummary: + type: object + required: + - totalTasks + - activeTasks + properties: + totalTasks: + type: integer + minimum: 0 + description: Total number of registered tasks + example: 6 + activeTasks: + type: integer + minimum: 0 + description: Number of currently enabled tasks + example: 5 + + TaskInfo: + type: object + required: + - name + - interval + - enabled + - running + - autoStart + properties: + name: + type: string + description: Unique task identifier + example: "discovery_send" + interval: + type: integer + minimum: 1 + description: Execution frequency in milliseconds + example: 1000 + enabled: + type: boolean + description: Whether task is currently enabled + example: true + running: + type: boolean + description: Whether task is actively executing + example: true + autoStart: + type: boolean + description: Whether task starts automatically + example: true + + SystemInfo: + type: object + required: + - freeHeap + - uptime + properties: + freeHeap: + type: integer + minimum: 0 + description: Available RAM in bytes + example: 48748 + uptime: + type: integer + minimum: 0 + description: System uptime in milliseconds + example: 12345 + + TaskControlResponse: + type: object + required: + - success + - message + - task + - action + properties: + success: + type: boolean + description: Whether the operation was successful + example: true + message: + type: string + description: Human-readable operation result + example: "Task enabled" + task: + type: string + description: Name of the task that was operated on + example: "heartbeat" + action: + type: string + description: Action that was performed + example: "enable" + + TaskStatusDetailResponse: + allOf: + - $ref: '#/components/schemas/TaskControlResponse' + - type: object + required: + - taskDetails + properties: + taskDetails: + $ref: '#/components/schemas/TaskDetailInfo' + + TaskDetailInfo: + type: object + required: + - name + - enabled + - running + - interval + - system + properties: + name: + type: string + description: Task name + example: "discovery_send" + enabled: + type: boolean + description: Whether task is enabled + example: true + running: + type: boolean + description: Whether task is running + example: true + interval: + type: integer + description: Task execution interval + example: 1000 + system: + $ref: '#/components/schemas/SystemInfo' + + SystemStatusResponse: + type: object + required: + - freeHeap + - chipId + - sdkVersion + - cpuFreqMHz + - flashChipSize + - api + properties: + freeHeap: + type: integer + description: Available RAM in bytes + example: 48748 + chipId: + type: integer + description: ESP8266 chip ID + example: 12345678 + sdkVersion: + type: string + description: ESP8266 SDK version + example: "3.1.2" + cpuFreqMHz: + type: integer + description: CPU frequency in MHz + example: 80 + flashChipSize: + type: integer + description: Flash chip size in bytes + example: 1048576 + api: + type: array + items: + $ref: '#/components/schemas/ApiEndpoint' + + ApiEndpoint: + type: object + required: + - uri + - method + properties: + uri: + type: string + description: API endpoint URI + example: "/api/node/status" + method: + type: integer + description: HTTP method (1=GET, 3=POST) + example: 1 + + ClusterMembersResponse: + type: object + required: + - members + properties: + members: + type: array + items: + $ref: '#/components/schemas/ClusterNode' + + ClusterNode: + type: object + required: + - hostname + - ip + - lastSeen + - latency + - status + - resources + - api + properties: + hostname: + type: string + description: Node hostname + example: "spore-node-1" + ip: + type: string + format: ipv4 + description: Node IP address + example: "192.168.1.100" + lastSeen: + type: integer + description: Timestamp of last communication + example: 1234567890 + latency: + type: integer + description: Network latency in milliseconds + example: 5 + status: + type: string + enum: [ACTIVE, INACTIVE, DEAD] + description: Node health status + example: "ACTIVE" + resources: + $ref: '#/components/schemas/SystemResources' + api: + type: array + items: + $ref: '#/components/schemas/ApiEndpoint' + + SystemResources: + type: object + required: + - freeHeap + - chipId + - sdkVersion + - cpuFreqMHz + - flashChipSize + properties: + freeHeap: + type: integer + description: Available RAM in bytes + example: 48748 + chipId: + type: integer + description: ESP8266 chip ID + example: 12345678 + sdkVersion: + type: string + description: ESP8266 SDK version + example: "3.1.2" + cpuFreqMHz: + type: integer + description: CPU frequency in MHz + example: 80 + flashChipSize: + type: integer + description: Flash chip size in bytes + example: 1048576 + + UpdateResponse: + type: object + required: + - status + properties: + status: + type: string + enum: [updating, failed] + description: Update operation status + example: "updating" + message: + type: string + description: Additional status information + example: "Firmware update in progress" + + RestartResponse: + type: object + required: + - status + properties: + status: + type: string + enum: [restarting] + description: Restart operation status + example: "restarting" + + ErrorResponse: + type: object + required: + - success + - message + properties: + success: + type: boolean + description: Always false for error responses + example: false + message: + type: string + description: Error description + example: "Missing parameters. Required: task, action" + example: + type: string + description: Example of correct usage + example: "{\"task\": \"discovery_send\", \"action\": \"status\"}" + task: + type: string + description: Task name (if applicable) + example: "heartbeat" + action: + type: string + description: Action attempted (if applicable) + example: "invalid_action" + + securitySchemes: + # Currently no authentication required + # This section can be expanded when authentication is implemented + +tags: + - name: Task Management + description: Operations for monitoring and controlling background tasks + - name: System Status + description: System resource monitoring and API information + - name: Cluster Management + description: Multi-node cluster coordination and health monitoring + - name: System Management + description: System-level operations like updates and restarts + +externalDocs: + description: SPORE Project Documentation + url: https://github.com/your-org/spore \ No newline at end of file diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 0000000..28fa33c --- /dev/null +++ b/docs/API.md @@ -0,0 +1,279 @@ +# SPORE API Documentation + +The SPORE system provides a comprehensive RESTful API for monitoring and controlling the embedded device. All endpoints return JSON responses and support standard HTTP status codes. + +## Quick Reference + +### Task Management API + +| Endpoint | Method | Description | Parameters | Response | +|----------|--------|-------------|------------|----------| +| `/api/tasks/status` | GET | Get comprehensive status of all tasks and system information | None | Task status overview with system metrics | +| `/api/tasks/control` | POST | Control individual task operations | `task`, `action` | Operation result with task details | + +### System Status API + +| Endpoint | Method | Description | Response | +|----------|--------|-------------|----------| +| `/api/node/status` | GET | System resource information and API endpoint registry | System metrics and API catalog | +| `/api/cluster/members` | GET | Cluster membership and node health information | Cluster topology and health status | +| `/api/node/update` | POST | Handle firmware updates via OTA | Update progress and status | +| `/api/node/restart` | POST | Trigger system restart | Restart confirmation | + +## Detailed API Reference + +### Task Management + +#### GET /api/tasks/status + +Returns comprehensive status information for all registered tasks, including system resource metrics and task execution details. + +**Response Fields:** + +| Field | Type | Description | +|-------|------|-------------| +| `summary.totalTasks` | integer | Total number of registered tasks | +| `summary.activeTasks` | integer | Number of currently enabled tasks | +| `tasks[].name` | string | Unique task identifier | +| `tasks[].interval` | integer | Execution frequency in milliseconds | +| `tasks[].enabled` | boolean | Whether task is currently enabled | +| `tasks[].running` | boolean | Whether task is actively executing | +| `tasks[].autoStart` | boolean | Whether task starts automatically | +| `system.freeHeap` | integer | Available RAM in bytes | +| `system.uptime` | integer | System uptime in milliseconds | + +**Example Response:** +```json +{ + "summary": { + "totalTasks": 6, + "activeTasks": 5 + }, + "tasks": [ + { + "name": "discovery_send", + "interval": 1000, + "enabled": true, + "running": true, + "autoStart": true + } + ], + "system": { + "freeHeap": 48748, + "uptime": 12345 + } +} +``` + +#### POST /api/tasks/control + +Controls the execution state of individual tasks. Supports enabling, disabling, starting, stopping, and getting detailed status for specific tasks. + +**Parameters:** +- `task` (required): Name of the task to control +- `action` (required): Action to perform + +**Available Actions:** + +| Action | Description | Use Case | +|--------|-------------|----------| +| `enable` | Enable a disabled task | Resume background operations | +| `disable` | Disable a running task | Pause resource-intensive tasks | +| `start` | Start a stopped task | Begin task execution | +| `stop` | Stop a running task | Halt task execution | +| `status` | Get detailed status for a specific task | Monitor individual task health | + +**Example Response:** +```json +{ + "success": true, + "message": "Task enabled", + "task": "heartbeat", + "action": "enable" +} +``` + +**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 + } + } +} +``` + +### System Status + +#### GET /api/node/status + +Returns comprehensive system resource information including memory usage, chip details, and a registry of all available API endpoints. + +**Response Fields:** +- `freeHeap`: Available RAM in bytes +- `chipId`: ESP8266 chip ID +- `sdkVersion`: ESP8266 SDK version +- `cpuFreqMHz`: CPU frequency in MHz +- `flashChipSize`: Flash chip size in bytes +- `api`: Array of registered API endpoints + +#### GET /api/cluster/members + +Returns information about all nodes in the cluster, including their health status, resources, and API endpoints. + +**Response Fields:** +- `members[]`: Array of cluster node information +- `hostname`: Node hostname +- `ip`: Node IP address +- `lastSeen`: Timestamp of last communication +- `latency`: Network latency in milliseconds +- `status`: Node health status (ACTIVE, INACTIVE, DEAD) +- `resources`: System resource information +- `api`: Available API endpoints + +### System Management + +#### POST /api/node/update + +Initiates an over-the-air firmware update. The firmware file should be uploaded as multipart/form-data. + +**Parameters:** +- `firmware`: Firmware binary file (.bin) + +#### POST /api/node/restart + +Triggers a system restart. The response will be sent before the restart occurs. + +## HTTP Status Codes + +| Code | Description | Use Case | +|------|-------------|----------| +| 200 | Success | Operation completed successfully | +| 400 | Bad Request | Invalid parameters or action | +| 404 | Not Found | Task or endpoint not found | +| 500 | Internal Server Error | System error occurred | + +## OpenAPI Specification + +A complete OpenAPI 3.0 specification is available in the [`api/`](../api/) folder. This specification can be used to: + +- Generate client libraries in multiple programming languages +- Create interactive API documentation +- Validate API requests and responses +- Generate mock servers for testing +- Integrate with API management platforms + +See [`api/README.md`](../api/README.md) for detailed usage instructions. + +## Usage Examples + +### Basic Task Status Check +```bash +curl -s http://10.0.1.60/api/tasks/status | jq '.' +``` + +### Task Control +```bash +# Disable a task +curl -X POST http://10.0.1.60/api/tasks/control \ + -d "task=heartbeat&action=disable" + +# Get detailed status +curl -X POST http://10.0.1.60/api/tasks/control \ + -d "task=discovery_send&action=status" +``` + +### System Monitoring +```bash +# Check system resources +curl -s http://10.0.1.60/api/node/status | jq '.freeHeap' + +# Monitor cluster health +curl -s http://10.0.1.60/api/cluster/members | jq '.members[].status' +``` + +## Integration Examples + +### Python Client +```python +import requests + +# Get task status +response = requests.get('http://10.0.1.60/api/tasks/status') +tasks = response.json() + +# Check active tasks +active_count = tasks['summary']['activeTasks'] +print(f"Active tasks: {active_count}") + +# Control a task +control_data = {'task': 'heartbeat', 'action': 'disable'} +response = requests.post('http://10.0.1.60/api/tasks/control', data=control_data) +``` + +### JavaScript Client +```javascript +// Get task status +fetch('http://10.0.1.60/api/tasks/status') + .then(response => response.json()) + .then(data => { + console.log(`Total tasks: ${data.summary.totalTasks}`); + console.log(`Active tasks: ${data.summary.activeTasks}`); + }); + +// Control a task +fetch('http://10.0.1.60/api/tasks/control', { + method: 'POST', + headers: {'Content-Type': 'application/x-www-form-urlencoded'}, + body: 'task=heartbeat&action=disable' +}); +``` + +## Task Management Examples + +### Monitoring Task Health +```bash +# Check overall task status +curl -s http://10.0.1.60/api/tasks/status | jq '.' + +# Monitor specific task +curl -s -X POST http://10.0.1.60/api/tasks/control \ + -d "task=heartbeat&action=status" | jq '.' + +# Watch for low memory conditions +watch -n 5 'curl -s http://10.0.1.60/api/tasks/status | jq ".system.freeHeap"' +``` + +### Task Control Workflows +```bash +# Temporarily disable discovery to reduce network traffic +curl -X POST http://10.0.1.60/api/tasks/control \ + -d "task=discovery_send&action=disable" + +# Check if it's disabled +curl -s -X POST http://10.0.1.60/api/tasks/control \ + -d "task=discovery_send&action=status" | jq '.taskDetails.enabled' + +# Re-enable when needed +curl -X POST http://10.0.1.60/api/tasks/control \ + -d "task=discovery_send&action=enable" +``` + +### Cluster Health Monitoring +```bash +# Monitor all nodes in cluster +for ip in 10.0.1.60 10.0.1.61 10.0.1.62; do + echo "=== Node $ip ===" + curl -s "http://$ip/api/tasks/status" | jq '.summary' +done +``` \ No newline at end of file diff --git a/docs/Architecture.md b/docs/Architecture.md new file mode 100644 index 0000000..3eafbb9 --- /dev/null +++ b/docs/Architecture.md @@ -0,0 +1,358 @@ +# SPORE Architecture & Implementation + +## System Overview + +SPORE (SProcket ORchestration Engine) is a cluster engine for ESP8266 microcontrollers that provides automatic node discovery, health monitoring, and over-the-air updates in a distributed network environment. + +## Core Components + +The system architecture consists of several key components working together: + +### Network Manager +- **WiFi Connection Handling**: Automatic WiFi STA/AP configuration +- **Hostname Configuration**: MAC-based hostname generation +- **Fallback Management**: Automatic access point creation if WiFi connection fails + +### Cluster Manager +- **Node Discovery**: UDP-based automatic node detection +- **Member List Management**: Dynamic cluster membership tracking +- **Health Monitoring**: Continuous node status checking +- **Resource Tracking**: Monitor node resources and capabilities + +### API Server +- **HTTP API Server**: RESTful API for cluster management +- **Dynamic Endpoint Registration**: Automatic API endpoint discovery +- **Service Registry**: Track available services across the cluster + +### Task Scheduler +- **Cooperative Multitasking**: Background task management system +- **Task Lifecycle Management**: Automatic task execution and monitoring +- **Resource Optimization**: Efficient task scheduling and execution + +### Node Context +- **Central Context**: Shared resources and configuration +- **Event System**: Local and cluster-wide event publishing/subscription +- **Resource Management**: Centralized resource allocation and monitoring + +## Auto Discovery Protocol + +The cluster uses a UDP-based discovery protocol for automatic node detection: + +### Discovery Process + +1. **Discovery Broadcast**: Nodes periodically send UDP packets on port 4210 +2. **Response Handling**: Nodes respond with their hostname and IP address +3. **Member Management**: Discovered nodes are automatically added to the cluster +4. **Health Monitoring**: Continuous status checking via HTTP API calls + +### Protocol Details + +- **UDP Port**: 4210 (configurable) +- **Discovery Message**: `CLUSTER_DISCOVERY` +- **Response Message**: `CLUSTER_RESPONSE` +- **Broadcast Address**: 255.255.255.255 +- **Discovery Interval**: 1 second (configurable) +- **Listen Interval**: 100ms (configurable) + +### Node Status Categories + +Nodes are automatically categorized by their activity: + +- **ACTIVE**: Responding within 10 seconds +- **INACTIVE**: No response for 10-60 seconds +- **DEAD**: No response for over 60 seconds + +## Task Scheduling System + +The system runs several background tasks at different intervals: + +### Core System Tasks + +| Task | Interval | Purpose | +|------|----------|---------| +| **Discovery Send** | 1 second | Send UDP discovery packets | +| **Discovery Listen** | 100ms | Listen for discovery responses | +| **Status Updates** | 1 second | Monitor cluster member health | +| **Heartbeat** | 2 seconds | Maintain cluster connectivity | +| **Member Info** | 10 seconds | Update detailed node information | +| **Debug Output** | 5 seconds | Print cluster status | + +### Task Management Features + +- **Dynamic Intervals**: Change execution frequency on-the-fly +- **Runtime Control**: Enable/disable tasks without restart +- **Status Monitoring**: Real-time task health tracking +- **Resource Integration**: View task status with system resources + +## Event System + +The `NodeContext` provides an event-driven architecture for system-wide communication: + +### Event Subscription + +```cpp +// Subscribe to events +ctx.on("node_discovered", [](void* data) { + NodeInfo* node = static_cast(data); + // Handle new node discovery +}); + +ctx.on("cluster_updated", [](void* data) { + // Handle cluster membership changes +}); +``` + +### Event Publishing + +```cpp +// Publish events +ctx.fire("node_discovered", &newNode); +ctx.fire("cluster_updated", &clusterData); +``` + +### Available Events + +- **`node_discovered`**: New node added to cluster +- **`cluster_updated`**: Cluster membership changed +- **`resource_update`**: Node resources updated +- **`health_check`**: Node health status changed + +## Resource Monitoring + +Each node tracks comprehensive system resources: + +### System Resources + +- **Free Heap Memory**: Available RAM in bytes +- **Chip ID**: Unique ESP8266 identifier +- **SDK Version**: ESP8266 firmware version +- **CPU Frequency**: Operating frequency in MHz +- **Flash Chip Size**: Total flash storage in bytes + +### API Endpoint Registry + +- **Dynamic Discovery**: Automatically detect available endpoints +- **Method Information**: HTTP method (GET, POST, etc.) +- **Service Catalog**: Complete service registry across cluster + +### Health Metrics + +- **Response Time**: API response latency +- **Uptime**: System uptime in milliseconds +- **Connection Status**: Network connectivity health +- **Resource Utilization**: Memory and CPU usage + +## WiFi Fallback System + +The system includes automatic WiFi fallback for robust operation: + +### Fallback Process + +1. **Primary Connection**: Attempts to connect to configured WiFi network +2. **Connection Failure**: If connection fails, creates an access point +3. **Hostname Generation**: Automatically generates hostname from MAC address +4. **Service Continuity**: Maintains cluster functionality in fallback mode + +### Configuration + +- **SSID Format**: `SPORE_` +- **Password**: Configurable fallback password +- **IP Range**: 192.168.4.x subnet +- **Gateway**: 192.168.4.1 + +## Cluster Topology + +### Node Types + +- **Master Node**: Primary cluster coordinator (if applicable) +- **Worker Nodes**: Standard cluster members +- **Edge Nodes**: Network edge devices + +### Network Architecture + +- **Mesh-like Structure**: Nodes can communicate with each other +- **Dynamic Routing**: Automatic path discovery between nodes +- **Load Distribution**: Tasks distributed across available nodes +- **Fault Tolerance**: Automatic failover and recovery + +## Data Flow + +### Discovery Flow + +``` +Node A → UDP Broadcast → Node B +Node B → HTTP Response → Node A +Node A → Add to Cluster → Update Member List +``` + +### Health Monitoring Flow + +``` +Cluster Manager → HTTP Request → Node Status +Node → JSON Response → Resource Information +Cluster Manager → Update Health → Fire Events +``` + +### Task Execution Flow + +``` +Task Scheduler → Check Intervals → Execute Tasks +Task → Update Status → API Server +API Server → JSON Response → Client +``` + +## Performance Characteristics + +### Memory Usage + +- **Base System**: ~15-20KB RAM +- **Per Task**: ~100-200 bytes per task +- **Cluster Members**: ~50-100 bytes per member +- **API Endpoints**: ~20-30 bytes per endpoint + +### Network Overhead + +- **Discovery Packets**: 64 bytes every 1 second +- **Health Checks**: ~200-500 bytes every 1 second +- **Status Updates**: ~1-2KB per node +- **API Responses**: Varies by endpoint (typically 100B-5KB) + +### Processing Overhead + +- **Task Execution**: Minimal overhead per task +- **Event Processing**: Fast event dispatch +- **JSON Parsing**: Efficient ArduinoJson usage +- **Network I/O**: Asynchronous operations + +## Security Considerations + +### Current Implementation + +- **Network Access**: Local network only (no internet exposure) +- **Authentication**: None currently implemented +- **Data Validation**: Basic input validation +- **Resource Limits**: Memory and processing constraints + +### Future Enhancements + +- **TLS/SSL**: Encrypted communications +- **API Keys**: Authentication for API access +- **Access Control**: Role-based permissions +- **Audit Logging**: Security event tracking + +## Scalability + +### Cluster Size Limits + +- **Theoretical**: Up to 255 nodes (IP subnet limit) +- **Practical**: 20-50 nodes for optimal performance +- **Memory Constraint**: ~8KB available for member tracking +- **Network Constraint**: UDP packet size limits + +### Performance Scaling + +- **Linear Scaling**: Most operations scale linearly with node count +- **Discovery Overhead**: Increases with cluster size +- **Health Monitoring**: Parallel HTTP requests +- **Task Management**: Independent per-node execution + +## Configuration Management + +### Environment Variables + +```bash +# API node IP for cluster management +export API_NODE=192.168.1.100 + +# Cluster configuration +export CLUSTER_PORT=4210 +export DISCOVERY_INTERVAL=1000 +export HEALTH_CHECK_INTERVAL=1000 +``` + +### PlatformIO Configuration + +The project uses PlatformIO with the following configuration: + +- **Framework**: Arduino +- **Board**: ESP-01 with 1MB flash +- **Upload Speed**: 115200 baud +- **Flash Mode**: DOUT (required for ESP-01S) + +### Dependencies + +The project requires the following libraries: +- `esp32async/ESPAsyncWebServer@^3.8.0` - HTTP API server +- `bblanchon/ArduinoJson@^7.4.2` - JSON processing +- `arkhipenko/TaskScheduler@^3.8.5` - Cooperative multitasking + +## Development Workflow + +### Building + +Build the firmware for specific chip: + +```bash +./ctl.sh build target esp01_1m +``` + +### Flashing + +Flash firmware to a connected device: + +```bash +./ctl.sh flash target esp01_1m +``` + +### Over-The-Air Updates + +Update a specific node: + +```bash +./ctl.sh ota update 192.168.1.100 esp01_1m +``` + +Update all nodes in the cluster: + +```bash +./ctl.sh ota all esp01_1m +``` + +### Cluster Management + +View cluster members: + +```bash +./ctl.sh cluster members +``` + +## Troubleshooting + +### Common Issues + +1. **Discovery Failures**: Check UDP port 4210 is not blocked +2. **WiFi Connection**: Verify SSID/password in Config.cpp +3. **OTA Updates**: Ensure sufficient flash space (1MB minimum) +4. **Cluster Split**: Check network connectivity between nodes + +### Debug Output + +Enable serial monitoring to see cluster activity: + +```bash +pio device monitor +``` + +### Performance Monitoring + +- **Memory Usage**: Monitor free heap with `/api/node/status` +- **Task Health**: Check task status with `/api/tasks/status` +- **Cluster Health**: Monitor member status with `/api/cluster/members` +- **Network Latency**: Track response times in cluster data + +## Related Documentation + +- **[Task Management](./TaskManagement.md)** - Background task system +- **[API Reference](./API.md)** - REST API documentation +- **[TaskManager API](./TaskManager.md)** - TaskManager class reference +- **[OpenAPI Specification](../api/)** - Machine-readable API specification \ No newline at end of file diff --git a/docs/Development.md b/docs/Development.md new file mode 100644 index 0000000..0cfd4d4 --- /dev/null +++ b/docs/Development.md @@ -0,0 +1,437 @@ +# Development & Deployment Guide + +## Prerequisites + +### Required Tools + +- **PlatformIO Core** or **PlatformIO IDE** +- **ESP8266 development tools** +- **`jq`** for JSON processing in scripts +- **Git** for version control + +### System Requirements + +- **Operating System**: Linux, macOS, or Windows +- **Python**: 3.7+ (for PlatformIO) +- **Memory**: 4GB+ RAM recommended +- **Storage**: 2GB+ free space for development environment + +## Project Structure + +``` +spore/ +├── src/ # Source code +│ ├── main.cpp # Main application entry point +│ ├── ApiServer.cpp # HTTP API server implementation +│ ├── ClusterManager.cpp # Cluster management logic +│ ├── NetworkManager.cpp # WiFi and network handling +│ ├── TaskManager.cpp # Background task management +│ └── NodeContext.cpp # Central context and events +├── include/ # Header files +├── lib/ # Library files +├── docs/ # Documentation +├── api/ # OpenAPI specification +├── examples/ # Example code +├── test/ # Test files +├── platformio.ini # PlatformIO configuration +└── ctl.sh # Build and deployment scripts +``` + +## PlatformIO Configuration + +### Framework and Board + +The project uses PlatformIO with the following configuration: + +```ini +[env:esp01_1m] +platform = platformio/espressif8266@^4.2.1 +board = esp01_1m +framework = arduino +upload_speed = 115200 +flash_mode = dout +``` + +### Key Configuration Details + +- **Framework**: Arduino +- **Board**: ESP-01 with 1MB flash +- **Upload Speed**: 115200 baud +- **Flash Mode**: DOUT (required for ESP-01S) +- **Build Type**: Release (optimized for production) + +### Dependencies + +The project requires the following libraries: + +```ini +lib_deps = + esp32async/ESPAsyncWebServer@^3.8.0 + bblanchon/ArduinoJson@^7.4.2 + arkhipenko/TaskScheduler@^3.8.5 + ESP8266HTTPClient@1.2 + ESP8266WiFi@1.0 +``` + +## Building + +### Basic Build Commands + +Build the firmware for specific chip: + +```bash +# Build for ESP-01 1MB +./ctl.sh build target esp01_1m + +# Build for D1 Mini +./ctl.sh build target d1_mini + +# Build with verbose output +pio run -v +``` + +### Build Targets + +Available build targets: + +| Target | Description | Flash Size | +|--------|-------------|------------| +| `esp01_1m` | ESP-01 with 1MB flash | 1MB | +| `d1_mini` | D1 Mini with 4MB flash | 4MB | + +### Build Artifacts + +After successful build: + +- **Firmware**: `.pio/build/{target}/firmware.bin` +- **ELF File**: `.pio/build/{target}/firmware.elf` +- **Map File**: `.pio/build/{target}/firmware.map` + +## Flashing + +### Direct USB Flashing + +Flash firmware to a connected device: + +```bash +# Flash ESP-01 +./ctl.sh flash target esp01_1m + +# Flash D1 Mini +./ctl.sh flash target d1_mini + +# Manual flash command +pio run --target upload +``` + +### Flash Settings + +- **Upload Speed**: 115200 baud (optimal for ESP-01) +- **Flash Mode**: DOUT (required for ESP-01S) +- **Reset Method**: Hardware reset or manual reset + +### Troubleshooting Flashing + +Common flashing issues: + +1. **Connection Failed**: Check USB cable and drivers +2. **Wrong Upload Speed**: Try lower speeds (9600, 57600) +3. **Flash Mode Error**: Ensure DOUT mode for ESP-01S +4. **Permission Denied**: Run with sudo or add user to dialout group + +## Over-The-Air Updates + +### Single Node Update + +Update a specific node: + +```bash +# Update specific node +./ctl.sh ota update 192.168.1.100 esp01_1m + +# Update with custom firmware +./ctl.sh ota update 192.168.1.100 esp01_1m custom_firmware.bin +``` + +### Cluster-Wide Updates + +Update all nodes in the cluster: + +```bash +# Update all nodes +./ctl.sh ota all esp01_1m +``` + +### OTA Process + +1. **Firmware Upload**: Send firmware to target node +2. **Verification**: Check firmware integrity +3. **Installation**: Install new firmware +4. **Restart**: Node restarts with new firmware +5. **Verification**: Confirm successful update + +### OTA Requirements + +- **Flash Space**: Minimum 1MB for OTA updates +- **Network**: Stable WiFi connection +- **Power**: Stable power supply during update +- **Memory**: Sufficient RAM for firmware processing + +## Cluster Management + +### View Cluster Status + +```bash +# View all cluster members +./ctl.sh cluster members + +# View specific node details +./ctl.sh cluster members --node 192.168.1.100 +``` + +### Cluster Commands + +Available cluster management commands: + +| Command | Description | +|---------|-------------| +| `members` | List all cluster members | +| `status` | Show cluster health status | +| `discover` | Force discovery process | +| `health` | Check cluster member health | + +### Cluster Monitoring + +Monitor cluster health in real-time: + +```bash +# Watch cluster status +watch -n 5 './ctl.sh cluster members' + +# Monitor specific metrics +./ctl.sh cluster members | jq '.members[] | {hostname, status, latency}' +``` + +## Development Workflow + +### Local Development + +1. **Setup Environment**: + ```bash + git clone + cd spore + pio run + ``` + +2. **Make Changes**: + - Edit source files in `src/` + - Modify headers in `include/` + - Update configuration in `platformio.ini` + +3. **Test Changes**: + ```bash + pio run + pio check + ``` + +### Testing + +Run various tests: + +```bash +# Code quality check +pio check + +# Unit tests (if available) +pio test + +# Memory usage analysis +pio run --target size +``` + +### Debugging + +Enable debug output: + +```bash +# Serial monitoring +pio device monitor + +# Build with debug symbols +pio run --environment esp01_1m --build-flags -DDEBUG +``` + +## Configuration Management + +### Environment Setup + +Create a `.env` file in your project root: + +```bash +# API node IP for cluster management +export API_NODE=192.168.1.100 +``` + +### Configuration Files + +Key configuration files: + +- **`platformio.ini`**: Build and upload configuration +- **`src/Config.cpp`**: Application configuration +- **`.env`**: Environment variables +- **`ctl.sh`**: Build and deployment scripts + +### Configuration Options + +Available configuration options: + +| Option | Default | Description | +|--------|---------|-------------| +| `CLUSTER_PORT` | 4210 | UDP discovery port | +| `DISCOVERY_INTERVAL` | 1000 | Discovery packet interval (ms) | +| `HEALTH_CHECK_INTERVAL` | 1000 | Health check interval (ms) | +| `API_SERVER_PORT` | 80 | HTTP API server port | + +## Deployment Strategies + +### Development Deployment + +For development and testing: + +1. **Build**: `pio run` +2. **Flash**: `pio run --target upload` +3. **Monitor**: `pio device monitor` + +### Production Deployment + +For production systems: + +1. **Build Release**: `pio run --environment esp01_1m` +2. **OTA Update**: `./ctl.sh ota update esp01_1m` +3. **Verify**: Check node status via API + +### Continuous Integration + +Automated deployment pipeline: + +```yaml +# Example GitHub Actions workflow +- name: Build Firmware + run: pio run --environment esp01_1m + +- name: Deploy to Test Cluster + run: ./ctl.sh ota all esp01_1m --target test + +- name: Deploy to Production + run: ./ctl.sh ota all esp01_1m --target production +``` + +## Monitoring and Debugging + +### Serial Output + +Enable serial monitoring: + +```bash +# Basic monitoring +pio device monitor + +# With specific baud rate +pio device monitor --baud 115200 + +# Filter specific messages +pio device monitor | grep "Cluster" +``` + +### API Monitoring + +Monitor system via HTTP API: + +```bash +# Check system status +curl -s http://192.168.1.100/api/node/status | jq '.' + +# Monitor tasks +curl -s http://192.168.1.100/api/tasks/status | jq '.' + +# Check cluster health +curl -s http://192.168.1.100/api/cluster/members | jq '.' +``` + +### Performance Monitoring + +Track system performance: + +```bash +# Memory usage over time +watch -n 5 'curl -s http://192.168.1.100/api/node/status | jq ".freeHeap"' + +# Task execution status +watch -n 10 'curl -s http://192.168.1.100/api/tasks/status | jq ".summary"' +``` + +## Troubleshooting + +### Common Issues + +1. **Discovery Failures**: Check UDP port 4210 is not blocked +2. **WiFi Connection**: Verify SSID/password in Config.cpp +3. **OTA Updates**: Ensure sufficient flash space (1MB minimum) +4. **Cluster Split**: Check network connectivity between nodes + +### Debug Commands + +Useful debugging commands: + +```bash +# Check network connectivity +ping 192.168.1.100 + +# Test UDP port +nc -u 192.168.1.100 4210 + +# Check HTTP API +curl -v http://192.168.1.100/api/node/status + +# Monitor system resources +./ctl.sh cluster members | jq '.members[] | {hostname, status, resources.freeHeap}' +``` + +### Performance Issues + +Common performance problems: + +- **Memory Leaks**: Monitor free heap over time +- **Network Congestion**: Check discovery intervals +- **Task Overload**: Review task execution intervals +- **WiFi Interference**: Check channel and signal strength + +## Best Practices + +### Code Organization + +1. **Modular Design**: Keep components loosely coupled +2. **Clear Interfaces**: Define clear APIs between components +3. **Error Handling**: Implement proper error handling and logging +4. **Resource Management**: Efficient memory and resource usage + +### Testing Strategy + +1. **Unit Tests**: Test individual components +2. **Integration Tests**: Test component interactions +3. **System Tests**: Test complete system functionality +4. **Performance Tests**: Monitor resource usage and performance + +### Deployment Strategy + +1. **Staged Rollout**: Deploy to test cluster first +2. **Rollback Plan**: Maintain ability to rollback updates +3. **Monitoring**: Monitor system health during deployment +4. **Documentation**: Keep deployment procedures updated + +## Related Documentation + +- **[Architecture Guide](./Architecture.md)** - System architecture overview +- **[Task Management](./TaskManagement.md)** - Background task system +- **[API Reference](./API.md)** - REST API documentation +- **[OpenAPI Specification](../api/)** - Machine-readable API specification \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..ce21dca --- /dev/null +++ b/docs/README.md @@ -0,0 +1,85 @@ +# SPORE Documentation + +This folder contains comprehensive documentation for the SPORE embedded system. + +## Available Documentation + +### 📖 [API.md](./API.md) +Complete API reference with detailed endpoint documentation, examples, and integration guides. + +**Includes:** +- API endpoint specifications +- Request/response examples +- HTTP status codes +- Integration examples (Python, JavaScript) +- Task management workflows +- Cluster monitoring examples + +### 📖 [TaskManager.md](./TaskManager.md) +Comprehensive guide to the TaskManager system for background task management. + +**Includes:** +- Basic usage examples +- Advanced binding techniques +- Task status monitoring +- API integration details +- Performance considerations + +### 📖 [TaskManagement.md](./TaskManagement.md) +Complete guide to the task management system with examples and best practices. + +**Includes:** +- Task registration methods (std::bind, lambdas, functions) +- Task control and lifecycle management +- Remote task management via API +- Performance considerations and best practices +- Migration guides and compatibility information + +### 📖 [Architecture.md](./Architecture.md) +Comprehensive system architecture and implementation details. + +**Includes:** +- Core component descriptions +- Auto discovery protocol details +- Task scheduling system +- Event system architecture +- Resource monitoring +- Performance characteristics +- Security and scalability considerations + +### 📖 [Development.md](./Development.md) +Complete development and deployment guide. + +**Includes:** +- PlatformIO configuration +- Build and flash instructions +- OTA update procedures +- Cluster management commands +- Development workflow +- Troubleshooting guide +- Best practices + +## Quick Links + +- **Main Project**: [../README.md](../README.md) +- **OpenAPI Specification**: [../api/](../api/) +- **Source Code**: [../src/](../src/) + +## Contributing + +When adding new documentation: + +1. Create a new `.md` file in this folder +2. Use clear, descriptive filenames +3. Include practical examples and code snippets +4. Update this README.md to reference new files +5. Follow the existing documentation style + +## Documentation Style Guide + +- Use clear, concise language +- Include practical examples +- Use code blocks with appropriate language tags +- Include links to related documentation +- Use emojis sparingly for visual organization +- Keep README.md files focused and scoped \ No newline at end of file diff --git a/docs/TaskManagement.md b/docs/TaskManagement.md new file mode 100644 index 0000000..ad9da85 --- /dev/null +++ b/docs/TaskManagement.md @@ -0,0 +1,348 @@ +# 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 + +```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(); +``` + +## 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: Using std::bind (Recommended) + +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 2: 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 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): +```cpp +void discoverySendTask() { cluster.sendDiscovery(); } +void discoveryListenTask() { cluster.listenForDiscovery(); } + +taskManager.registerTask("discovery_send", interval, discoverySendTask); +taskManager.registerTask("discovery_listen", interval, discoveryListenTask); +``` + +### After (with std::bind): +```cpp +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 + +## 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 \ No newline at end of file diff --git a/docs/TaskManager.md b/docs/TaskManager.md deleted file mode 100644 index 0315a1c..0000000 --- a/docs/TaskManager.md +++ /dev/null @@ -1,180 +0,0 @@ -# TaskManager - -## Basic Usage - -### Including Required Headers - -```cpp -#include // For std::bind -#include "TaskManager.h" -``` - -### Registering Member Functions - -```cpp -class MyClass { -public: - void myMethod() { - Serial.println("My method called"); - } - - void methodWithParams(int value, String text) { - Serial.printf("Method called with %d and %s\n", value, text.c_str()); - } -}; - -// Create an instance -MyClass myObject; - -// Register member function -taskManager.registerTask("my_task", 1000, - std::bind(&MyClass::myMethod, &myObject)); - -// Register method with parameters -taskManager.registerTask("param_task", 2000, - std::bind(&MyClass::methodWithParams, &myObject, 42, "hello")); -``` - -### Registering Lambda Functions - -```cpp -// Simple lambda -taskManager.registerTask("lambda_task", 3000, []() { - Serial.println("Lambda executed"); -}); - -// Lambda with capture -int counter = 0; -taskManager.registerTask("counter_task", 4000, [&counter]() { - counter++; - Serial.printf("Counter: %d\n", counter); -}); - -// Lambda that calls multiple methods -taskManager.registerTask("multi_task", 5000, [&myObject]() { - myObject.myMethod(); - // Do other work... -}); -``` - -### Registering Global Functions - -```cpp -void globalFunction() { - Serial.println("Global function called"); -} - -// Still supported for backward compatibility -taskManager.registerTask("global_task", 6000, globalFunction); -``` - -## Advanced Examples - -### Binding to Different Object Types - -```cpp -class NetworkManager { -public: - void sendHeartbeat() { /* ... */ } - void checkConnection() { /* ... */ } -}; - -class SensorManager { -public: - void readSensors() { /* ... */ } - void calibrate() { /* ... */ } -}; - -NetworkManager network; -SensorManager sensors; - -// Bind to different objects -taskManager.registerTask("heartbeat", 1000, - std::bind(&NetworkManager::sendHeartbeat, &network)); -taskManager.registerTask("sensor_read", 500, - std::bind(&SensorManager::readSensors, &sensors)); -``` - -### Using std::placeholders for Complex Binding - -```cpp -#include - -class ConfigManager { -public: - void updateConfig(int interval, bool enabled) { - Serial.printf("Updating config: interval=%d, enabled=%d\n", interval, enabled); - } -}; - -ConfigManager config; - -// Use placeholders for complex parameter binding -using namespace std::placeholders; -taskManager.registerTask("config_update", 10000, - std::bind(&ConfigManager::updateConfig, &config, _1, _2)); -``` - -### Conditional Task Execution - -```cpp -class TaskController { -public: - bool shouldExecute() { - return millis() % 10000 < 5000; // Execute only in first 5 seconds of each 10-second cycle - } - - void conditionalTask() { - if (shouldExecute()) { - Serial.println("Conditional task executed"); - } - } -}; - -TaskController controller; - -taskManager.registerTask("conditional", 1000, - std::bind(&TaskController::conditionalTask, &controller)); -``` - -## Benefits of Using std::bind - -1. **Cleaner Code**: No need for wrapper functions -2. **Direct Binding**: Bind member functions directly to objects -3. **Parameter Passing**: Easily pass parameters to bound methods -4. **Lambda Support**: Use lambdas for complex logic -5. **Type Safety**: Better type checking than function pointers -6. **Flexibility**: Mix and match different callable types - -## Migration from Wrapper Functions - -### Before (with wrapper functions): -```cpp -void discoverySendTask() { cluster.sendDiscovery(); } -void discoveryListenTask() { cluster.listenForDiscovery(); } - -taskManager.registerTask("discovery_send", interval, discoverySendTask); -taskManager.registerTask("discovery_listen", interval, discoveryListenTask); -``` - -### After (with std::bind): -```cpp -taskManager.registerTask("discovery_send", interval, - std::bind(&ClusterManager::sendDiscovery, &cluster)); -taskManager.registerTask("discovery_listen", interval, - std::bind(&ClusterManager::listenForDiscovery, &cluster)); -``` - -## 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 - -## 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 \ No newline at end of file diff --git a/include/TaskManager.h b/include/TaskManager.h index cdcf67a..c08136f 100644 --- a/include/TaskManager.h +++ b/include/TaskManager.h @@ -5,6 +5,7 @@ #include #include #include "NodeContext.h" +#include // Forward declarations to avoid multiple definition errors class Task; @@ -45,6 +46,9 @@ public: bool isTaskRunning(const std::string& name) const; unsigned long getTaskInterval(const std::string& name) const; + // Get comprehensive task status information + std::vector> getAllTaskStatuses(JsonDocument& doc) const; + // Management methods void initialize(); void enableAllTasks(); diff --git a/src/ApiServer.cpp b/src/ApiServer.cpp index 5713d9b..625b0bf 100644 --- a/src/ApiServer.cpp +++ b/src/ApiServer.cpp @@ -1,4 +1,5 @@ #include "ApiServer.h" +#include ApiServer::ApiServer(NodeContext& ctx, TaskManager& taskMgr, uint16_t port) : server(port), ctx(ctx), taskManager(taskMgr) {} @@ -184,13 +185,31 @@ void ApiServer::onRestartRequest(AsyncWebServerRequest *request) { void ApiServer::onTaskStatusRequest(AsyncWebServerRequest *request) { JsonDocument doc; - JsonArray tasksArr = doc["tasks"].to(); - // This would need to be implemented in TaskManager to expose task status - // For now, we'll return a basic response - JsonObject taskObj = tasksArr.add(); - taskObj["message"] = "Task status endpoint - implementation pending"; - taskObj["note"] = "Task status will be available in future versions"; + // Get comprehensive task status from TaskManager + auto taskStatuses = taskManager.getAllTaskStatuses(doc); + + // Add summary information + JsonObject summaryObj = doc["summary"].to(); + summaryObj["totalTasks"] = taskStatuses.size(); + summaryObj["activeTasks"] = std::count_if(taskStatuses.begin(), taskStatuses.end(), + [](const auto& pair) { return pair.second["enabled"]; }); + + // Add detailed task information + JsonArray tasksArr = doc["tasks"].to(); + for (const auto& taskPair : taskStatuses) { + JsonObject taskObj = tasksArr.add(); + taskObj["name"] = taskPair.first; + taskObj["interval"] = taskPair.second["interval"]; + taskObj["enabled"] = taskPair.second["enabled"]; + taskObj["running"] = taskPair.second["running"]; + taskObj["autoStart"] = taskPair.second["autoStart"]; + } + + // Add system information + JsonObject systemObj = doc["system"].to(); + systemObj["freeHeap"] = ESP.getFreeHeap(); + systemObj["uptime"] = millis(); String json; serializeJson(doc, json); @@ -222,9 +241,39 @@ void ApiServer::onTaskControlRequest(AsyncWebServerRequest *request) { taskManager.stopTask(taskName.c_str()); success = true; message = "Task stopped"; + } else if (action == "status") { + // Get detailed status for a specific task + success = true; + message = "Task status retrieved"; + + // Create JsonDocument for status response + JsonDocument statusDoc; + statusDoc["success"] = success; + statusDoc["message"] = message; + statusDoc["task"] = taskName; + statusDoc["action"] = action; + + // Add task details to response + statusDoc["taskDetails"] = JsonObject(); + JsonObject taskDetails = statusDoc["taskDetails"]; + taskDetails["name"] = taskName; + taskDetails["enabled"] = taskManager.isTaskEnabled(taskName.c_str()); + taskDetails["running"] = taskManager.isTaskRunning(taskName.c_str()); + taskDetails["interval"] = taskManager.getTaskInterval(taskName.c_str()); + + // Add system context + taskDetails["system"] = JsonObject(); + JsonObject systemInfo = taskDetails["system"]; + systemInfo["freeHeap"] = ESP.getFreeHeap(); + systemInfo["uptime"] = millis(); + + String statusJson; + serializeJson(statusDoc, statusJson); + request->send(200, "application/json", statusJson); + return; // Early return since we've already sent the response } else { success = false; - message = "Invalid action. Use: enable, disable, start, or stop"; + message = "Invalid action. Use: enable, disable, start, stop, or status"; } JsonDocument doc; @@ -241,7 +290,7 @@ void ApiServer::onTaskControlRequest(AsyncWebServerRequest *request) { JsonDocument doc; doc["success"] = false; doc["message"] = "Missing parameters. Required: task, action"; - doc["example"] = "{\"task\": \"discovery_send\", \"action\": \"disable\"}"; + doc["example"] = "{\"task\": \"discovery_send\", \"action\": \"status\"}"; String json; serializeJson(doc, json); diff --git a/src/ClusterManager.cpp b/src/ClusterManager.cpp index dca56fc..c339e0d 100644 --- a/src/ClusterManager.cpp +++ b/src/ClusterManager.cpp @@ -6,6 +6,8 @@ ClusterManager::ClusterManager(NodeContext& ctx, TaskManager& taskMgr) : ctx(ctx NodeInfo* node = static_cast(data); this->addOrUpdateNode(node->hostname, node->ip); }); + // Register tasks + registerTasks(); } void ClusterManager::registerTasks() { diff --git a/src/TaskManager.cpp b/src/TaskManager.cpp index e944fc7..fc5d64f 100644 --- a/src/TaskManager.cpp +++ b/src/TaskManager.cpp @@ -191,4 +191,24 @@ Task* TaskManager::findTask(const std::string& name) const { } } return nullptr; +} + +std::vector> TaskManager::getAllTaskStatuses(JsonDocument& doc) const { + std::vector> taskStatuses; + + for (size_t i = 0; i < tasks.size() && i < taskDefinitions.size(); ++i) { + const auto& taskDef = taskDefinitions[i]; + const auto& task = tasks[i]; + + JsonObject taskStatus = doc.add(); + taskStatus["name"] = taskDef.name; + taskStatus["interval"] = task->getInterval(); + taskStatus["enabled"] = task->isEnabled(); + taskStatus["running"] = task->isEnabled(); // For now, enabled = running + taskStatus["autoStart"] = taskDef.autoStart; + + taskStatuses.push_back(std::make_pair(taskDef.name, taskStatus)); + } + + return taskStatuses; } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 4e810aa..8ecb2cb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,10 +18,7 @@ ApiServer apiServer(ctx, taskManager, ctx.config.api_server_port); void setup() { // Setup WiFi first network.setupWiFi(); - - // Register tasks in their respective classes - cluster.registerTasks(); - + // Initialize and start all tasks taskManager.initialize();