feat: config class

This commit is contained in:
2025-08-21 21:33:54 +02:00
parent 3124a7f2db
commit fd89c8e7eb
8 changed files with 110 additions and 25 deletions

37
include/Config.h Normal file
View 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();
};

View File

@@ -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();
@@ -57,7 +57,7 @@ void ClusterManager::addOrUpdateNode(const String& nodeHost, IPAddress nodeIP) {
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[nodeHost] = 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(),
@@ -140,7 +140,7 @@ void ClusterManager::updateAllNodeStatuses() {
unsigned long now = millis(); unsigned long now = millis();
for (auto& pair : memberList) { for (auto& pair : memberList) {
NodeInfo& node = pair.second; NodeInfo& node = pair.second;
updateNodeStatus(node, now); 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;
} }
} }
@@ -152,7 +152,7 @@ void ClusterManager::removeDeadNodes() {
// Use iterator to safely remove elements from map // Use iterator to safely remove elements from map
for (auto it = memberList.begin(); it != memberList.end(); ) { for (auto it = memberList.begin(); it != memberList.end(); ) {
unsigned long diff = now - it->second.lastSeen; unsigned long diff = now - it->second.lastSeen;
if (it->second.status == NodeInfo::DEAD && diff > NODE_DEAD_THRESHOLD) { 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()); Serial.printf("[Cluster] Removing node: %s\n", it->second.hostname.c_str());
it = memberList.erase(it); it = memberList.erase(it);
} else { } else {

31
src/Config.cpp Normal file
View 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;
}

View File

@@ -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;

View File

@@ -5,6 +5,7 @@
#include "NodeInfo.h" #include "NodeInfo.h"
#include <functional> #include <functional>
#include <string> #include <string>
#include "Config.h"
class NodeContext { class NodeContext {
public: public:
@@ -15,6 +16,7 @@ public:
String hostname; String hostname;
IPAddress localIP; IPAddress localIP;
std::map<String, 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;

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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();
} }