Merge pull request 'feat: implement Spore framework class as main orchestration layer' (#3) from feature/spore into main

Reviewed-on: #3
This commit is contained in:
2025-09-14 12:24:24 +02:00
40 changed files with 438 additions and 252 deletions

View File

@@ -41,6 +41,7 @@ SPORE is a cluster engine for ESP8266 microcontrollers that provides automatic n
SPORE uses a modular architecture with automatic node discovery, health monitoring, and distributed task management. SPORE uses a modular architecture with automatic node discovery, health monitoring, and distributed task management.
**Core Components:** **Core Components:**
- **Spore Framework**: Main framework class that orchestrates all components
- **Network Manager**: WiFi connection handling and hostname configuration - **Network Manager**: WiFi connection handling and hostname configuration
- **Cluster Manager**: Node discovery, member list management, and health monitoring - **Cluster Manager**: Node discovery, member list management, and health monitoring
- **API Server**: HTTP API server with dynamic endpoint registration - **API Server**: HTTP API server with dynamic endpoint registration
@@ -58,6 +59,50 @@ SPORE uses a modular architecture with automatic node discovery, health monitori
📖 **Detailed Architecture:** See [`docs/Architecture.md`](./docs/Architecture.md) for comprehensive system design and implementation details. 📖 **Detailed Architecture:** See [`docs/Architecture.md`](./docs/Architecture.md) for comprehensive system design and implementation details.
## Quick Start
The Spore framework provides a simple, unified interface for all core functionality:
```cpp
#include <Arduino.h>
#include "Spore.h"
// Create Spore instance with custom labels
Spore spore({
{"app", "my_app"},
{"role", "controller"}
});
void setup() {
spore.setup();
spore.begin();
}
void loop() {
spore.loop();
}
```
**Adding Custom Services:**
```cpp
void setup() {
spore.setup();
// Create and register custom services
RelayService* relayService = new RelayService(spore.getTaskManager(), 2);
spore.addService(relayService);
// Or using smart pointers
auto sensorService = std::make_shared<SensorService>();
spore.addService(sensorService);
// Start the API server and complete initialization
spore.begin();
}
```
**Examples:** See [`examples/base/`](./examples/base/) for basic usage and [`examples/relay/`](./examples/relay/) for custom service integration.
## API Reference ## API Reference
The system provides a comprehensive RESTful API for monitoring and controlling the embedded device. All endpoints return JSON responses and support standard HTTP status codes. The system provides a comprehensive RESTful API for monitoring and controlling the embedded device. All endpoints return JSON responses and support standard HTTP status codes.

View File

@@ -1,56 +1,21 @@
#include <Arduino.h> #include <Arduino.h>
#include <functional> #include "spore/Spore.h"
#include "Globals.h"
#include "NodeContext.h"
#include "NetworkManager.h"
#include "ClusterManager.h"
#include "ApiServer.h"
#include "TaskManager.h"
// Services // Create Spore instance with custom labels
#include "services/NodeService.h" Spore spore({
#include "services/NetworkService.h"
#include "services/ClusterService.h"
#include "services/TaskService.h"
using namespace std;
NodeContext ctx({
{"app", "base"}, {"app", "base"},
{"role", "demo"} {"role", "demo"}
}); });
NetworkManager network(ctx);
TaskManager taskManager(ctx);
ClusterManager cluster(ctx, taskManager);
ApiServer apiServer(ctx, taskManager, ctx.config.api_server_port);
// Create services
NodeService nodeService(ctx, apiServer);
NetworkService networkService(network);
ClusterService clusterService(ctx);
TaskService taskService(taskManager);
void setup() { void setup() {
Serial.begin(115200); // Initialize the Spore framework
spore.setup();
// Setup WiFi first // Start the API server and complete initialization
network.setupWiFi(); spore.begin();
// Initialize and start all tasks
taskManager.initialize();
// Register services and start API server
apiServer.addService(nodeService);
apiServer.addService(networkService);
apiServer.addService(clusterService);
apiServer.addService(taskService);
apiServer.begin();
// Print initial task status
taskManager.printTaskStatus();
} }
void loop() { void loop() {
taskManager.execute(); // Run the Spore framework loop
yield(); spore.loop();
} }

View File

@@ -1,5 +1,5 @@
#include "NeoPatternService.h" #include "NeoPatternService.h"
#include "ApiServer.h" #include "spore/core/ApiServer.h"
NeoPatternService::NeoPatternService(TaskManager& taskMgr, uint16_t numPixels, uint8_t pin, uint8_t type) NeoPatternService::NeoPatternService(TaskManager& taskMgr, uint16_t numPixels, uint8_t pin, uint8_t type)
: taskManager(taskMgr), : taskManager(taskMgr),

View File

@@ -1,6 +1,6 @@
#pragma once #pragma once
#include "services/Service.h" #include "spore/Service.h"
#include "TaskManager.h" #include "spore/core/TaskManager.h"
#include "NeoPattern.cpp" #include "NeoPattern.cpp"
#include <map> #include <map>
#include <vector> #include <vector>

View File

@@ -1,17 +1,5 @@
#include <Arduino.h> #include <Arduino.h>
#include <functional> #include "spore/Spore.h"
#include "Globals.h"
#include "NodeContext.h"
#include "NetworkManager.h"
#include "ClusterManager.h"
#include "ApiServer.h"
#include "TaskManager.h"
// Services
#include "services/NodeService.h"
#include "services/NetworkService.h"
#include "services/ClusterService.h"
#include "services/TaskService.h"
#include "NeoPatternService.h" #include "NeoPatternService.h"
#ifndef LED_STRIP_PIN #ifndef LED_STRIP_PIN
@@ -26,46 +14,32 @@
#define LED_STRIP_TYPE (NEO_GRB + NEO_KHZ800) #define LED_STRIP_TYPE (NEO_GRB + NEO_KHZ800)
#endif #endif
NodeContext ctx({ // Create Spore instance with custom labels
Spore spore({
{"app", "neopattern"}, {"app", "neopattern"},
{"device", "light"}, {"device", "light"},
{"pixels", String(LED_STRIP_LENGTH)}, {"pixels", String(LED_STRIP_LENGTH)},
{"pin", String(LED_STRIP_PIN)} {"pin", String(LED_STRIP_PIN)}
}); });
NetworkManager network(ctx);
TaskManager taskManager(ctx);
ClusterManager cluster(ctx, taskManager);
ApiServer apiServer(ctx, taskManager, ctx.config.api_server_port);
// Create services // Create custom service
NodeService nodeService(ctx, apiServer); NeoPatternService* neoPatternService = nullptr;
NetworkService networkService(network);
ClusterService clusterService(ctx);
TaskService taskService(taskManager);
NeoPatternService neoPatternService(taskManager, LED_STRIP_LENGTH, LED_STRIP_PIN, LED_STRIP_TYPE);
void setup() { void setup() {
Serial.begin(115200); // Initialize the Spore framework
spore.setup();
// Setup WiFi first // Create and add custom service
network.setupWiFi(); neoPatternService = new NeoPatternService(spore.getTaskManager(), LED_STRIP_LENGTH, LED_STRIP_PIN, LED_STRIP_TYPE);
spore.addService(neoPatternService);
// Initialize and start all tasks // Start the API server and complete initialization
taskManager.initialize(); spore.begin();
// Register services and start API server Serial.println("[Main] NeoPattern service registered and ready!");
apiServer.addService(nodeService);
apiServer.addService(networkService);
apiServer.addService(clusterService);
apiServer.addService(taskService);
apiServer.addService(neoPatternService);
apiServer.begin();
// Print initial task status
taskManager.printTaskStatus();
} }
void loop() { void loop() {
taskManager.execute(); // Run the Spore framework loop
yield(); spore.loop();
} }

View File

@@ -1,5 +1,5 @@
#include "NeoPixelService.h" #include "NeoPixelService.h"
#include "ApiServer.h" #include "spore/core/ApiServer.h"
// Wheel helper: map 0-255 to RGB rainbow // Wheel helper: map 0-255 to RGB rainbow
static uint32_t colorWheel(Adafruit_NeoPixel& strip, uint8_t pos) { static uint32_t colorWheel(Adafruit_NeoPixel& strip, uint8_t pos) {

View File

@@ -1,6 +1,6 @@
#pragma once #pragma once
#include "services/Service.h" #include "spore/Service.h"
#include "TaskManager.h" #include "spore/core/TaskManager.h"
#include <Adafruit_NeoPixel.h> #include <Adafruit_NeoPixel.h>
#include <map> #include <map>
#include <vector> #include <vector>

View File

@@ -1,17 +1,5 @@
#include <Arduino.h> #include <Arduino.h>
#include <functional> #include "spore/Spore.h"
#include "Globals.h"
#include "NodeContext.h"
#include "NetworkManager.h"
#include "ClusterManager.h"
#include "ApiServer.h"
#include "TaskManager.h"
// Services
#include "services/NodeService.h"
#include "services/NetworkService.h"
#include "services/ClusterService.h"
#include "services/TaskService.h"
#include "NeoPixelService.h" #include "NeoPixelService.h"
#ifndef NEOPIXEL_PIN #ifndef NEOPIXEL_PIN
@@ -26,46 +14,32 @@
#define NEOPIXEL_TYPE (NEO_GRB + NEO_KHZ800) #define NEOPIXEL_TYPE (NEO_GRB + NEO_KHZ800)
#endif #endif
NodeContext ctx({ // Create Spore instance with custom labels
Spore spore({
{"app", "neopixel"}, {"app", "neopixel"},
{"device", "light"}, {"device", "light"},
{"pixels", String(NEOPIXEL_COUNT)}, {"pixels", String(NEOPIXEL_COUNT)},
{"pin", String(NEOPIXEL_PIN)} {"pin", String(NEOPIXEL_PIN)}
}); });
NetworkManager network(ctx);
TaskManager taskManager(ctx);
ClusterManager cluster(ctx, taskManager);
ApiServer apiServer(ctx, taskManager, ctx.config.api_server_port);
// Create services // Create custom service
NodeService nodeService(ctx, apiServer); NeoPixelService* neoPixelService = nullptr;
NetworkService networkService(network);
ClusterService clusterService(ctx);
TaskService taskService(taskManager);
NeoPixelService neoPixelService(taskManager, NEOPIXEL_COUNT, NEOPIXEL_PIN, NEOPIXEL_TYPE);
void setup() { void setup() {
Serial.begin(115200); // Initialize the Spore framework
spore.setup();
// Setup WiFi first // Create and add custom service
network.setupWiFi(); neoPixelService = new NeoPixelService(spore.getTaskManager(), NEOPIXEL_COUNT, NEOPIXEL_PIN, NEOPIXEL_TYPE);
spore.addService(neoPixelService);
// Initialize and start all tasks // Start the API server and complete initialization
taskManager.initialize(); spore.begin();
// Register services and start API server Serial.println("[Main] NeoPixel service registered and ready!");
apiServer.addService(nodeService);
apiServer.addService(networkService);
apiServer.addService(clusterService);
apiServer.addService(taskService);
apiServer.addService(neoPixelService);
apiServer.begin();
// Print initial task status
taskManager.printTaskStatus();
} }
void loop() { void loop() {
taskManager.execute(); // Run the Spore framework loop
yield(); spore.loop();
} }

View File

@@ -1,10 +1,48 @@
# Relay Service Example # Relay Service Example
A minimal example that uses the framework's `NodeContext`, `NetworkManager`, `TaskManager`, and `ApiServer` to control a relay via REST and log status periodically as a task. A minimal example that demonstrates the Spore framework with a custom RelayService. The Spore framework automatically handles all core functionality (WiFi, clustering, API server, task management) while allowing easy registration of custom services.
- Default relay pin: `GPIO0` (ESP-01). Override with `-DRELAY_PIN=<pin>`. - Default relay pin: `GPIO0` (ESP-01). Override with `-DRELAY_PIN=<pin>`.
- WiFi and API port are configured in `src/Config.cpp`. - WiFi and API port are configured in `src/Config.cpp`.
## Spore Framework Usage
This example demonstrates the simplified Spore framework approach:
```cpp
#include <Arduino.h>
#include "Spore.h"
#include "RelayService.h"
Spore spore({
{"app", "relay"},
{"device", "actuator"},
{"pin", String(RELAY_PIN)}
});
RelayService* relayService = nullptr;
void setup() {
spore.setup();
relayService = new RelayService(spore.getTaskManager(), RELAY_PIN);
spore.addService(relayService);
spore.begin();
}
void loop() {
spore.loop();
}
```
The Spore framework automatically provides:
- WiFi connectivity management
- Cluster discovery and management
- REST API server with core endpoints
- Task scheduling and execution
- Node status monitoring
## Build & Upload ## Build & Upload
- ESP01S: - ESP01S:
@@ -32,17 +70,17 @@ curl http://192.168.1.50/api/relay/status
- Turn relay ON - Turn relay ON
```bash ```bash
curl -X POST http://192.168.1.50/api/relay/set -d state=on curl -X POST http://192.168.1.50/api/relay -d state=on
``` ```
- Turn relay OFF - Turn relay OFF
```bash ```bash
curl -X POST http://192.168.1.50/api/relay/set -d state=off curl -X POST http://192.168.1.50/api/relay -d state=off
``` ```
- Toggle relay - Toggle relay
```bash ```bash
curl -X POST http://192.168.1.50/api/relay/set -d state=toggle curl -X POST http://192.168.1.50/api/relay -d state=toggle
``` ```
Notes: Notes:

View File

@@ -1,5 +1,5 @@
#include "RelayService.h" #include "RelayService.h"
#include "ApiServer.h" #include "spore/core/ApiServer.h"
RelayService::RelayService(TaskManager& taskMgr, int pin) RelayService::RelayService(TaskManager& taskMgr, int pin)
: taskManager(taskMgr), relayPin(pin), relayOn(false) { : taskManager(taskMgr), relayPin(pin), relayOn(false) {

View File

@@ -1,6 +1,6 @@
#pragma once #pragma once
#include "services/Service.h" #include "spore/Service.h"
#include "TaskManager.h" #include "spore/core/TaskManager.h"
#include <ArduinoJson.h> #include <ArduinoJson.h>
class RelayService : public Service { class RelayService : public Service {

View File

@@ -1,65 +1,37 @@
#include <Arduino.h> #include <Arduino.h>
#include <functional> #include "spore/Spore.h"
#include "Globals.h"
#include "NodeContext.h"
#include "NetworkManager.h"
#include "ClusterManager.h"
#include "ApiServer.h"
#include "TaskManager.h"
// Services
#include "services/NodeService.h"
#include "services/NetworkService.h"
#include "services/ClusterService.h"
#include "services/TaskService.h"
#include "RelayService.h" #include "RelayService.h"
using namespace std;
// Choose a default relay pin. For ESP-01 this is GPIO0. Adjust as needed for your board. // Choose a default relay pin. For ESP-01 this is GPIO0. Adjust as needed for your board.
#ifndef RELAY_PIN #ifndef RELAY_PIN
#define RELAY_PIN 0 #define RELAY_PIN 0
#endif #endif
NodeContext ctx({ // Create Spore instance with custom labels
Spore spore({
{"app", "relay"}, {"app", "relay"},
{"device", "actuator"}, {"device", "actuator"},
{"pin", String(RELAY_PIN)} {"pin", String(RELAY_PIN)}
}); });
NetworkManager network(ctx);
TaskManager taskManager(ctx);
ClusterManager cluster(ctx, taskManager);
ApiServer apiServer(ctx, taskManager, ctx.config.api_server_port);
// Create services // Create custom service
NodeService nodeService(ctx, apiServer); RelayService* relayService = nullptr;
NetworkService networkService(network);
ClusterService clusterService(ctx);
TaskService taskService(taskManager);
RelayService relayService(taskManager, RELAY_PIN);
void setup() { void setup() {
Serial.begin(115200); // Initialize the Spore framework
spore.setup();
// Setup WiFi first // Create and add custom service
network.setupWiFi(); relayService = new RelayService(spore.getTaskManager(), RELAY_PIN);
spore.addService(relayService);
// Initialize and start all tasks // Start the API server and complete initialization
taskManager.initialize(); spore.begin();
// Register services and start API server Serial.println("[Main] Relay service registered and ready!");
apiServer.addService(nodeService);
apiServer.addService(networkService);
apiServer.addService(clusterService);
apiServer.addService(taskService);
apiServer.addService(relayService);
apiServer.begin();
// Print initial task status
taskManager.printTaskStatus();
} }
void loop() { void loop() {
taskManager.execute(); // Run the Spore framework loop
yield(); spore.loop();
} }

View File

@@ -1,5 +1,5 @@
#pragma once #pragma once
#include "ApiServer.h" #include "spore/core/ApiServer.h"
class Service { class Service {
public: public:

51
include/spore/Spore.h Normal file
View File

@@ -0,0 +1,51 @@
#pragma once
#include <vector>
#include <memory>
#include <initializer_list>
#include <utility>
#include "core/NodeContext.h"
#include "core/NetworkManager.h"
#include "core/ClusterManager.h"
#include "core/ApiServer.h"
#include "core/TaskManager.h"
#include "Service.h"
class Spore {
public:
Spore();
Spore(std::initializer_list<std::pair<String, String>> initialLabels);
~Spore();
// Core lifecycle methods
void setup();
void begin();
void loop();
// Service management
void addService(std::shared_ptr<Service> service);
void addService(Service* service);
// Access to core components
NodeContext& getContext() { return ctx; }
NetworkManager& getNetwork() { return network; }
TaskManager& getTaskManager() { return taskManager; }
ClusterManager& getCluster() { return cluster; }
ApiServer& getApiServer() { return apiServer; }
private:
void initializeCore();
void registerCoreServices();
void startApiServer();
NodeContext ctx;
NetworkManager network;
TaskManager taskManager;
ClusterManager cluster;
ApiServer apiServer;
std::vector<std::shared_ptr<Service>> services;
bool initialized;
bool apiServerStarted;
};

View File

@@ -7,10 +7,10 @@
#include <vector> #include <vector>
#include <tuple> #include <tuple>
#include "NodeContext.h" #include "spore/core/NodeContext.h"
#include "NodeInfo.h" #include "spore/types/NodeInfo.h"
#include "TaskManager.h" #include "spore/core/TaskManager.h"
#include "ApiTypes.h" #include "spore/types/ApiTypes.h"
class Service; // Forward declaration class Service; // Forward declaration

View File

@@ -1,8 +1,7 @@
#pragma once #pragma once
#include "Globals.h" #include "spore/core/NodeContext.h"
#include "NodeContext.h" #include "spore/types/NodeInfo.h"
#include "NodeInfo.h" #include "spore/core/TaskManager.h"
#include "TaskManager.h"
#include <Arduino.h> #include <Arduino.h>
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>

View File

@@ -1,5 +1,5 @@
#pragma once #pragma once
#include "NodeContext.h" #include "spore/core/NodeContext.h"
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <vector> #include <vector>

View File

@@ -2,12 +2,12 @@
#include <WiFiUdp.h> #include <WiFiUdp.h>
#include <map> #include <map>
#include "NodeInfo.h" #include "spore/types/NodeInfo.h"
#include <functional> #include <functional>
#include <string> #include <string>
#include <initializer_list> #include <initializer_list>
#include "Config.h" #include "spore/types/Config.h"
#include "ApiTypes.h" #include "spore/types/ApiTypes.h"
class NodeContext { class NodeContext {
public: public:

View File

@@ -4,7 +4,7 @@
#include <functional> #include <functional>
#include <string> #include <string>
#include <map> #include <map>
#include "NodeContext.h" #include "spore/core/NodeContext.h"
#include <ArduinoJson.h> #include <ArduinoJson.h>
// Define our own callback type to avoid conflict with TaskScheduler // Define our own callback type to avoid conflict with TaskScheduler

View File

@@ -1,6 +1,6 @@
#pragma once #pragma once
#include "services/Service.h" #include "spore/Service.h"
#include "NodeContext.h" #include "spore/core/NodeContext.h"
#include <ArduinoJson.h> #include <ArduinoJson.h>
class ClusterService : public Service { class ClusterService : public Service {

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
#include "services/Service.h" #include "spore/Service.h"
#include "NetworkManager.h" #include "spore/core/NetworkManager.h"
#include "NodeContext.h" #include "spore/core/NodeContext.h"
class NetworkService : public Service { class NetworkService : public Service {
public: public:

View File

@@ -1,6 +1,6 @@
#pragma once #pragma once
#include "services/Service.h" #include "spore/Service.h"
#include "NodeContext.h" #include "spore/core/NodeContext.h"
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <Updater.h> #include <Updater.h>

View File

@@ -1,6 +1,6 @@
#pragma once #pragma once
#include "services/Service.h" #include "spore/Service.h"
#include "TaskManager.h" #include "spore/core/TaskManager.h"
#include <ArduinoJson.h> #include <ArduinoJson.h>
class TaskService : public Service { class TaskService : public Service {

View File

@@ -1,5 +1,4 @@
#pragma once #pragma once
#include "Globals.h"
#include <IPAddress.h> #include <IPAddress.h>
#include <vector> #include <vector>
#include <tuple> #include <tuple>

View File

@@ -9,7 +9,7 @@
; https://docs.platformio.org/page/projectconf.html ; https://docs.platformio.org/page/projectconf.html
[platformio] [platformio]
default_envs = esp01_1m ;default_envs = esp01_1m
src_dir = . src_dir = .
[common] [common]
@@ -30,9 +30,11 @@ board_build.flash_size = 1M
lib_deps = ${common.lib_deps} lib_deps = ${common.lib_deps}
build_src_filter = build_src_filter =
+<examples/base/*.cpp> +<examples/base/*.cpp>
+<src/*.c> +<src/spore/*.cpp>
+<src/*.cpp> +<src/spore/core/*.cpp>
+<src/services/*.cpp> +<src/spore/services/*.cpp>
+<src/spore/types/*.cpp>
+<src/internal/*.cpp>
[env:d1_mini] [env:d1_mini]
platform = platformio/espressif8266@^4.2.1 platform = platformio/espressif8266@^4.2.1
@@ -45,9 +47,11 @@ board_build.flash_size = 4M
lib_deps = ${common.lib_deps} lib_deps = ${common.lib_deps}
build_src_filter = build_src_filter =
+<examples/base/*.cpp> +<examples/base/*.cpp>
+<src/*.c> +<src/spore/*.cpp>
+<src/*.cpp> +<src/spore/core/*.cpp>
+<src/services/*.cpp> +<src/spore/services/*.cpp>
+<src/spore/types/*.cpp>
+<src/internal/*.cpp>
[env:esp01_1m_relay] [env:esp01_1m_relay]
platform = platformio/espressif8266@^4.2.1 platform = platformio/espressif8266@^4.2.1
@@ -61,9 +65,11 @@ board_build.flash_size = 1M
lib_deps = ${common.lib_deps} lib_deps = ${common.lib_deps}
build_src_filter = build_src_filter =
+<examples/relay/*.cpp> +<examples/relay/*.cpp>
+<src/*.c> +<src/spore/*.cpp>
+<src/*.cpp> +<src/spore/core/*.cpp>
+<src/services/*.cpp> +<src/spore/services/*.cpp>
+<src/spore/types/*.cpp>
+<src/internal/*.cpp>
[env:esp01_1m_neopixel] [env:esp01_1m_neopixel]
platform = platformio/espressif8266@^4.2.1 platform = platformio/espressif8266@^4.2.1
@@ -78,9 +84,11 @@ lib_deps = ${common.lib_deps}
adafruit/Adafruit NeoPixel@^1.15.1 adafruit/Adafruit NeoPixel@^1.15.1
build_src_filter = build_src_filter =
+<examples/neopixel/*.cpp> +<examples/neopixel/*.cpp>
+<src/*.c> +<src/spore/*.cpp>
+<src/*.cpp> +<src/spore/core/*.cpp>
+<src/services/*.cpp> +<src/spore/services/*.cpp>
+<src/spore/types/*.cpp>
+<src/internal/*.cpp>
[env:d1_mini_neopixel] [env:d1_mini_neopixel]
platform = platformio/espressif8266@^4.2.1 platform = platformio/espressif8266@^4.2.1
@@ -94,9 +102,11 @@ lib_deps = ${common.lib_deps}
adafruit/Adafruit NeoPixel@^1.15.1 adafruit/Adafruit NeoPixel@^1.15.1
build_src_filter = build_src_filter =
+<examples/neopixel/*.cpp> +<examples/neopixel/*.cpp>
+<src/*.c> +<src/spore/*.cpp>
+<src/*.cpp> +<src/spore/core/*.cpp>
+<src/services/*.cpp> +<src/spore/services/*.cpp>
+<src/spore/types/*.cpp>
+<src/internal/*.cpp>
[env:esp01_1m_neopattern] [env:esp01_1m_neopattern]
platform = platformio/espressif8266@^4.2.1 platform = platformio/espressif8266@^4.2.1
@@ -112,9 +122,11 @@ lib_deps = ${common.lib_deps}
build_flags = -DLED_STRIP_PIN=2 build_flags = -DLED_STRIP_PIN=2
build_src_filter = build_src_filter =
+<examples/neopattern/*.cpp> +<examples/neopattern/*.cpp>
+<src/*.c> +<src/spore/*.cpp>
+<src/*.cpp> +<src/spore/core/*.cpp>
+<src/services/*.cpp> +<src/spore/services/*.cpp>
+<src/spore/types/*.cpp>
+<src/internal/*.cpp>
[env:d1_mini_neopattern] [env:d1_mini_neopattern]
platform = platformio/espressif8266@^4.2.1 platform = platformio/espressif8266@^4.2.1
@@ -128,6 +140,8 @@ lib_deps = ${common.lib_deps}
adafruit/Adafruit NeoPixel@^1.15.1 adafruit/Adafruit NeoPixel@^1.15.1
build_src_filter = build_src_filter =
+<examples/neopattern/*.cpp> +<examples/neopattern/*.cpp>
+<src/*.c> +<src/spore/*.cpp>
+<src/*.cpp> +<src/spore/core/*.cpp>
+<src/services/*.cpp> +<src/spore/services/*.cpp>
+<src/spore/types/*.cpp>
+<src/internal/*.cpp>

153
src/spore/Spore.cpp Normal file
View File

@@ -0,0 +1,153 @@
#include "spore/Spore.h"
#include "spore/services/NodeService.h"
#include "spore/services/NetworkService.h"
#include "spore/services/ClusterService.h"
#include "spore/services/TaskService.h"
#include <Arduino.h>
Spore::Spore() : ctx(), network(ctx), taskManager(ctx), cluster(ctx, taskManager),
apiServer(ctx, taskManager, ctx.config.api_server_port),
initialized(false), apiServerStarted(false) {
}
Spore::Spore(std::initializer_list<std::pair<String, String>> initialLabels)
: ctx(initialLabels), network(ctx), taskManager(ctx), cluster(ctx, taskManager),
apiServer(ctx, taskManager, ctx.config.api_server_port),
initialized(false), apiServerStarted(false) {
}
Spore::~Spore() {
// Services will be automatically cleaned up by shared_ptr
}
void Spore::setup() {
if (initialized) {
Serial.println("[Spore] Already initialized, skipping setup");
return;
}
Serial.begin(115200);
Serial.println("[Spore] Starting Spore framework...");
// Initialize core components
initializeCore();
// Register core services
registerCoreServices();
initialized = true;
Serial.println("[Spore] Framework setup complete - call begin() to start API server");
}
void Spore::begin() {
if (!initialized) {
Serial.println("[Spore] Framework not initialized, call setup() first");
return;
}
if (apiServerStarted) {
Serial.println("[Spore] API server already started");
return;
}
Serial.println("[Spore] Starting API server...");
// Start API server
startApiServer();
// Print initial task status
taskManager.printTaskStatus();
Serial.println("[Spore] Framework ready!");
}
void Spore::loop() {
if (!initialized) {
Serial.println("[Spore] Framework not initialized, call setup() first");
return;
}
taskManager.execute();
yield();
}
void Spore::addService(std::shared_ptr<Service> service) {
if (!service) {
Serial.println("[Spore] Warning: Attempted to add null service");
return;
}
services.push_back(service);
if (apiServerStarted) {
// If API server is already started, register the service immediately
apiServer.addService(*service);
Serial.printf("[Spore] Added service '%s' to running API server\n", service->getName());
} else {
Serial.printf("[Spore] Registered service '%s' (will be added to API server when begin() is called)\n", service->getName());
}
}
void Spore::addService(Service* service) {
if (!service) {
Serial.println("[Spore] Warning: 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, [](Service*){}));
}
void Spore::initializeCore() {
Serial.println("[Spore] Initializing core components...");
// Setup WiFi first
network.setupWiFi();
// Initialize task manager
taskManager.initialize();
Serial.println("[Spore] Core components initialized");
}
void Spore::registerCoreServices() {
Serial.println("[Spore] Registering core services...");
// Create core services
auto nodeService = std::make_shared<NodeService>(ctx, apiServer);
auto networkService = std::make_shared<NetworkService>(network);
auto clusterService = std::make_shared<ClusterService>(ctx);
auto taskService = std::make_shared<TaskService>(taskManager);
// Add to services list
services.push_back(nodeService);
services.push_back(networkService);
services.push_back(clusterService);
services.push_back(taskService);
Serial.println("[Spore] Core services registered");
}
void Spore::startApiServer() {
if (apiServerStarted) {
Serial.println("[Spore] API server already started");
return;
}
Serial.println("[Spore] Starting API server...");
// Register all services with API server
for (auto& service : services) {
if (service) {
apiServer.addService(*service);
Serial.printf("[Spore] Added service '%s' to API server\n", service->getName());
}
}
// Start the API server
apiServer.begin();
apiServerStarted = true;
Serial.println("[Spore] API server started");
}

View File

@@ -1,5 +1,5 @@
#include "ApiServer.h" #include "spore/core/ApiServer.h"
#include "services/Service.h" #include "spore/Service.h"
#include <algorithm> #include <algorithm>
const char* ApiServer::methodToStr(int method) { const char* ApiServer::methodToStr(int method) {

View File

@@ -1,4 +1,5 @@
#include "ClusterManager.h" #include "spore/core/ClusterManager.h"
#include "spore/internal/Globals.h"
ClusterManager::ClusterManager(NodeContext& ctx, TaskManager& taskMgr) : ctx(ctx), taskManager(taskMgr) { ClusterManager::ClusterManager(NodeContext& ctx, TaskManager& taskMgr) : ctx(ctx), taskManager(taskMgr) {
// Register callback for node_discovered event // Register callback for node_discovered event

View File

@@ -1,4 +1,4 @@
#include "NetworkManager.h" #include "spore/core/NetworkManager.h"
// SSID and password are now configured via Config class // SSID and password are now configured via Config class

View File

@@ -1,4 +1,4 @@
#include "NodeContext.h" #include "spore/core/NodeContext.h"
NodeContext::NodeContext() { NodeContext::NodeContext() {
udp = new WiFiUDP(); udp = new WiFiUDP();

View File

@@ -1,4 +1,4 @@
#include "TaskManager.h" #include "spore/core/TaskManager.h"
#include <Arduino.h> #include <Arduino.h>
TaskManager::TaskManager(NodeContext& ctx) : ctx(ctx) {} TaskManager::TaskManager(NodeContext& ctx) : ctx(ctx) {}

View File

@@ -1,5 +1,5 @@
#include "services/ClusterService.h" #include "spore/services/ClusterService.h"
#include "ApiServer.h" #include "spore/core/ApiServer.h"
ClusterService::ClusterService(NodeContext& ctx) : ctx(ctx) {} ClusterService::ClusterService(NodeContext& ctx) : ctx(ctx) {}

View File

@@ -1,4 +1,4 @@
#include "services/NetworkService.h" #include "spore/services/NetworkService.h"
#include <ArduinoJson.h> #include <ArduinoJson.h>
NetworkService::NetworkService(NetworkManager& networkManager) NetworkService::NetworkService(NetworkManager& networkManager)

View File

@@ -1,5 +1,5 @@
#include "services/NodeService.h" #include "spore/services/NodeService.h"
#include "ApiServer.h" #include "spore/core/ApiServer.h"
NodeService::NodeService(NodeContext& ctx, ApiServer& apiServer) : ctx(ctx), apiServer(apiServer) {} NodeService::NodeService(NodeContext& ctx, ApiServer& apiServer) : ctx(ctx), apiServer(apiServer) {}

View File

@@ -1,5 +1,5 @@
#include "services/TaskService.h" #include "spore/services/TaskService.h"
#include "ApiServer.h" #include "spore/core/ApiServer.h"
#include <algorithm> #include <algorithm>
TaskService::TaskService(TaskManager& taskManager) : taskManager(taskManager) {} TaskService::TaskService(TaskManager& taskManager) : taskManager(taskManager) {}

View File

@@ -1,4 +1,4 @@
#include "Config.h" #include "spore/types/Config.h"
Config::Config() { Config::Config() {
// WiFi Configuration // WiFi Configuration

View File

@@ -1,4 +1,5 @@
#include "NodeInfo.h" #include "spore/types/NodeInfo.h"
#include "spore/internal/Globals.h"
const char* statusToStr(NodeInfo::Status status) { const char* statusToStr(NodeInfo::Status status) {
switch (status) { switch (status) {