Compare commits
2 Commits
175ed8cae8
...
fd89c8e7eb
| Author | SHA1 | Date | |
|---|---|---|---|
| fd89c8e7eb | |||
| 3124a7f2db |
37
include/Config.h
Normal file
37
include/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();
|
||||||
|
};
|
||||||
@@ -6,7 +6,10 @@ void ApiServer::addEndpoint(const String& uri, int method, std::function<void(As
|
|||||||
serviceRegistry.push_back(std::make_tuple(uri, method));
|
serviceRegistry.push_back(std::make_tuple(uri, method));
|
||||||
// Store in NodeInfo for local node
|
// Store in NodeInfo for local node
|
||||||
if (ctx.memberList && !ctx.memberList->empty()) {
|
if (ctx.memberList && !ctx.memberList->empty()) {
|
||||||
(*ctx.memberList)[0].apiEndpoints.push_back(std::make_tuple(uri, method));
|
auto it = ctx.memberList->find(ctx.hostname);
|
||||||
|
if (it != ctx.memberList->end()) {
|
||||||
|
it->second.apiEndpoints.push_back(std::make_tuple(uri, method));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
server.on(uri.c_str(), method, requestHandler);
|
server.on(uri.c_str(), method, requestHandler);
|
||||||
}
|
}
|
||||||
@@ -15,7 +18,10 @@ void ApiServer::addEndpoint(const String& uri, int method, std::function<void(As
|
|||||||
std::function<void(AsyncWebServerRequest*, const String&, size_t, uint8_t*, size_t, bool)> uploadHandler) {
|
std::function<void(AsyncWebServerRequest*, const String&, size_t, uint8_t*, size_t, bool)> uploadHandler) {
|
||||||
serviceRegistry.push_back(std::make_tuple(uri, method));
|
serviceRegistry.push_back(std::make_tuple(uri, method));
|
||||||
if (ctx.memberList && !ctx.memberList->empty()) {
|
if (ctx.memberList && !ctx.memberList->empty()) {
|
||||||
(*ctx.memberList)[0].apiEndpoints.push_back(std::make_tuple(uri, method));
|
auto it = ctx.memberList->find(ctx.hostname);
|
||||||
|
if (it != ctx.memberList->end()) {
|
||||||
|
it->second.apiEndpoints.push_back(std::make_tuple(uri, method));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
server.on(uri.c_str(), method, requestHandler, uploadHandler);
|
server.on(uri.c_str(), method, requestHandler, uploadHandler);
|
||||||
}
|
}
|
||||||
@@ -55,7 +61,8 @@ void ApiServer::onSystemStatusRequest(AsyncWebServerRequest *request) {
|
|||||||
void ApiServer::onClusterMembersRequest(AsyncWebServerRequest *request) {
|
void ApiServer::onClusterMembersRequest(AsyncWebServerRequest *request) {
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
JsonArray arr = doc["members"].to<JsonArray>();
|
JsonArray arr = doc["members"].to<JsonArray>();
|
||||||
for (const auto& node : *ctx.memberList) {
|
for (const auto& pair : *ctx.memberList) {
|
||||||
|
const NodeInfo& node = pair.second;
|
||||||
JsonObject obj = arr.add<JsonObject>();
|
JsonObject obj = arr.add<JsonObject>();
|
||||||
obj["hostname"] = node.hostname;
|
obj["hostname"] = node.hostname;
|
||||||
obj["ip"] = node.ip.toString();
|
obj["ip"] = node.ip.toString();
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ ClusterManager::ClusterManager(NodeContext& ctx) : ctx(ctx) {
|
|||||||
|
|
||||||
void ClusterManager::sendDiscovery() {
|
void ClusterManager::sendDiscovery() {
|
||||||
//Serial.println("[Cluster] Sending discovery packet...");
|
//Serial.println("[Cluster] Sending discovery packet...");
|
||||||
ctx.udp->beginPacket("255.255.255.255", ClusterProtocol::UDP_PORT);
|
ctx.udp->beginPacket("255.255.255.255", ctx.config.udp_port);
|
||||||
ctx.udp->write(ClusterProtocol::DISCOVERY_MSG);
|
ctx.udp->write(ClusterProtocol::DISCOVERY_MSG);
|
||||||
ctx.udp->endPacket();
|
ctx.udp->endPacket();
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,7 @@ void ClusterManager::listenForDiscovery() {
|
|||||||
//Serial.printf("[UDP] Packet received: %s\n", incoming);
|
//Serial.printf("[UDP] Packet received: %s\n", incoming);
|
||||||
if (strcmp(incoming, ClusterProtocol::DISCOVERY_MSG) == 0) {
|
if (strcmp(incoming, ClusterProtocol::DISCOVERY_MSG) == 0) {
|
||||||
//Serial.printf("[UDP] Discovery request from: %s\n", ctx.udp->remoteIP().toString().c_str());
|
//Serial.printf("[UDP] Discovery request from: %s\n", ctx.udp->remoteIP().toString().c_str());
|
||||||
ctx.udp->beginPacket(ctx.udp->remoteIP(), ClusterProtocol::UDP_PORT);
|
ctx.udp->beginPacket(ctx.udp->remoteIP(), ctx.config.udp_port);
|
||||||
String response = String(ClusterProtocol::RESPONSE_MSG) + ":" + ctx.hostname;
|
String response = String(ClusterProtocol::RESPONSE_MSG) + ":" + ctx.hostname;
|
||||||
ctx.udp->write(response.c_str());
|
ctx.udp->write(response.c_str());
|
||||||
ctx.udp->endPacket();
|
ctx.udp->endPacket();
|
||||||
@@ -41,19 +41,24 @@ void ClusterManager::listenForDiscovery() {
|
|||||||
|
|
||||||
void ClusterManager::addOrUpdateNode(const String& nodeHost, IPAddress nodeIP) {
|
void ClusterManager::addOrUpdateNode(const String& nodeHost, IPAddress nodeIP) {
|
||||||
auto& memberList = *ctx.memberList;
|
auto& memberList = *ctx.memberList;
|
||||||
for (auto& node : memberList) {
|
|
||||||
if (node.hostname == nodeHost) {
|
// O(1) lookup instead of O(n) search
|
||||||
node.ip = nodeIP;
|
auto it = memberList.find(nodeHost);
|
||||||
//fetchNodeInfo(nodeIP); // Do not fetch here, handled by periodic task
|
if (it != memberList.end()) {
|
||||||
return;
|
// Update existing node
|
||||||
}
|
it->second.ip = nodeIP;
|
||||||
|
it->second.lastSeen = millis();
|
||||||
|
//fetchNodeInfo(nodeIP); // Do not fetch here, handled by periodic task
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add new node
|
||||||
NodeInfo newNode;
|
NodeInfo newNode;
|
||||||
newNode.hostname = nodeHost;
|
newNode.hostname = nodeHost;
|
||||||
newNode.ip = nodeIP;
|
newNode.ip = nodeIP;
|
||||||
newNode.lastSeen = millis();
|
newNode.lastSeen = millis();
|
||||||
updateNodeStatus(newNode, newNode.lastSeen);
|
updateNodeStatus(newNode, newNode.lastSeen, ctx.config.node_inactive_threshold_ms, ctx.config.node_dead_threshold_ms);
|
||||||
memberList.push_back(newNode);
|
memberList[nodeHost] = newNode;
|
||||||
Serial.printf("[Cluster] Added node: %s @ %s | Status: %s | last update: 0\n",
|
Serial.printf("[Cluster] Added node: %s @ %s | Status: %s | last update: 0\n",
|
||||||
nodeHost.c_str(),
|
nodeHost.c_str(),
|
||||||
newNode.ip.toString().c_str(),
|
newNode.ip.toString().c_str(),
|
||||||
@@ -77,7 +82,9 @@ void ClusterManager::fetchNodeInfo(const IPAddress& ip) {
|
|||||||
DeserializationError err = deserializeJson(doc, payload);
|
DeserializationError err = deserializeJson(doc, payload);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
auto& memberList = *ctx.memberList;
|
auto& memberList = *ctx.memberList;
|
||||||
for (auto& node : memberList) {
|
// Still need to iterate since we're searching by IP, not hostname
|
||||||
|
for (auto& pair : memberList) {
|
||||||
|
NodeInfo& node = pair.second;
|
||||||
if (node.ip == ip) {
|
if (node.ip == ip) {
|
||||||
node.resources.freeHeap = doc["freeHeap"];
|
node.resources.freeHeap = doc["freeHeap"];
|
||||||
node.resources.chipId = doc["chipId"];
|
node.resources.chipId = doc["chipId"];
|
||||||
@@ -96,6 +103,7 @@ void ClusterManager::fetchNodeInfo(const IPAddress& ip) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Serial.printf("[Cluster] Fetched info for node: %s @ %s\n", node.hostname.c_str(), ip.toString().c_str());
|
Serial.printf("[Cluster] Fetched info for node: %s @ %s\n", node.hostname.c_str(), ip.toString().c_str());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,20 +114,21 @@ void ClusterManager::fetchNodeInfo(const IPAddress& ip) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ClusterManager::heartbeatTaskCallback() {
|
void ClusterManager::heartbeatTaskCallback() {
|
||||||
for (auto& node : *ctx.memberList) {
|
auto& memberList = *ctx.memberList;
|
||||||
if (node.hostname == ctx.hostname) {
|
auto it = memberList.find(ctx.hostname);
|
||||||
node.lastSeen = millis();
|
if (it != memberList.end()) {
|
||||||
node.status = NodeInfo::ACTIVE;
|
NodeInfo& node = it->second;
|
||||||
updateLocalNodeResources();
|
node.lastSeen = millis();
|
||||||
ctx.fire("node_discovered", &node);
|
node.status = NodeInfo::ACTIVE;
|
||||||
break;
|
updateLocalNodeResources();
|
||||||
}
|
ctx.fire("node_discovered", &node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClusterManager::updateAllMembersInfoTaskCallback() {
|
void ClusterManager::updateAllMembersInfoTaskCallback() {
|
||||||
auto& memberList = *ctx.memberList;
|
auto& memberList = *ctx.memberList;
|
||||||
for (const auto& node : memberList) {
|
for (auto& pair : memberList) {
|
||||||
|
const NodeInfo& node = pair.second;
|
||||||
if (node.ip != ctx.localIP) {
|
if (node.ip != ctx.localIP) {
|
||||||
fetchNodeInfo(node.ip);
|
fetchNodeInfo(node.ip);
|
||||||
}
|
}
|
||||||
@@ -129,8 +138,9 @@ void ClusterManager::updateAllMembersInfoTaskCallback() {
|
|||||||
void ClusterManager::updateAllNodeStatuses() {
|
void ClusterManager::updateAllNodeStatuses() {
|
||||||
auto& memberList = *ctx.memberList;
|
auto& memberList = *ctx.memberList;
|
||||||
unsigned long now = millis();
|
unsigned long now = millis();
|
||||||
for (auto& node : memberList) {
|
for (auto& pair : memberList) {
|
||||||
updateNodeStatus(node, now);
|
NodeInfo& node = pair.second;
|
||||||
|
updateNodeStatus(node, now, ctx.config.node_inactive_threshold_ms, ctx.config.node_dead_threshold_ms);
|
||||||
node.latency = now - node.lastSeen;
|
node.latency = now - node.lastSeen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,13 +148,15 @@ void ClusterManager::updateAllNodeStatuses() {
|
|||||||
void ClusterManager::removeDeadNodes() {
|
void ClusterManager::removeDeadNodes() {
|
||||||
auto& memberList = *ctx.memberList;
|
auto& memberList = *ctx.memberList;
|
||||||
unsigned long now = millis();
|
unsigned long now = millis();
|
||||||
for (size_t i = 0; i < memberList.size(); ) {
|
|
||||||
unsigned long diff = now - memberList[i].lastSeen;
|
// Use iterator to safely remove elements from map
|
||||||
if (memberList[i].status == NodeInfo::DEAD && diff > NODE_DEAD_THRESHOLD) {
|
for (auto it = memberList.begin(); it != memberList.end(); ) {
|
||||||
Serial.printf("[Cluster] Removing node: %s\n", memberList[i].hostname.c_str());
|
unsigned long diff = now - it->second.lastSeen;
|
||||||
memberList.erase(memberList.begin() + i);
|
if (it->second.status == NodeInfo::DEAD && diff > ctx.config.node_dead_threshold_ms) {
|
||||||
|
Serial.printf("[Cluster] Removing node: %s\n", it->second.hostname.c_str());
|
||||||
|
it = memberList.erase(it);
|
||||||
} else {
|
} else {
|
||||||
++i;
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -156,20 +168,21 @@ void ClusterManager::printMemberList() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Serial.println("[Cluster] Member List:");
|
Serial.println("[Cluster] Member List:");
|
||||||
for (const auto& node : memberList) {
|
for (const auto& pair : memberList) {
|
||||||
|
const NodeInfo& node = pair.second;
|
||||||
Serial.printf(" %s @ %s | Status: %s | last seen: %lu\n", node.hostname.c_str(), node.ip.toString().c_str(), statusToStr(node.status), millis() - node.lastSeen);
|
Serial.printf(" %s @ %s | Status: %s | last seen: %lu\n", node.hostname.c_str(), node.ip.toString().c_str(), statusToStr(node.status), millis() - node.lastSeen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClusterManager::updateLocalNodeResources() {
|
void ClusterManager::updateLocalNodeResources() {
|
||||||
for (auto& node : *ctx.memberList) {
|
auto& memberList = *ctx.memberList;
|
||||||
if (node.hostname == ctx.hostname) {
|
auto it = memberList.find(ctx.hostname);
|
||||||
node.resources.freeHeap = ESP.getFreeHeap();
|
if (it != memberList.end()) {
|
||||||
node.resources.chipId = ESP.getChipId();
|
NodeInfo& node = it->second;
|
||||||
node.resources.sdkVersion = String(ESP.getSdkVersion());
|
node.resources.freeHeap = ESP.getFreeHeap();
|
||||||
node.resources.cpuFreqMHz = ESP.getCpuFreqMHz();
|
node.resources.chipId = ESP.getChipId();
|
||||||
node.resources.flashChipSize = ESP.getFlashChipSize();
|
node.resources.sdkVersion = String(ESP.getSdkVersion());
|
||||||
break;
|
node.resources.cpuFreqMHz = ESP.getCpuFreqMHz();
|
||||||
}
|
node.resources.flashChipSize = ESP.getFlashChipSize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <ESP8266HTTPClient.h>
|
#include <ESP8266HTTPClient.h>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
class ClusterManager {
|
class ClusterManager {
|
||||||
public:
|
public:
|
||||||
@@ -16,7 +17,7 @@ public:
|
|||||||
void updateAllNodeStatuses();
|
void updateAllNodeStatuses();
|
||||||
void removeDeadNodes();
|
void removeDeadNodes();
|
||||||
void printMemberList();
|
void printMemberList();
|
||||||
const std::vector<NodeInfo>& getMemberList() const { return *ctx.memberList; }
|
const std::map<String, NodeInfo>& getMemberList() const { return *ctx.memberList; }
|
||||||
void fetchNodeInfo(const IPAddress& ip);
|
void fetchNodeInfo(const IPAddress& ip);
|
||||||
void updateLocalNodeResources();
|
void updateLocalNodeResources();
|
||||||
void heartbeatTaskCallback();
|
void heartbeatTaskCallback();
|
||||||
|
|||||||
31
src/Config.cpp
Normal file
31
src/Config.cpp
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
Config::Config() {
|
||||||
|
// WiFi Configuration
|
||||||
|
wifi_ssid = "shroud";
|
||||||
|
wifi_password = "th3r31sn0sp00n";
|
||||||
|
|
||||||
|
// Network Configuration
|
||||||
|
udp_port = 4210;
|
||||||
|
api_server_port = 80;
|
||||||
|
|
||||||
|
// Cluster Configuration
|
||||||
|
discovery_interval_ms = 1000;
|
||||||
|
heartbeat_interval_ms = 2000;
|
||||||
|
status_update_interval_ms = 1000;
|
||||||
|
member_info_update_interval_ms = 10000;
|
||||||
|
print_interval_ms = 5000;
|
||||||
|
|
||||||
|
// Node Status Thresholds
|
||||||
|
node_active_threshold_ms = 10000;
|
||||||
|
node_inactive_threshold_ms = 60000;
|
||||||
|
node_dead_threshold_ms = 120000;
|
||||||
|
|
||||||
|
// WiFi Connection
|
||||||
|
wifi_connect_timeout_ms = 15000;
|
||||||
|
wifi_retry_delay_ms = 500;
|
||||||
|
|
||||||
|
// System Configuration
|
||||||
|
restart_delay_ms = 10;
|
||||||
|
json_doc_size = 1024;
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "NetworkManager.h"
|
#include "NetworkManager.h"
|
||||||
|
|
||||||
const char* STA_SSID = "shroud";
|
// SSID and password are now configured via Config class
|
||||||
const char* STA_PASS = "th3r31sn0sp00n";
|
|
||||||
|
|
||||||
NetworkManager::NetworkManager(NodeContext& ctx) : ctx(ctx) {}
|
NetworkManager::NetworkManager(NodeContext& ctx) : ctx(ctx) {}
|
||||||
|
|
||||||
@@ -17,11 +16,11 @@ void NetworkManager::setHostnameFromMac() {
|
|||||||
void NetworkManager::setupWiFi() {
|
void NetworkManager::setupWiFi() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
WiFi.mode(WIFI_STA);
|
WiFi.mode(WIFI_STA);
|
||||||
WiFi.begin(STA_SSID, STA_PASS);
|
WiFi.begin(ctx.config.wifi_ssid.c_str(), ctx.config.wifi_password.c_str());
|
||||||
Serial.println("[WiFi] Connecting to AP...");
|
Serial.println("[WiFi] Connecting to AP...");
|
||||||
unsigned long startAttemptTime = millis();
|
unsigned long startAttemptTime = millis();
|
||||||
while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < 15000) {
|
while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < ctx.config.wifi_connect_timeout_ms) {
|
||||||
delay(500);
|
delay(ctx.config.wifi_retry_delay_ms);
|
||||||
Serial.print(".");
|
Serial.print(".");
|
||||||
}
|
}
|
||||||
if (WiFi.status() == WL_CONNECTED) {
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
@@ -32,17 +31,17 @@ void NetworkManager::setupWiFi() {
|
|||||||
Serial.println();
|
Serial.println();
|
||||||
Serial.println("[WiFi] Failed to connect to AP. Creating AP...");
|
Serial.println("[WiFi] Failed to connect to AP. Creating AP...");
|
||||||
WiFi.mode(WIFI_AP);
|
WiFi.mode(WIFI_AP);
|
||||||
WiFi.softAP(STA_SSID, STA_PASS);
|
WiFi.softAP(ctx.config.wifi_ssid.c_str(), ctx.config.wifi_password.c_str());
|
||||||
Serial.print("[WiFi] AP created, IP: ");
|
Serial.print("[WiFi] AP created, IP: ");
|
||||||
Serial.println(WiFi.softAPIP());
|
Serial.println(WiFi.softAPIP());
|
||||||
}
|
}
|
||||||
setHostnameFromMac();
|
setHostnameFromMac();
|
||||||
ctx.udp->begin(4210);
|
ctx.udp->begin(ctx.config.udp_port);
|
||||||
ctx.localIP = WiFi.localIP();
|
ctx.localIP = WiFi.localIP();
|
||||||
ctx.hostname = WiFi.hostname();
|
ctx.hostname = WiFi.hostname();
|
||||||
Serial.print("[WiFi] Hostname set to: ");
|
Serial.print("[WiFi] Hostname set to: ");
|
||||||
Serial.println(ctx.hostname);
|
Serial.println(ctx.hostname);
|
||||||
Serial.print("[WiFi] UDP listening on port 4210\n");
|
Serial.printf("[WiFi] UDP listening on port %d\n", ctx.config.udp_port);
|
||||||
|
|
||||||
// Register this node in the memberlist via event system
|
// Register this node in the memberlist via event system
|
||||||
NodeInfo self;
|
NodeInfo self;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
NodeContext::NodeContext() {
|
NodeContext::NodeContext() {
|
||||||
scheduler = new Scheduler();
|
scheduler = new Scheduler();
|
||||||
udp = new WiFiUDP();
|
udp = new WiFiUDP();
|
||||||
memberList = new std::vector<NodeInfo>();
|
memberList = new std::map<String, NodeInfo>();
|
||||||
hostname = "";
|
hostname = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <TaskSchedulerDeclarations.h>
|
#include <TaskSchedulerDeclarations.h>
|
||||||
#include <WiFiUdp.h>
|
#include <WiFiUdp.h>
|
||||||
#include <vector>
|
#include <map>
|
||||||
#include "NodeInfo.h"
|
#include "NodeInfo.h"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <map>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
class NodeContext {
|
class NodeContext {
|
||||||
public:
|
public:
|
||||||
@@ -15,7 +15,8 @@ public:
|
|||||||
WiFiUDP* udp;
|
WiFiUDP* udp;
|
||||||
String hostname;
|
String hostname;
|
||||||
IPAddress localIP;
|
IPAddress localIP;
|
||||||
std::vector<NodeInfo>* memberList;
|
std::map<String, NodeInfo>* memberList;
|
||||||
|
Config config;
|
||||||
|
|
||||||
using EventCallback = std::function<void(void*)>;
|
using EventCallback = std::function<void(void*)>;
|
||||||
std::map<std::string, std::vector<EventCallback>> eventRegistry;
|
std::map<std::string, std::vector<EventCallback>> eventRegistry;
|
||||||
|
|||||||
@@ -9,13 +9,18 @@ const char* statusToStr(NodeInfo::Status status) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateNodeStatus(NodeInfo &node, unsigned long now) {
|
void updateNodeStatus(NodeInfo &node, unsigned long now, unsigned long inactive_threshold, unsigned long dead_threshold) {
|
||||||
unsigned long diff = now - node.lastSeen;
|
unsigned long diff = now - node.lastSeen;
|
||||||
if (diff < NODE_INACTIVE_THRESHOLD) {
|
if (diff < inactive_threshold) {
|
||||||
node.status = NodeInfo::ACTIVE;
|
node.status = NodeInfo::ACTIVE;
|
||||||
} else if (diff < NODE_INACTIVE_THRESHOLD) {
|
} else if (diff < dead_threshold) {
|
||||||
node.status = NodeInfo::INACTIVE;
|
node.status = NodeInfo::DEAD;
|
||||||
} else {
|
} else {
|
||||||
node.status = NodeInfo::DEAD;
|
node.status = NodeInfo::DEAD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateNodeStatus(NodeInfo &node, unsigned long now) {
|
||||||
|
// Legacy implementation using hardcoded values
|
||||||
|
updateNodeStatus(node, now, NODE_INACTIVE_THRESHOLD, NODE_DEAD_THRESHOLD);
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,4 +21,5 @@ struct NodeInfo {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const char* statusToStr(NodeInfo::Status status);
|
const char* statusToStr(NodeInfo::Status status);
|
||||||
void updateNodeStatus(NodeInfo &node, unsigned long now = millis());
|
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
|
||||||
|
|||||||
24
src/main.cpp
24
src/main.cpp
@@ -8,18 +8,27 @@
|
|||||||
NodeContext ctx;
|
NodeContext ctx;
|
||||||
NetworkManager network(ctx);
|
NetworkManager network(ctx);
|
||||||
ClusterManager cluster(ctx);
|
ClusterManager cluster(ctx);
|
||||||
ApiServer apiServer(ctx);
|
ApiServer apiServer(ctx, ctx.config.api_server_port);
|
||||||
|
|
||||||
Task tSendDiscovery(TaskIntervals::SEND_DISCOVERY, TASK_FOREVER, [](){ cluster.sendDiscovery(); });
|
Task tSendDiscovery(0, TASK_FOREVER, [](){ cluster.sendDiscovery(); });
|
||||||
Task tListenForDiscovery(TaskIntervals::LISTEN_FOR_DISCOVERY, TASK_FOREVER, [](){ cluster.listenForDiscovery(); });
|
Task tListenForDiscovery(0, TASK_FOREVER, [](){ cluster.listenForDiscovery(); });
|
||||||
Task tUpdateStatus(TaskIntervals::UPDATE_STATUS, TASK_FOREVER, [](){ cluster.updateAllNodeStatuses(); cluster.removeDeadNodes(); });
|
Task tUpdateStatus(0, TASK_FOREVER, [](){ cluster.updateAllNodeStatuses(); cluster.removeDeadNodes(); });
|
||||||
Task tPrintMemberList(TaskIntervals::PRINT_MEMBER_LIST, TASK_FOREVER, [](){ cluster.printMemberList(); });
|
Task tPrintMemberList(0, TASK_FOREVER, [](){ cluster.printMemberList(); });
|
||||||
Task tHeartbeat(TaskIntervals::HEARTBEAT, TASK_FOREVER, [](){ cluster.heartbeatTaskCallback(); });
|
Task tHeartbeat(0, TASK_FOREVER, [](){ cluster.heartbeatTaskCallback(); });
|
||||||
Task tUpdateAllMembersInfo(TaskIntervals::UPDATE_ALL_MEMBERS_INFO, TASK_FOREVER, [](){ cluster.updateAllMembersInfoTaskCallback(); });
|
Task tUpdateAllMembersInfo(0, TASK_FOREVER, [](){ cluster.updateAllMembersInfoTaskCallback(); });
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
network.setupWiFi();
|
network.setupWiFi();
|
||||||
ctx.scheduler->init();
|
ctx.scheduler->init();
|
||||||
|
|
||||||
|
// Set task intervals from config
|
||||||
|
tSendDiscovery.setInterval(ctx.config.discovery_interval_ms);
|
||||||
|
tListenForDiscovery.setInterval(ctx.config.discovery_interval_ms / 10); // Listen more frequently
|
||||||
|
tUpdateStatus.setInterval(ctx.config.status_update_interval_ms);
|
||||||
|
tPrintMemberList.setInterval(ctx.config.print_interval_ms);
|
||||||
|
tHeartbeat.setInterval(ctx.config.heartbeat_interval_ms);
|
||||||
|
tUpdateAllMembersInfo.setInterval(ctx.config.member_info_update_interval_ms);
|
||||||
|
|
||||||
ctx.scheduler->addTask(tSendDiscovery);
|
ctx.scheduler->addTask(tSendDiscovery);
|
||||||
ctx.scheduler->addTask(tListenForDiscovery);
|
ctx.scheduler->addTask(tListenForDiscovery);
|
||||||
ctx.scheduler->addTask(tUpdateStatus);
|
ctx.scheduler->addTask(tUpdateStatus);
|
||||||
@@ -37,4 +46,5 @@ void setup() {
|
|||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
ctx.scheduler->execute();
|
ctx.scheduler->execute();
|
||||||
|
yield();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user