From a45871625e20a42b0650e98b49a54963e4fb7dff Mon Sep 17 00:00:00 2001 From: 0x1d Date: Tue, 14 Oct 2025 17:09:54 +0200 Subject: [PATCH] refactor: harmonize method names --- README.md | 8 +- docs/Architecture.md | 4 +- docs/Development.md | 14 +- docs/TaskManagement.md | 180 +++++++++++++++++++- examples/multimatrix/MultiMatrixService.cpp | 9 +- examples/multimatrix/MultiMatrixService.h | 2 +- examples/multimatrix/main.cpp | 2 +- examples/neopattern/NeoPatternService.cpp | 11 +- examples/neopattern/NeoPatternService.h | 2 +- examples/neopattern/main.cpp | 2 +- examples/relay/README.md | 2 +- examples/relay/RelayService.cpp | 7 +- examples/relay/RelayService.h | 3 +- examples/relay/main.cpp | 2 +- include/spore/Service.h | 3 + include/spore/Spore.h | 4 +- include/spore/core/ApiServer.h | 10 +- include/spore/services/ClusterService.h | 1 + include/spore/services/MonitoringService.h | 1 + include/spore/services/NetworkService.h | 1 + include/spore/services/NodeService.h | 1 + include/spore/services/StaticFileService.h | 1 + include/spore/services/TaskService.h | 1 + platformio.ini | 2 +- src/spore/Spore.cpp | 18 +- src/spore/core/ApiServer.cpp | 10 +- src/spore/services/ClusterService.cpp | 8 +- src/spore/services/MonitoringService.cpp | 6 +- src/spore/services/NetworkService.cpp | 12 +- src/spore/services/NodeService.cpp | 14 +- src/spore/services/StaticFileService.cpp | 4 + src/spore/services/TaskService.cpp | 8 +- 32 files changed, 274 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index 29cef7b..56520ae 100644 --- a/README.md +++ b/README.md @@ -93,12 +93,12 @@ void setup() { spore.setup(); // Create and register custom services - RelayService* relayService = new RelayService(spore.getTaskManager(), 2); - spore.addService(relayService); + RelayService* relayService = new RelayService(spore.getContext(), spore.getTaskManager(), 2); + spore.registerService(relayService); // Or using smart pointers - auto sensorService = std::make_shared(); - spore.addService(sensorService); + auto sensorService = std::make_shared(spore.getContext(), spore.getTaskManager()); + spore.registerService(sensorService); // Start the API server and complete initialization spore.begin(); diff --git a/docs/Architecture.md b/docs/Architecture.md index dfbb4ba..35cbc35 100644 --- a/docs/Architecture.md +++ b/docs/Architecture.md @@ -21,11 +21,13 @@ The system architecture consists of several key components working together: ### API Server - **HTTP API Server**: RESTful API for cluster management -- **Dynamic Endpoint Registration**: Automatic API endpoint discovery +- **Dynamic Endpoint Registration**: Services register endpoints via `registerEndpoints(ApiServer&)` - **Service Registry**: Track available services across the cluster +- **Service Lifecycle**: Services register both endpoints and tasks through unified interface ### Task Scheduler - **Cooperative Multitasking**: Background task management system (`TaskManager`) +- **Service Task Registration**: Services register tasks via `registerTasks(TaskManager&)` - **Task Lifecycle Management**: Enable/disable tasks and set intervals at runtime - **Execution Model**: Tasks run in `Spore::loop()` when their interval elapses diff --git a/docs/Development.md b/docs/Development.md index 9885c78..bcfaa33 100644 --- a/docs/Development.md +++ b/docs/Development.md @@ -29,13 +29,13 @@ spore/ │ │ ├── NetworkManager.cpp # WiFi and network handling │ │ ├── TaskManager.cpp # Background task management │ │ └── NodeContext.cpp # Central context and events -│ ├── services/ # Built-in services -│ │ ├── NodeService.cpp -│ │ ├── NetworkService.cpp -│ │ ├── ClusterService.cpp -│ │ ├── TaskService.cpp -│ │ ├── StaticFileService.cpp -│ │ └── MonitoringService.cpp +│ ├── services/ # Built-in services (implement Service interface) +│ │ ├── NodeService.cpp # registerEndpoints() + registerTasks() +│ │ ├── NetworkService.cpp # registerEndpoints() + registerTasks() +│ │ ├── ClusterService.cpp # registerEndpoints() + registerTasks() +│ │ ├── TaskService.cpp # registerEndpoints() + registerTasks() +│ │ ├── StaticFileService.cpp # registerEndpoints() + registerTasks() +│ │ └── MonitoringService.cpp # registerEndpoints() + registerTasks() │ └── types/ # Shared types ├── include/ # Header files ├── examples/ # Example apps per env (base, relay, neopattern) diff --git a/docs/TaskManagement.md b/docs/TaskManagement.md index b583463..07ada60 100644 --- a/docs/TaskManagement.md +++ b/docs/TaskManagement.md @@ -11,6 +11,31 @@ The TaskManager system provides: - **Status Monitoring**: View task status and configuration - **Automatic Lifecycle**: Tasks are automatically managed and executed +## Service Interface Integration + +Services now implement a unified interface for both endpoint and task registration: + +```cpp +class MyService : public Service { +public: + void registerEndpoints(ApiServer& api) override { + // Register HTTP endpoints + api.registerEndpoint("/api/my/status", HTTP_GET, + [this](AsyncWebServerRequest* request) { handleStatus(request); }); + } + + void registerTasks(TaskManager& taskManager) override { + // Register background tasks + taskManager.registerTask("my_heartbeat", 2000, + [this]() { sendHeartbeat(); }); + taskManager.registerTask("my_maintenance", 30000, + [this]() { performMaintenance(); }); + } + + const char* getName() const override { return "MyService"; } +}; +``` + ## Basic Usage ```cpp @@ -27,6 +52,46 @@ taskManager.registerTask("maintenance", 30000, maintenanceFunction); taskManager.initialize(); ``` +## Service Lifecycle + +The Spore framework automatically manages service registration and task lifecycle: + +### Service Registration Process + +1. **Service Creation**: Services are created with required dependencies (NodeContext, TaskManager, etc.) +2. **Service Registration**: Services are registered with the Spore framework via `spore.registerService()` +3. **Endpoint Registration**: When `spore.begin()` is called, `registerEndpoints()` is called for each service +4. **Task Registration**: Simultaneously, `registerTasks()` is called for each service +5. **Task Initialization**: The TaskManager initializes all registered tasks +6. **Execution**: Tasks run in the main loop when their intervals elapse + +### Framework Integration + +```cpp +void setup() { + spore.setup(); + + // Create service with dependencies + MyService* service = new MyService(spore.getContext(), spore.getTaskManager()); + + // Register service (endpoints and tasks will be registered when begin() is called) + spore.registerService(service); + + // This triggers registerEndpoints() and registerTasks() for all services + spore.begin(); +} +``` + +### Dynamic Service Addition + +Services can be added after the framework has started: + +```cpp +// Add service to running framework +MyService* newService = new MyService(spore.getContext(), spore.getTaskManager()); +spore.registerService(newService); // Immediately registers endpoints and tasks +``` + ## Task Registration Methods ### Using std::bind with Member Functions (Recommended) @@ -153,7 +218,62 @@ taskManager.registerTask("lambda_task", 2000, ## Adding Custom Tasks -### Method 1: Using std::bind (Recommended) +### Method 1: Service Interface (Recommended) + +1. **Create your service class implementing the Service interface**: + ```cpp + class SensorService : public Service { + public: + SensorService(NodeContext& ctx, TaskManager& taskManager) + : ctx(ctx), taskManager(taskManager) {} + + void registerEndpoints(ApiServer& api) override { + api.registerEndpoint("/api/sensor/status", HTTP_GET, + [this](AsyncWebServerRequest* request) { handleStatus(request); }); + } + + void registerTasks(TaskManager& taskManager) override { + taskManager.registerTask("temp_read", 1000, + [this]() { readTemperature(); }); + taskManager.registerTask("calibrate", 60000, + [this]() { calibrateSensors(); }); + } + + const char* getName() const override { return "SensorService"; } + + private: + NodeContext& ctx; + TaskManager& taskManager; + + void readTemperature() { + // Read sensor logic + Serial.println("Reading temperature"); + } + + void calibrateSensors() { + // Calibration logic + Serial.println("Calibrating sensors"); + } + + void handleStatus(AsyncWebServerRequest* request) { + // Handle status request + } + }; + ``` + +2. **Register with Spore framework**: + ```cpp + void setup() { + spore.setup(); + + SensorService* sensorService = new SensorService(spore.getContext(), spore.getTaskManager()); + spore.registerService(sensorService); + + spore.begin(); // This will call registerTasks() automatically + } + ``` + +### Method 2: Direct TaskManager Registration 1. **Create your service class**: ```cpp @@ -181,7 +301,7 @@ taskManager.registerTask("lambda_task", 2000, std::bind(&SensorService::calibrateSensors, &sensors)); ``` -### Method 2: Traditional Functions +### Method 3: Traditional Functions 1. **Define your task function**: ```cpp @@ -308,13 +428,55 @@ curl -X POST http://192.168.1.100/api/tasks/control \ ## 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 +1. **Use Service Interface**: Implement the Service interface for clean integration with the framework +2. **Group related tasks**: Register multiple related operations in a single service 3. **Monitor task health**: Use the status API to monitor task performance 4. **Plan intervals carefully**: Balance responsiveness with system resources 5. **Use descriptive names**: Make task names clear and meaningful +6. **Separate concerns**: Use registerEndpoints() for HTTP API and registerTasks() for background work +7. **Dependency injection**: Pass required dependencies (NodeContext, TaskManager) to service constructors -## Migration from Wrapper Functions +## Migration to Service Interface + +### Before (manual task registration in constructor): +```cpp +class MyService : public Service { +public: + MyService(TaskManager& taskManager) : taskManager(taskManager) { + // Tasks registered in constructor + taskManager.registerTask("heartbeat", 2000, [this]() { sendHeartbeat(); }); + } + + void registerEndpoints(ApiServer& api) override { + // Only endpoints registered here + } +}; +``` + +### After (using Service interface): +```cpp +class MyService : public Service { +public: + MyService(TaskManager& taskManager) : taskManager(taskManager) { + // No task registration in constructor + } + + void registerEndpoints(ApiServer& api) override { + // Register HTTP endpoints + api.registerEndpoint("/api/my/status", HTTP_GET, + [this](AsyncWebServerRequest* request) { handleStatus(request); }); + } + + void registerTasks(TaskManager& taskManager) override { + // Register background tasks + taskManager.registerTask("heartbeat", 2000, [this]() { sendHeartbeat(); }); + } + + const char* getName() const override { return "MyService"; } +}; +``` + +### Migration from Wrapper Functions ### Before (with wrapper functions): ```cpp @@ -335,11 +497,11 @@ taskManager.registerTask("cluster_listen", interval, ## 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 +- The new Service interface is fully backward compatible +- Existing code using direct TaskManager registration will continue to work +- You can mix Service interface and direct registration in the same project - All existing TaskManager methods remain unchanged -- New status monitoring methods are additive and don't break existing functionality +- The Service interface provides a cleaner, more organized approach for framework integration ## Related Documentation diff --git a/examples/multimatrix/MultiMatrixService.cpp b/examples/multimatrix/MultiMatrixService.cpp index cf34d9c..0d8e974 100644 --- a/examples/multimatrix/MultiMatrixService.cpp +++ b/examples/multimatrix/MultiMatrixService.cpp @@ -35,15 +35,14 @@ MultiMatrixService::MultiMatrixService(NodeContext& ctx, TaskManager& taskManage } else { LOG_ERROR("MultiMatrixService", "Failed to initialize DFPlayer"); } - registerTasks(); } void MultiMatrixService::registerEndpoints(ApiServer& api) { - api.addEndpoint(API_STATUS_ENDPOINT, HTTP_GET, + api.registerEndpoint(API_STATUS_ENDPOINT, HTTP_GET, [this](AsyncWebServerRequest* request) { handleStatusRequest(request); }, std::vector{}); - api.addEndpoint(API_CONTROL_ENDPOINT, HTTP_POST, + api.registerEndpoint(API_CONTROL_ENDPOINT, HTTP_POST, [this](AsyncWebServerRequest* request) { handleControlRequest(request); }, std::vector{ ParamSpec{String("action"), true, String("body"), String("string"), @@ -144,8 +143,8 @@ void MultiMatrixService::setLoop(bool enabled) { LOG_INFO("MultiMatrixService", String("Loop ") + (enabled ? "enabled" : "disabled")); } -void MultiMatrixService::registerTasks() { - m_taskManager.registerTask("multimatrix_potentiometer", POTENTIOMETER_SAMPLE_INTERVAL_MS, +void MultiMatrixService::registerTasks(TaskManager& taskManager) { + taskManager.registerTask("multimatrix_potentiometer", POTENTIOMETER_SAMPLE_INTERVAL_MS, [this]() { pollPotentiometer(); }); } diff --git a/examples/multimatrix/MultiMatrixService.h b/examples/multimatrix/MultiMatrixService.h index b673126..01bebca 100644 --- a/examples/multimatrix/MultiMatrixService.h +++ b/examples/multimatrix/MultiMatrixService.h @@ -14,6 +14,7 @@ class MultiMatrixService : public Service { public: MultiMatrixService(NodeContext& ctx, TaskManager& taskManager, uint8_t rxPin, uint8_t txPin, uint8_t potentiometerPin); void registerEndpoints(ApiServer& api) override; + void registerTasks(TaskManager& taskManager) override; const char* getName() const override { return "MultiMatrixAudio"; } bool isReady() const; @@ -34,7 +35,6 @@ private: static constexpr uint8_t MAX_VOLUME = 30; static constexpr uint8_t POT_VOLUME_EPSILON = 2; - void registerTasks(); void pollPotentiometer(); void handleStatusRequest(AsyncWebServerRequest* request); void handleControlRequest(AsyncWebServerRequest* request); diff --git a/examples/multimatrix/main.cpp b/examples/multimatrix/main.cpp index 2732964..fdb7482 100644 --- a/examples/multimatrix/main.cpp +++ b/examples/multimatrix/main.cpp @@ -42,7 +42,7 @@ void setup() { pixelController->begin(); audioService = std::make_shared(spore.getContext(), spore.getTaskManager(), MP3PLAYER_PIN_RX, MP3PLAYER_PIN_TX, POTENTIOMETER_PIN); - spore.addService(audioService); + spore.registerService(audioService); spore.begin(); diff --git a/examples/neopattern/NeoPatternService.cpp b/examples/neopattern/NeoPatternService.cpp index 2d17d0c..aa32fd9 100644 --- a/examples/neopattern/NeoPatternService.cpp +++ b/examples/neopattern/NeoPatternService.cpp @@ -34,7 +34,6 @@ NeoPatternService::NeoPatternService(NodeContext& ctx, TaskManager& taskMgr, con neoPattern->Direction = static_cast<::direction>(direction); registerPatterns(); - registerTasks(); registerEventHandlers(); initialized = true; @@ -49,17 +48,17 @@ NeoPatternService::~NeoPatternService() { void NeoPatternService::registerEndpoints(ApiServer& api) { // Status endpoint - api.addEndpoint("/api/neopattern/status", HTTP_GET, + api.registerEndpoint("/api/neopattern/status", HTTP_GET, [this](AsyncWebServerRequest* request) { handleStatusRequest(request); }, std::vector{}); // Patterns list endpoint - api.addEndpoint("/api/neopattern/patterns", HTTP_GET, + api.registerEndpoint("/api/neopattern/patterns", HTTP_GET, [this](AsyncWebServerRequest* request) { handlePatternsRequest(request); }, std::vector{}); // Control endpoint - api.addEndpoint("/api/neopattern", HTTP_POST, + api.registerEndpoint("/api/neopattern", HTTP_POST, [this](AsyncWebServerRequest* request) { handleControlRequest(request); }, std::vector{ ParamSpec{String("pattern"), false, String("body"), String("string"), patternNamesVector()}, @@ -73,7 +72,7 @@ void NeoPatternService::registerEndpoints(ApiServer& api) { }); // State endpoint for complex state updates - api.addEndpoint("/api/neopattern/state", HTTP_POST, + api.registerEndpoint("/api/neopattern/state", HTTP_POST, [this](AsyncWebServerRequest* request) { handleStateRequest(request); }, std::vector{}); } @@ -403,7 +402,7 @@ NeoPatternState NeoPatternService::getState() const { return currentState; } -void NeoPatternService::registerTasks() { +void NeoPatternService::registerTasks(TaskManager& taskManager) { taskManager.registerTask("neopattern_update", updateIntervalMs, [this]() { update(); }); taskManager.registerTask("neopattern_status_print", 10000, [this]() { LOG_INFO("NeoPattern", "Status update"); diff --git a/examples/neopattern/NeoPatternService.h b/examples/neopattern/NeoPatternService.h index b4e1a1b..2dca54e 100644 --- a/examples/neopattern/NeoPatternService.h +++ b/examples/neopattern/NeoPatternService.h @@ -30,6 +30,7 @@ public: ~NeoPatternService(); void registerEndpoints(ApiServer& api) override; + void registerTasks(TaskManager& taskManager) override; const char* getName() const override { return "NeoPattern"; } // Pattern control methods @@ -47,7 +48,6 @@ public: NeoPatternState getState() const; private: - void registerTasks(); void registerPatterns(); void update(); void registerEventHandlers(); diff --git a/examples/neopattern/main.cpp b/examples/neopattern/main.cpp index b942b11..6932562 100644 --- a/examples/neopattern/main.cpp +++ b/examples/neopattern/main.cpp @@ -46,7 +46,7 @@ void setup() { // Create and add custom service neoPatternService = new NeoPatternService(spore.getContext(), spore.getTaskManager(), config); - spore.addService(neoPatternService); + spore.registerService(neoPatternService); // Start the API server and complete initialization spore.begin(); diff --git a/examples/relay/README.md b/examples/relay/README.md index 6fc8ace..56cd2b1 100644 --- a/examples/relay/README.md +++ b/examples/relay/README.md @@ -33,7 +33,7 @@ void setup() { spore.setup(); relayService = new RelayService(spore.getContext(), spore.getTaskManager(), RELAY_PIN); - spore.addService(relayService); + spore.registerService(relayService); spore.begin(); } diff --git a/examples/relay/RelayService.cpp b/examples/relay/RelayService.cpp index 7d5890c..1c0605f 100644 --- a/examples/relay/RelayService.cpp +++ b/examples/relay/RelayService.cpp @@ -7,15 +7,14 @@ RelayService::RelayService(NodeContext& ctx, TaskManager& taskMgr, int pin) pinMode(relayPin, OUTPUT); // Many relay modules are active LOW. Start in OFF state (relay de-energized). digitalWrite(relayPin, HIGH); - registerTasks(); } void RelayService::registerEndpoints(ApiServer& api) { - api.addEndpoint("/api/relay/status", HTTP_GET, + api.registerEndpoint("/api/relay/status", HTTP_GET, [this](AsyncWebServerRequest* request) { handleStatusRequest(request); }, std::vector{}); - api.addEndpoint("/api/relay", HTTP_POST, + api.registerEndpoint("/api/relay", HTTP_POST, [this](AsyncWebServerRequest* request) { handleControlRequest(request); }, std::vector{ ParamSpec{String("state"), true, String("body"), String("string"), @@ -82,7 +81,7 @@ void RelayService::toggle() { } } -void RelayService::registerTasks() { +void RelayService::registerTasks(TaskManager& taskManager) { taskManager.registerTask("relay_status_print", 5000, [this]() { LOG_INFO("RelayService", "Status - pin: " + String(relayPin) + ", state: " + (relayOn ? "ON" : "OFF")); }); diff --git a/examples/relay/RelayService.h b/examples/relay/RelayService.h index 4b62848..7f5fcba 100644 --- a/examples/relay/RelayService.h +++ b/examples/relay/RelayService.h @@ -8,6 +8,7 @@ class RelayService : public Service { public: RelayService(NodeContext& ctx, TaskManager& taskMgr, int pin); void registerEndpoints(ApiServer& api) override; + void registerTasks(TaskManager& taskManager) override; const char* getName() const override { return "Relay"; } void turnOn(); @@ -15,8 +16,6 @@ public: void toggle(); private: - void registerTasks(); - NodeContext& ctx; TaskManager& taskManager; int relayPin; diff --git a/examples/relay/main.cpp b/examples/relay/main.cpp index 27f3a75..5c00bd5 100644 --- a/examples/relay/main.cpp +++ b/examples/relay/main.cpp @@ -23,7 +23,7 @@ void setup() { // Create and add custom service relayService = new RelayService(spore.getContext(), spore.getTaskManager(), RELAY_PIN); - spore.addService(relayService); + spore.registerService(relayService); // Start the API server and complete initialization spore.begin(); diff --git a/include/spore/Service.h b/include/spore/Service.h index 735f592..6911403 100644 --- a/include/spore/Service.h +++ b/include/spore/Service.h @@ -1,9 +1,12 @@ #pragma once #include "spore/core/ApiServer.h" +class TaskManager; + class Service { public: virtual ~Service() = default; virtual void registerEndpoints(ApiServer& api) = 0; + virtual void registerTasks(TaskManager& taskManager) = 0; virtual const char* getName() const = 0; }; diff --git a/include/spore/Spore.h b/include/spore/Spore.h index ea0f89e..55a732f 100644 --- a/include/spore/Spore.h +++ b/include/spore/Spore.h @@ -25,8 +25,8 @@ public: void loop(); // Service management - void addService(std::shared_ptr service); - void addService(Service* service); + void registerService(std::shared_ptr service); + void registerService(Service* service); // Access to core components NodeContext& getContext() { return ctx; } diff --git a/include/spore/core/ApiServer.h b/include/spore/core/ApiServer.h index dcf2eb0..f54c78c 100644 --- a/include/spore/core/ApiServer.h +++ b/include/spore/core/ApiServer.h @@ -19,14 +19,14 @@ class ApiServer { public: ApiServer(NodeContext& ctx, TaskManager& taskMgr, uint16_t port = 80); void begin(); - void addService(Service& service); - void addEndpoint(const String& uri, int method, std::function requestHandler); - void addEndpoint(const String& uri, int method, std::function requestHandler, + void registerService(Service& service); + void registerEndpoint(const String& uri, int method, std::function requestHandler); + void registerEndpoint(const String& uri, int method, std::function requestHandler, std::function uploadHandler); - void addEndpoint(const String& uri, int method, std::function requestHandler, + void registerEndpoint(const String& uri, int method, std::function requestHandler, const std::vector& params); - void addEndpoint(const String& uri, int method, std::function requestHandler, + void registerEndpoint(const String& uri, int method, std::function requestHandler, std::function uploadHandler, const std::vector& params); diff --git a/include/spore/services/ClusterService.h b/include/spore/services/ClusterService.h index 21b8a9e..249dd1e 100644 --- a/include/spore/services/ClusterService.h +++ b/include/spore/services/ClusterService.h @@ -7,6 +7,7 @@ class ClusterService : public Service { public: ClusterService(NodeContext& ctx); void registerEndpoints(ApiServer& api) override; + void registerTasks(TaskManager& taskManager) override; const char* getName() const override { return "Cluster"; } private: diff --git a/include/spore/services/MonitoringService.h b/include/spore/services/MonitoringService.h index 9da968f..8c2901a 100644 --- a/include/spore/services/MonitoringService.h +++ b/include/spore/services/MonitoringService.h @@ -7,6 +7,7 @@ class MonitoringService : public Service { public: MonitoringService(CpuUsage& cpuUsage); void registerEndpoints(ApiServer& api) override; + void registerTasks(TaskManager& taskManager) override; const char* getName() const override { return "Monitoring"; } // System resource information diff --git a/include/spore/services/NetworkService.h b/include/spore/services/NetworkService.h index 4c8973b..e5a2af1 100644 --- a/include/spore/services/NetworkService.h +++ b/include/spore/services/NetworkService.h @@ -7,6 +7,7 @@ class NetworkService : public Service { public: NetworkService(NetworkManager& networkManager); void registerEndpoints(ApiServer& api) override; + void registerTasks(TaskManager& taskManager) override; const char* getName() const override { return "Network"; } private: diff --git a/include/spore/services/NodeService.h b/include/spore/services/NodeService.h index daae61c..7829d6e 100644 --- a/include/spore/services/NodeService.h +++ b/include/spore/services/NodeService.h @@ -8,6 +8,7 @@ class NodeService : public Service { public: NodeService(NodeContext& ctx, ApiServer& apiServer); void registerEndpoints(ApiServer& api) override; + void registerTasks(TaskManager& taskManager) override; const char* getName() const override { return "Node"; } private: diff --git a/include/spore/services/StaticFileService.h b/include/spore/services/StaticFileService.h index ba77128..882c14c 100644 --- a/include/spore/services/StaticFileService.h +++ b/include/spore/services/StaticFileService.h @@ -9,6 +9,7 @@ class StaticFileService : public Service { public: StaticFileService(NodeContext& ctx, ApiServer& apiServer); void registerEndpoints(ApiServer& api) override; + void registerTasks(TaskManager& taskManager) override; const char* getName() const override { return name.c_str(); } private: diff --git a/include/spore/services/TaskService.h b/include/spore/services/TaskService.h index d5b6edb..9196e71 100644 --- a/include/spore/services/TaskService.h +++ b/include/spore/services/TaskService.h @@ -7,6 +7,7 @@ class TaskService : public Service { public: TaskService(TaskManager& taskManager); void registerEndpoints(ApiServer& api) override; + void registerTasks(TaskManager& taskManager) override; const char* getName() const override { return "Task"; } private: diff --git a/platformio.ini b/platformio.ini index 1c96bdd..fba0ac4 100644 --- a/platformio.ini +++ b/platformio.ini @@ -9,7 +9,7 @@ ; https://docs.platformio.org/page/projectconf.html [platformio] -default_envs = base +;default_envs = base src_dir = . data_dir = ${PROJECT_DIR}/examples/${PIOENV}/data diff --git a/src/spore/Spore.cpp b/src/spore/Spore.cpp index 85618d9..9fbfed1 100644 --- a/src/spore/Spore.cpp +++ b/src/spore/Spore.cpp @@ -85,7 +85,7 @@ void Spore::loop() { yield(); } -void Spore::addService(std::shared_ptr service) { +void Spore::registerService(std::shared_ptr service) { if (!service) { LOG_WARN("Spore", "Attempted to add null service"); return; @@ -95,21 +95,22 @@ void Spore::addService(std::shared_ptr service) { if (apiServerStarted) { // If API server is already started, register the service immediately - apiServer.addService(*service); - LOG_INFO("Spore", "Added service '" + String(service->getName()) + "' to running API server"); + apiServer.registerService(*service); + service->registerTasks(taskManager); + LOG_INFO("Spore", "Added service '" + String(service->getName()) + "' to running API server and task manager"); } else { LOG_INFO("Spore", "Registered service '" + String(service->getName()) + "' (will be added to API server when begin() is called)"); } } -void Spore::addService(Service* service) { +void Spore::registerService(Service* service) { if (!service) { LOG_WARN("Spore", "Attempted to add null service"); return; } // Wrap raw pointer in shared_ptr with no-op deleter to avoid double-delete - addService(std::shared_ptr(service, [](Service*){})); + registerService(std::shared_ptr(service, [](Service*){})); } @@ -155,11 +156,12 @@ void Spore::startApiServer() { LOG_INFO("Spore", "Starting API server..."); - // Register all services with API server + // Register all services with API server and task manager for (auto& service : services) { if (service) { - apiServer.addService(*service); - LOG_INFO("Spore", "Added service '" + String(service->getName()) + "' to API server"); + apiServer.registerService(*service); + service->registerTasks(taskManager); + LOG_INFO("Spore", "Added service '" + String(service->getName()) + "' to API server and task manager"); } } diff --git a/src/spore/core/ApiServer.cpp b/src/spore/core/ApiServer.cpp index ae95a4e..58148de 100644 --- a/src/spore/core/ApiServer.cpp +++ b/src/spore/core/ApiServer.cpp @@ -31,7 +31,7 @@ void ApiServer::registerEndpoint(const String& uri, int method, } } -void ApiServer::addEndpoint(const String& uri, int method, std::function requestHandler) { +void ApiServer::registerEndpoint(const String& uri, int method, std::function requestHandler) { // Get current service name if available String serviceName = "unknown"; if (!services.empty()) { @@ -41,7 +41,7 @@ void ApiServer::addEndpoint(const String& uri, int method, std::function requestHandler, +void ApiServer::registerEndpoint(const String& uri, int method, std::function requestHandler, std::function uploadHandler) { // Get current service name if available String serviceName = "unknown"; @@ -53,7 +53,7 @@ void ApiServer::addEndpoint(const String& uri, int method, std::function requestHandler, +void ApiServer::registerEndpoint(const String& uri, int method, std::function requestHandler, const std::vector& params) { // Get current service name if available String serviceName = "unknown"; @@ -64,7 +64,7 @@ void ApiServer::addEndpoint(const String& uri, int method, std::function requestHandler, +void ApiServer::registerEndpoint(const String& uri, int method, std::function requestHandler, std::function uploadHandler, const std::vector& params) { // Get current service name if available @@ -76,7 +76,7 @@ void ApiServer::addEndpoint(const String& uri, int method, std::function{}); // Generic cluster broadcast endpoint - api.addEndpoint("/api/cluster/event", HTTP_POST, + api.registerEndpoint("/api/cluster/event", HTTP_POST, [this](AsyncWebServerRequest* request) { if (!request->hasParam("event", true) || !request->hasParam("payload", true)) { request->send(400, "application/json", "{\"error\":\"Missing 'event' or 'payload'\"}"); @@ -32,6 +32,10 @@ void ClusterService::registerEndpoints(ApiServer& api) { }); } +void ClusterService::registerTasks(TaskManager& taskManager) { + // ClusterService doesn't register any tasks itself +} + void ClusterService::handleMembersRequest(AsyncWebServerRequest* request) { JsonDocument doc; JsonArray arr = doc["members"].to(); diff --git a/src/spore/services/MonitoringService.cpp b/src/spore/services/MonitoringService.cpp index a611456..53a62c2 100644 --- a/src/spore/services/MonitoringService.cpp +++ b/src/spore/services/MonitoringService.cpp @@ -10,11 +10,15 @@ MonitoringService::MonitoringService(CpuUsage& cpuUsage) } void MonitoringService::registerEndpoints(ApiServer& api) { - api.addEndpoint("/api/monitoring/resources", HTTP_GET, + api.registerEndpoint("/api/monitoring/resources", HTTP_GET, [this](AsyncWebServerRequest* request) { handleResourcesRequest(request); }, std::vector{}); } +void MonitoringService::registerTasks(TaskManager& taskManager) { + // MonitoringService doesn't register any tasks itself +} + MonitoringService::SystemResources MonitoringService::getSystemResources() const { SystemResources resources; diff --git a/src/spore/services/NetworkService.cpp b/src/spore/services/NetworkService.cpp index 1009f4f..333abc1 100644 --- a/src/spore/services/NetworkService.cpp +++ b/src/spore/services/NetworkService.cpp @@ -6,20 +6,20 @@ NetworkService::NetworkService(NetworkManager& networkManager) void NetworkService::registerEndpoints(ApiServer& api) { // WiFi scanning endpoints - api.addEndpoint("/api/network/wifi/scan", HTTP_POST, + api.registerEndpoint("/api/network/wifi/scan", HTTP_POST, [this](AsyncWebServerRequest* request) { handleWifiScanRequest(request); }, std::vector{}); - api.addEndpoint("/api/network/wifi/scan", HTTP_GET, + api.registerEndpoint("/api/network/wifi/scan", HTTP_GET, [this](AsyncWebServerRequest* request) { handleGetWifiNetworks(request); }, std::vector{}); // Network status and configuration endpoints - api.addEndpoint("/api/network/status", HTTP_GET, + api.registerEndpoint("/api/network/status", HTTP_GET, [this](AsyncWebServerRequest* request) { handleNetworkStatus(request); }, std::vector{}); - api.addEndpoint("/api/network/wifi/config", HTTP_POST, + api.registerEndpoint("/api/network/wifi/config", HTTP_POST, [this](AsyncWebServerRequest* request) { handleSetWifiConfig(request); }, std::vector{ ParamSpec{String("ssid"), true, String("body"), String("string"), {}, String("")}, @@ -29,6 +29,10 @@ void NetworkService::registerEndpoints(ApiServer& api) { }); } +void NetworkService::registerTasks(TaskManager& taskManager) { + // NetworkService doesn't register any tasks itself +} + void NetworkService::handleWifiScanRequest(AsyncWebServerRequest* request) { networkManager.scanWifi(); diff --git a/src/spore/services/NodeService.cpp b/src/spore/services/NodeService.cpp index ffbebc4..6af2650 100644 --- a/src/spore/services/NodeService.cpp +++ b/src/spore/services/NodeService.cpp @@ -6,12 +6,12 @@ NodeService::NodeService(NodeContext& ctx, ApiServer& apiServer) : ctx(ctx), api void NodeService::registerEndpoints(ApiServer& api) { // Status endpoint - api.addEndpoint("/api/node/status", HTTP_GET, + api.registerEndpoint("/api/node/status", HTTP_GET, [this](AsyncWebServerRequest* request) { handleStatusRequest(request); }, std::vector{}); // Update endpoint with file upload - api.addEndpoint("/api/node/update", HTTP_POST, + api.registerEndpoint("/api/node/update", HTTP_POST, [this](AsyncWebServerRequest* request) { handleUpdateRequest(request); }, [this](AsyncWebServerRequest* request, const String& filename, size_t index, uint8_t* data, size_t len, bool final) { handleUpdateUpload(request, filename, index, data, len, final); @@ -21,17 +21,17 @@ void NodeService::registerEndpoints(ApiServer& api) { }); // Restart endpoint - api.addEndpoint("/api/node/restart", HTTP_POST, + api.registerEndpoint("/api/node/restart", HTTP_POST, [this](AsyncWebServerRequest* request) { handleRestartRequest(request); }, std::vector{}); // Endpoints endpoint - api.addEndpoint("/api/node/endpoints", HTTP_GET, + api.registerEndpoint("/api/node/endpoints", HTTP_GET, [this](AsyncWebServerRequest* request) { handleEndpointsRequest(request); }, std::vector{}); // Generic local event endpoint - api.addEndpoint("/api/node/event", HTTP_POST, + api.registerEndpoint("/api/node/event", HTTP_POST, [this](AsyncWebServerRequest* request) { if (!request->hasParam("event", true) || !request->hasParam("payload", true)) { request->send(400, "application/json", "{\"error\":\"Missing 'event' or 'payload'\"}"); @@ -49,6 +49,10 @@ void NodeService::registerEndpoints(ApiServer& api) { }); } +void NodeService::registerTasks(TaskManager& taskManager) { + // NodeService doesn't register any tasks itself +} + void NodeService::handleStatusRequest(AsyncWebServerRequest* request) { JsonDocument doc; doc["freeHeap"] = ESP.getFreeHeap(); diff --git a/src/spore/services/StaticFileService.cpp b/src/spore/services/StaticFileService.cpp index dbb5298..5011b16 100644 --- a/src/spore/services/StaticFileService.cpp +++ b/src/spore/services/StaticFileService.cpp @@ -20,3 +20,7 @@ void StaticFileService::registerEndpoints(ApiServer& api) { api.serveStatic("/", LittleFS, "/public", "max-age=3600"); } +void StaticFileService::registerTasks(TaskManager& taskManager) { + // StaticFileService doesn't register any tasks itself +} + diff --git a/src/spore/services/TaskService.cpp b/src/spore/services/TaskService.cpp index c6db68e..55b8ba1 100644 --- a/src/spore/services/TaskService.cpp +++ b/src/spore/services/TaskService.cpp @@ -5,11 +5,11 @@ TaskService::TaskService(TaskManager& taskManager) : taskManager(taskManager) {} void TaskService::registerEndpoints(ApiServer& api) { - api.addEndpoint("/api/tasks/status", HTTP_GET, + api.registerEndpoint("/api/tasks/status", HTTP_GET, [this](AsyncWebServerRequest* request) { handleStatusRequest(request); }, std::vector{}); - api.addEndpoint("/api/tasks/control", HTTP_POST, + api.registerEndpoint("/api/tasks/control", HTTP_POST, [this](AsyncWebServerRequest* request) { handleControlRequest(request); }, std::vector{ ParamSpec{ @@ -31,6 +31,10 @@ void TaskService::registerEndpoints(ApiServer& api) { }); } +void TaskService::registerTasks(TaskManager& taskManager) { + // TaskService doesn't register any tasks itself - it manages other tasks +} + void TaskService::handleStatusRequest(AsyncWebServerRequest* request) { JsonDocument scratch; auto taskStatuses = taskManager.getAllTaskStatuses(scratch);