refactor: reorganize project structure with modern C++ namespace organization
- Restructure include/ and src/ directories with logical grouping - Move core components to spore/core/ (NodeContext, NetworkManager, TaskManager, ClusterManager, ApiServer) - Move services to spore/services/ (NodeService, NetworkService, ClusterService, TaskService) - Move types to spore/types/ (NodeInfo, ApiTypes, Config) - Move internal components to spore/internal/ (Globals) - Update all #include statements to use new namespace paths - Update platformio.ini build filters for all environments - Update all example files to use new include paths - Maintain backward compatibility for public API - Improve code organization, maintainability, and scalability This reorganization follows modern C++ project structure patterns and provides clear separation between public API, internal implementation, and utilities. All examples compile successfully with the new structure.
This commit is contained in:
9
include/spore/Service.h
Normal file
9
include/spore/Service.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include "spore/core/ApiServer.h"
|
||||
|
||||
class Service {
|
||||
public:
|
||||
virtual ~Service() = default;
|
||||
virtual void registerEndpoints(ApiServer& api) = 0;
|
||||
virtual const char* getName() const = 0;
|
||||
};
|
||||
51
include/spore/Spore.h
Normal file
51
include/spore/Spore.h
Normal 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;
|
||||
};
|
||||
48
include/spore/core/ApiServer.h
Normal file
48
include/spore/core/ApiServer.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <Updater.h>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
|
||||
#include "spore/core/NodeContext.h"
|
||||
#include "spore/types/NodeInfo.h"
|
||||
#include "spore/core/TaskManager.h"
|
||||
#include "spore/types/ApiTypes.h"
|
||||
|
||||
class Service; // Forward declaration
|
||||
|
||||
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<void(AsyncWebServerRequest*)> requestHandler);
|
||||
void addEndpoint(const String& uri, int method, std::function<void(AsyncWebServerRequest*)> requestHandler,
|
||||
std::function<void(AsyncWebServerRequest*, const String&, size_t, uint8_t*, size_t, bool)> uploadHandler);
|
||||
|
||||
void addEndpoint(const String& uri, int method, std::function<void(AsyncWebServerRequest*)> requestHandler,
|
||||
const std::vector<ParamSpec>& params);
|
||||
void addEndpoint(const String& uri, int method, std::function<void(AsyncWebServerRequest*)> requestHandler,
|
||||
std::function<void(AsyncWebServerRequest*, const String&, size_t, uint8_t*, size_t, bool)> uploadHandler,
|
||||
const std::vector<ParamSpec>& params);
|
||||
|
||||
static const char* methodToStr(int method);
|
||||
|
||||
// Access to endpoints for endpoints endpoint
|
||||
const std::vector<EndpointInfo>& getEndpoints() const { return endpoints; }
|
||||
|
||||
private:
|
||||
AsyncWebServer server;
|
||||
NodeContext& ctx;
|
||||
TaskManager& taskManager;
|
||||
std::vector<std::reference_wrapper<Service>> services;
|
||||
std::vector<EndpointInfo> endpoints; // Single source of truth for endpoints
|
||||
|
||||
// Internal helpers
|
||||
void registerEndpoint(const String& uri, int method,
|
||||
const std::vector<ParamSpec>& params,
|
||||
const String& serviceName);
|
||||
};
|
||||
29
include/spore/core/ClusterManager.h
Normal file
29
include/spore/core/ClusterManager.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include "spore/core/NodeContext.h"
|
||||
#include "spore/types/NodeInfo.h"
|
||||
#include "spore/core/TaskManager.h"
|
||||
#include <Arduino.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESP8266HTTPClient.h>
|
||||
#include <map>
|
||||
|
||||
class ClusterManager {
|
||||
public:
|
||||
ClusterManager(NodeContext& ctx, TaskManager& taskMgr);
|
||||
void registerTasks();
|
||||
void sendDiscovery();
|
||||
void listenForDiscovery();
|
||||
void addOrUpdateNode(const String& nodeHost, IPAddress nodeIP);
|
||||
void updateAllNodeStatuses();
|
||||
void removeDeadNodes();
|
||||
void printMemberList();
|
||||
const std::map<String, NodeInfo>& getMemberList() const { return *ctx.memberList; }
|
||||
void fetchNodeInfo(const IPAddress& ip);
|
||||
void updateLocalNodeResources();
|
||||
void heartbeatTaskCallback();
|
||||
void updateAllMembersInfoTaskCallback();
|
||||
private:
|
||||
NodeContext& ctx;
|
||||
TaskManager& taskManager;
|
||||
};
|
||||
49
include/spore/core/NetworkManager.h
Normal file
49
include/spore/core/NetworkManager.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include "spore/core/NodeContext.h"
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <vector>
|
||||
|
||||
struct AccessPoint {
|
||||
String ssid;
|
||||
int32_t rssi;
|
||||
uint8_t encryptionType;
|
||||
uint8_t* bssid;
|
||||
int32_t channel;
|
||||
bool isHidden;
|
||||
};
|
||||
|
||||
class NetworkManager {
|
||||
public:
|
||||
NetworkManager(NodeContext& ctx);
|
||||
void setupWiFi();
|
||||
void setHostnameFromMac();
|
||||
|
||||
// WiFi scanning methods
|
||||
void scanWifi();
|
||||
void processAccessPoints();
|
||||
std::vector<AccessPoint> getAccessPoints() const;
|
||||
|
||||
// WiFi configuration methods
|
||||
void setWiFiConfig(const String& ssid, const String& password,
|
||||
uint32_t connect_timeout_ms = 10000,
|
||||
uint32_t retry_delay_ms = 500);
|
||||
|
||||
// Network status methods
|
||||
bool isConnected() const { return WiFi.isConnected(); }
|
||||
String getSSID() const { return WiFi.SSID(); }
|
||||
IPAddress getLocalIP() const { return WiFi.localIP(); }
|
||||
String getMacAddress() const { return WiFi.macAddress(); }
|
||||
String getHostname() const { return WiFi.hostname(); }
|
||||
int32_t getRSSI() const { return WiFi.RSSI(); }
|
||||
WiFiMode_t getMode() const { return WiFi.getMode(); }
|
||||
|
||||
// AP mode specific methods
|
||||
IPAddress getAPIP() const { return WiFi.softAPIP(); }
|
||||
String getAPMacAddress() const { return WiFi.softAPmacAddress(); }
|
||||
uint8_t getConnectedStations() const { return WiFi.softAPgetStationNum(); }
|
||||
|
||||
private:
|
||||
NodeContext& ctx;
|
||||
std::vector<AccessPoint> accessPoints;
|
||||
bool isScanning = false;
|
||||
};
|
||||
29
include/spore/core/NodeContext.h
Normal file
29
include/spore/core/NodeContext.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <WiFiUdp.h>
|
||||
#include <map>
|
||||
#include "spore/types/NodeInfo.h"
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <initializer_list>
|
||||
#include "spore/types/Config.h"
|
||||
#include "spore/types/ApiTypes.h"
|
||||
|
||||
class NodeContext {
|
||||
public:
|
||||
NodeContext();
|
||||
NodeContext(std::initializer_list<std::pair<String, String>> initialLabels);
|
||||
~NodeContext();
|
||||
WiFiUDP* udp;
|
||||
String hostname;
|
||||
IPAddress localIP;
|
||||
NodeInfo self;
|
||||
std::map<String, NodeInfo>* memberList;
|
||||
Config config;
|
||||
|
||||
using EventCallback = std::function<void(void*)>;
|
||||
std::map<std::string, std::vector<EventCallback>> eventRegistry;
|
||||
|
||||
void on(const std::string& event, EventCallback cb);
|
||||
void fire(const std::string& event, void* data);
|
||||
};
|
||||
63
include/spore/core/TaskManager.h
Normal file
63
include/spore/core/TaskManager.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "spore/core/NodeContext.h"
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
// Define our own callback type to avoid conflict with TaskScheduler
|
||||
using TaskFunction = std::function<void()>;
|
||||
|
||||
struct TaskDefinition {
|
||||
std::string name;
|
||||
unsigned long interval;
|
||||
TaskFunction callback;
|
||||
bool enabled;
|
||||
bool autoStart;
|
||||
|
||||
TaskDefinition(const std::string& n, unsigned long intv, TaskFunction cb, bool en = true, bool autoS = true)
|
||||
: name(n), interval(intv), callback(cb), enabled(en), autoStart(autoS) {}
|
||||
};
|
||||
|
||||
class TaskManager {
|
||||
public:
|
||||
TaskManager(NodeContext& ctx);
|
||||
~TaskManager();
|
||||
|
||||
// Task registration methods
|
||||
void registerTask(const std::string& name, unsigned long interval, TaskFunction callback, bool enabled = true, bool autoStart = true);
|
||||
void registerTask(const TaskDefinition& taskDef);
|
||||
|
||||
// Task control methods
|
||||
void enableTask(const std::string& name);
|
||||
void disableTask(const std::string& name);
|
||||
void setTaskInterval(const std::string& name, unsigned long interval);
|
||||
void startTask(const std::string& name);
|
||||
void stopTask(const std::string& name);
|
||||
|
||||
// Task status methods
|
||||
bool isTaskEnabled(const std::string& name) const;
|
||||
bool isTaskRunning(const std::string& name) const;
|
||||
unsigned long getTaskInterval(const std::string& name) const;
|
||||
|
||||
// Get comprehensive task status information
|
||||
std::vector<std::pair<std::string, JsonObject>> getAllTaskStatuses(JsonDocument& doc) const;
|
||||
|
||||
// Management methods
|
||||
void initialize();
|
||||
void enableAllTasks();
|
||||
void disableAllTasks();
|
||||
void printTaskStatus() const;
|
||||
|
||||
// Task execution
|
||||
void execute();
|
||||
|
||||
private:
|
||||
NodeContext& ctx;
|
||||
std::vector<TaskDefinition> taskDefinitions;
|
||||
std::vector<unsigned long> lastExecutionTimes;
|
||||
|
||||
int findTaskIndex(const std::string& name) const;
|
||||
};
|
||||
27
include/spore/internal/Globals.h
Normal file
27
include/spore/internal/Globals.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
// Cluster protocol and API constants
|
||||
namespace ClusterProtocol {
|
||||
constexpr const char* DISCOVERY_MSG = "CLUSTER_DISCOVERY";
|
||||
constexpr const char* RESPONSE_MSG = "CLUSTER_RESPONSE";
|
||||
constexpr uint16_t UDP_PORT = 4210;
|
||||
constexpr size_t UDP_BUF_SIZE = 64;
|
||||
constexpr const char* API_NODE_STATUS = "/api/node/status";
|
||||
}
|
||||
|
||||
namespace TaskIntervals {
|
||||
constexpr unsigned long SEND_DISCOVERY = 1000;
|
||||
constexpr unsigned long LISTEN_FOR_DISCOVERY = 100;
|
||||
constexpr unsigned long UPDATE_STATUS = 1000;
|
||||
constexpr unsigned long PRINT_MEMBER_LIST = 5000;
|
||||
constexpr unsigned long HEARTBEAT = 2000;
|
||||
constexpr unsigned long UPDATE_ALL_MEMBERS_INFO = 10000;
|
||||
}
|
||||
|
||||
constexpr unsigned long NODE_ACTIVE_THRESHOLD = 10000;
|
||||
constexpr unsigned long NODE_INACTIVE_THRESHOLD = 60000;
|
||||
constexpr unsigned long NODE_DEAD_THRESHOLD = 120000;
|
||||
|
||||
16
include/spore/services/ClusterService.h
Normal file
16
include/spore/services/ClusterService.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include "spore/Service.h"
|
||||
#include "spore/core/NodeContext.h"
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
class ClusterService : public Service {
|
||||
public:
|
||||
ClusterService(NodeContext& ctx);
|
||||
void registerEndpoints(ApiServer& api) override;
|
||||
const char* getName() const override { return "Cluster"; }
|
||||
|
||||
private:
|
||||
NodeContext& ctx;
|
||||
|
||||
void handleMembersRequest(AsyncWebServerRequest* request);
|
||||
};
|
||||
22
include/spore/services/NetworkService.h
Normal file
22
include/spore/services/NetworkService.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "spore/Service.h"
|
||||
#include "spore/core/NetworkManager.h"
|
||||
#include "spore/core/NodeContext.h"
|
||||
|
||||
class NetworkService : public Service {
|
||||
public:
|
||||
NetworkService(NetworkManager& networkManager);
|
||||
void registerEndpoints(ApiServer& api) override;
|
||||
const char* getName() const override { return "Network"; }
|
||||
|
||||
private:
|
||||
NetworkManager& networkManager;
|
||||
|
||||
// WiFi scanning endpoints
|
||||
void handleWifiScanRequest(AsyncWebServerRequest* request);
|
||||
void handleGetWifiNetworks(AsyncWebServerRequest* request);
|
||||
|
||||
// Network status endpoints
|
||||
void handleNetworkStatus(AsyncWebServerRequest* request);
|
||||
void handleSetWifiConfig(AsyncWebServerRequest* request);
|
||||
};
|
||||
22
include/spore/services/NodeService.h
Normal file
22
include/spore/services/NodeService.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "spore/Service.h"
|
||||
#include "spore/core/NodeContext.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include <Updater.h>
|
||||
|
||||
class NodeService : public Service {
|
||||
public:
|
||||
NodeService(NodeContext& ctx, ApiServer& apiServer);
|
||||
void registerEndpoints(ApiServer& api) override;
|
||||
const char* getName() const override { return "Node"; }
|
||||
|
||||
private:
|
||||
NodeContext& ctx;
|
||||
ApiServer& apiServer;
|
||||
|
||||
void handleStatusRequest(AsyncWebServerRequest* request);
|
||||
void handleUpdateRequest(AsyncWebServerRequest* request);
|
||||
void handleUpdateUpload(AsyncWebServerRequest* request, const String& filename, size_t index, uint8_t* data, size_t len, bool final);
|
||||
void handleRestartRequest(AsyncWebServerRequest* request);
|
||||
void handleEndpointsRequest(AsyncWebServerRequest* request);
|
||||
};
|
||||
17
include/spore/services/TaskService.h
Normal file
17
include/spore/services/TaskService.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include "spore/Service.h"
|
||||
#include "spore/core/TaskManager.h"
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
class TaskService : public Service {
|
||||
public:
|
||||
TaskService(TaskManager& taskManager);
|
||||
void registerEndpoints(ApiServer& api) override;
|
||||
const char* getName() const override { return "Task"; }
|
||||
|
||||
private:
|
||||
TaskManager& taskManager;
|
||||
|
||||
void handleStatusRequest(AsyncWebServerRequest* request);
|
||||
void handleControlRequest(AsyncWebServerRequest* request);
|
||||
};
|
||||
37
include/spore/types/ApiTypes.h
Normal file
37
include/spore/types/ApiTypes.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include <vector>
|
||||
|
||||
struct ParamSpec {
|
||||
String name;
|
||||
bool required;
|
||||
String location; // "query" | "body" | "path" | "header"
|
||||
String type; // e.g. "string", "number", "boolean"
|
||||
std::vector<String> values; // optional allowed values
|
||||
String defaultValue; // optional default value (stringified)
|
||||
};
|
||||
|
||||
struct EndpointCapability {
|
||||
String uri;
|
||||
int method;
|
||||
std::vector<ParamSpec> params;
|
||||
};
|
||||
|
||||
struct EndpointInfo {
|
||||
String uri;
|
||||
int method;
|
||||
std::vector<ParamSpec> params;
|
||||
String serviceName; // Name of the service that registered this endpoint
|
||||
bool isLocal; // Whether this endpoint is on the local node
|
||||
|
||||
// Constructor for individual parameters
|
||||
EndpointInfo(const String& u, int m, const std::vector<ParamSpec>& p, const String& service, bool local)
|
||||
: uri(u), method(m), params(p), serviceName(service), isLocal(local) {}
|
||||
|
||||
// Constructor for easy conversion from EndpointCapability
|
||||
EndpointInfo(const EndpointCapability& cap, const String& service = "", bool local = true)
|
||||
: uri(cap.uri), method(cap.method), params(cap.params), serviceName(service), isLocal(local) {}
|
||||
|
||||
// Default constructor
|
||||
EndpointInfo() : isLocal(true) {}
|
||||
};
|
||||
37
include/spore/types/Config.h
Normal file
37
include/spore/types/Config.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class Config {
|
||||
public:
|
||||
// WiFi Configuration
|
||||
String wifi_ssid;
|
||||
String wifi_password;
|
||||
|
||||
// Network Configuration
|
||||
uint16_t udp_port;
|
||||
uint16_t api_server_port;
|
||||
|
||||
// Cluster Configuration
|
||||
unsigned long discovery_interval_ms;
|
||||
unsigned long heartbeat_interval_ms;
|
||||
unsigned long status_update_interval_ms;
|
||||
unsigned long member_info_update_interval_ms;
|
||||
unsigned long print_interval_ms;
|
||||
|
||||
// Node Status Thresholds
|
||||
unsigned long node_active_threshold_ms;
|
||||
unsigned long node_inactive_threshold_ms;
|
||||
unsigned long node_dead_threshold_ms;
|
||||
|
||||
// WiFi Connection
|
||||
unsigned long wifi_connect_timeout_ms;
|
||||
unsigned long wifi_retry_delay_ms;
|
||||
|
||||
// System Configuration
|
||||
unsigned long restart_delay_ms;
|
||||
uint16_t json_doc_size;
|
||||
|
||||
// Constructor
|
||||
Config();
|
||||
};
|
||||
27
include/spore/types/NodeInfo.h
Normal file
27
include/spore/types/NodeInfo.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <IPAddress.h>
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
#include <map>
|
||||
#include "ApiTypes.h"
|
||||
|
||||
struct NodeInfo {
|
||||
String hostname;
|
||||
IPAddress ip;
|
||||
unsigned long lastSeen;
|
||||
enum Status { ACTIVE, INACTIVE, DEAD } status;
|
||||
struct Resources {
|
||||
uint32_t freeHeap = 0;
|
||||
uint32_t chipId = 0;
|
||||
String sdkVersion;
|
||||
uint32_t cpuFreqMHz = 0;
|
||||
uint32_t flashChipSize = 0;
|
||||
} resources;
|
||||
unsigned long latency = 0; // ms since lastSeen
|
||||
std::vector<EndpointInfo> endpoints; // List of registered endpoints
|
||||
std::map<String, String> labels; // Arbitrary node labels (key -> value)
|
||||
};
|
||||
|
||||
const char* statusToStr(NodeInfo::Status status);
|
||||
void updateNodeStatus(NodeInfo &node, unsigned long now, unsigned long inactive_threshold, unsigned long dead_threshold);
|
||||
void updateNodeStatus(NodeInfo &node, unsigned long now = millis()); // Legacy overload for backward compatibility
|
||||
Reference in New Issue
Block a user