diff --git a/README.md b/README.md index c0653b5..f5e04df 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,46 @@ # SPORE -SProcket ORchestration Engine +> SProcket ORchestration Engine + +SPORE is a simple cluster engine for ESP8266 microcontrollers. ## Features - WiFi STA / AP -- node auto discovery over UDP +- auto discovery over UDP - service registry - pub/sub event system - Over-The-Air updates ## Supported Hardware -- ESP8266 +- ESP-01 ## Architecture +### Components + +The core architecture consists for following components: + +- Network Manager: WiFi connection handling +- Cluster Manager: node discovery and memberlist management +- API Server: HTTP API for interacting with node and cluster +- Task Scheduler: internal scheduler used for system and user defined tasks + ### Auto Discovery -A node periodically executes 2 tasks responsible for auto discovers: +A node periodically executes 2 tasks responsible for auto discovery: - send discovery: send UDP packet on broadcast address to discover nodes - listen for discovery: receive UDP packets and send response back to the node who initiated discovery -Discovered nodes are added to the so cluster memberlist. -Another periodic task will then call the `/api/node/status` endpoint over HTTP on each node in the memberlist to get detailed informations about the node (e.g. freeHeap, available API endpoints). +Discovered nodes are added to the so clusters memberlist. +Another periodic task will then call the `/api/node/status` endpoint over HTTP on each node in the memberlist to get system resources and available API endpoints. + +### Event System + +The `NodeContext` implements an event system for publishing and subscribing to local and cluster wide events (TODO). +It is used internally for communication between different components and tasks. ## Develop diff --git a/src/RestApiServer.cpp b/src/ApiServer.cpp similarity index 77% rename from src/RestApiServer.cpp rename to src/ApiServer.cpp index 156ead2..341b2a8 100644 --- a/src/RestApiServer.cpp +++ b/src/ApiServer.cpp @@ -1,8 +1,8 @@ -#include "RestApiServer.h" +#include "ApiServer.h" -RestApiServer::RestApiServer(NodeContext& ctx, uint16_t port) : server(port), ctx(ctx) {} +ApiServer::ApiServer(NodeContext& ctx, uint16_t port) : server(port), ctx(ctx) {} -void RestApiServer::addEndpoint(const String& uri, int method, std::function requestHandler) { +void ApiServer::addEndpoint(const String& uri, int method, std::function requestHandler) { serviceRegistry.push_back(std::make_tuple(uri, method)); // Store in NodeInfo for local node if (ctx.memberList && !ctx.memberList->empty()) { @@ -11,7 +11,7 @@ void RestApiServer::addEndpoint(const String& uri, int method, std::function requestHandler, +void ApiServer::addEndpoint(const String& uri, int method, std::function requestHandler, std::function uploadHandler) { serviceRegistry.push_back(std::make_tuple(uri, method)); if (ctx.memberList && !ctx.memberList->empty()) { @@ -20,21 +20,21 @@ void RestApiServer::addEndpoint(const String& uri, int method, std::functionsend(200, "application/json", json); } -void RestApiServer::getClusterMembersJson(AsyncWebServerRequest *request) { +void ApiServer::onClusterMembersRequest(AsyncWebServerRequest *request) { JsonDocument doc; JsonArray arr = doc["members"].to(); for (const auto& node : *ctx.memberList) { @@ -79,7 +79,7 @@ void RestApiServer::getClusterMembersJson(AsyncWebServerRequest *request) { request->send(200, "application/json", json); } -void RestApiServer::methodToStr(const std::tuple &endpoint, ArduinoJson::V742PB22::JsonObject &apiObj) +void ApiServer::methodToStr(const std::tuple &endpoint, ArduinoJson::V742PB22::JsonObject &apiObj) { int method = std::get<1>(endpoint); const char *methodStr = nullptr; @@ -107,7 +107,7 @@ void RestApiServer::methodToStr(const std::tuple &endpoint, Arduino apiObj["method"] = methodStr; } -void RestApiServer::onFirmwareUpdateRequest(AsyncWebServerRequest *request) { +void ApiServer::onFirmwareUpdateRequest(AsyncWebServerRequest *request) { bool hasError = !Update.hasError(); AsyncWebServerResponse *response = request->beginResponse(200, "application/json", hasError ? "{\"status\": \"OK\"}" : "{\"status\": \"FAIL\"}"); response->addHeader("Connection", "close"); @@ -119,7 +119,7 @@ void RestApiServer::onFirmwareUpdateRequest(AsyncWebServerRequest *request) { }); } -void RestApiServer::onFirmwareUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) { +void ApiServer::onFirmwareUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) { if (!index) { Serial.print("[OTA] Update Start "); Serial.println(filename); @@ -157,7 +157,7 @@ void RestApiServer::onFirmwareUpload(AsyncWebServerRequest *request, const Strin return; } -void RestApiServer::onRestartRequest(AsyncWebServerRequest *request) { +void ApiServer::onRestartRequest(AsyncWebServerRequest *request) { AsyncWebServerResponse *response = request->beginResponse(200, "application/json", "{\"status\": \"restarting\"}"); response->addHeader("Connection", "close"); request->send(response); diff --git a/src/RestApiServer.h b/src/ApiServer.h similarity index 83% rename from src/RestApiServer.h rename to src/ApiServer.h index 1058ef2..1c73cd4 100644 --- a/src/RestApiServer.h +++ b/src/ApiServer.h @@ -10,14 +10,12 @@ #include "NodeContext.h" #include "NodeInfo.h" -#define DEBUG_UPDATER 1 - using namespace std; using namespace std::placeholders; -class RestApiServer { +class ApiServer { public: - RestApiServer(NodeContext& ctx, uint16_t port = 80); + ApiServer(NodeContext& ctx, uint16_t port = 80); void begin(); void addEndpoint(const String& uri, int method, std::function requestHandler); void addEndpoint(const String& uri, int method, std::function requestHandler, @@ -26,9 +24,9 @@ private: AsyncWebServer server; NodeContext& ctx; std::vector> serviceRegistry; - void getClusterMembersJson(AsyncWebServerRequest *request); + void onClusterMembersRequest(AsyncWebServerRequest *request); void methodToStr(const std::tuple &endpoint, ArduinoJson::V742PB22::JsonObject &apiObj); - void getSystemStatusJson(AsyncWebServerRequest *request); + void onSystemStatusRequest(AsyncWebServerRequest *request); void onFirmwareUpdateRequest(AsyncWebServerRequest *request); void onFirmwareUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final); void onRestartRequest(AsyncWebServerRequest *request); diff --git a/src/ClusterManager.cpp b/src/ClusterManager.cpp index 08b3653..b456b0e 100644 --- a/src/ClusterManager.cpp +++ b/src/ClusterManager.cpp @@ -2,7 +2,7 @@ ClusterManager::ClusterManager(NodeContext& ctx) : ctx(ctx) { // Register callback for node_discovered event - ctx.registerEvent("node_discovered", [this](void* data) { + ctx.on("node_discovered", [this](void* data) { NodeInfo* node = static_cast(data); this->addOrUpdateNode(node->hostname, node->ip); }); @@ -111,7 +111,7 @@ void ClusterManager::heartbeatTaskCallback() { node.lastSeen = millis(); node.status = NodeInfo::ACTIVE; updateLocalNodeResources(); - ctx.triggerEvent("node_discovered", &node); + ctx.fire("node_discovered", &node); break; } } diff --git a/src/NetworkManager.cpp b/src/NetworkManager.cpp index 8800f6b..be25db0 100644 --- a/src/NetworkManager.cpp +++ b/src/NetworkManager.cpp @@ -55,5 +55,5 @@ void NetworkManager::setupWiFi() { } self.lastSeen = millis(); self.status = NodeInfo::ACTIVE; - ctx.triggerEvent("node_discovered", &self); + ctx.fire("node_discovered", &self); } diff --git a/src/NodeContext..cpp b/src/NodeContext..cpp index 5f54d84..0149778 100644 --- a/src/NodeContext..cpp +++ b/src/NodeContext..cpp @@ -13,11 +13,11 @@ NodeContext::~NodeContext() { delete memberList; } -void NodeContext::registerEvent(const std::string& event, EventCallback cb) { +void NodeContext::on(const std::string& event, EventCallback cb) { eventRegistry[event].push_back(cb); } -void NodeContext::triggerEvent(const std::string& event, void* data) { +void NodeContext::fire(const std::string& event, void* data) { for (auto& cb : eventRegistry[event]) { cb(data); } diff --git a/src/NodeContext.h b/src/NodeContext.h index 3b8b579..14060a3 100644 --- a/src/NodeContext.h +++ b/src/NodeContext.h @@ -20,6 +20,6 @@ public: using EventCallback = std::function; std::map> eventRegistry; - void registerEvent(const std::string& event, EventCallback cb); - void triggerEvent(const std::string& event, void* data); + void on(const std::string& event, EventCallback cb); + void fire(const std::string& event, void* data); }; diff --git a/src/main.cpp b/src/main.cpp index 04454bc..17532e0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,12 +3,12 @@ #include "NodeContext.h" #include "NetworkManager.h" #include "ClusterManager.h" -#include "RestApiServer.h" +#include "ApiServer.h" NodeContext ctx; NetworkManager network(ctx); ClusterManager cluster(ctx); -RestApiServer apiServer(ctx); +ApiServer apiServer(ctx); Task tSendDiscovery(TaskIntervals::SEND_DISCOVERY, TASK_FOREVER, [](){ cluster.sendDiscovery(); }); Task tListenForDiscovery(TaskIntervals::LISTEN_FOR_DISCOVERY, TASK_FOREVER, [](){ cluster.listenForDiscovery(); });