feat: persistent config
This commit is contained in:
@@ -65,6 +65,16 @@ void NetworkManager::setWiFiConfig(const String& ssid, const String& password,
|
||||
ctx.config.wifi_retry_delay_ms = retry_delay_ms;
|
||||
}
|
||||
|
||||
bool NetworkManager::saveConfig() {
|
||||
return ctx.config.saveToFile();
|
||||
}
|
||||
|
||||
void NetworkManager::restartNode() {
|
||||
LOG_INFO("NetworkManager", "Restarting node after WiFi configuration change...");
|
||||
delay(100); // Give time for response to be sent
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
void NetworkManager::setHostnameFromMac() {
|
||||
uint8_t mac[6];
|
||||
WiFi.macAddress(mac);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "spore/services/NetworkService.h"
|
||||
#include "spore/util/Logging.h"
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
NetworkService::NetworkService(NetworkManager& networkManager)
|
||||
@@ -116,21 +117,34 @@ void NetworkService::handleSetWifiConfig(AsyncWebServerRequest* request) {
|
||||
retry_delay_ms = request->getParam("retry_delay_ms", true)->value().toInt();
|
||||
}
|
||||
|
||||
// Update configuration
|
||||
// Update configuration in memory
|
||||
networkManager.setWiFiConfig(ssid, password, connect_timeout_ms, retry_delay_ms);
|
||||
|
||||
// Attempt to connect with new settings
|
||||
networkManager.setupWiFi();
|
||||
// Save configuration to persistent storage
|
||||
bool configSaved = networkManager.saveConfig();
|
||||
if (!configSaved) {
|
||||
LOG_WARN("NetworkService", "Failed to save WiFi configuration to persistent storage");
|
||||
}
|
||||
|
||||
// Prepare response
|
||||
JsonDocument doc;
|
||||
doc["status"] = "success";
|
||||
doc["message"] = "WiFi configuration updated";
|
||||
doc["connected"] = WiFi.isConnected();
|
||||
if (WiFi.isConnected()) {
|
||||
doc["ip"] = WiFi.localIP().toString();
|
||||
}
|
||||
doc["message"] = "WiFi configuration updated and saved";
|
||||
doc["config_saved"] = configSaved;
|
||||
doc["restarting"] = true;
|
||||
|
||||
String json;
|
||||
serializeJson(doc, json);
|
||||
request->send(200, "application/json", json);
|
||||
|
||||
// Send response before restarting
|
||||
AsyncWebServerResponse* response = request->beginResponse(200, "application/json", json);
|
||||
response->addHeader("Connection", "close");
|
||||
request->send(response);
|
||||
|
||||
// Restart the node to apply new WiFi settings
|
||||
request->onDisconnect([this]() {
|
||||
LOG_INFO("NetworkService", "Restarting node to apply WiFi configuration...");
|
||||
delay(100); // Give time for response to be sent
|
||||
networkManager.restartNode();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,37 +1,185 @@
|
||||
#include "spore/types/Config.h"
|
||||
#include "spore/util/Logging.h"
|
||||
|
||||
const char* Config::CONFIG_FILE_PATH = "/config.json";
|
||||
|
||||
Config::Config() {
|
||||
// Initialize LittleFS
|
||||
if (!LittleFS.begin()) {
|
||||
LOG_WARN("Config", "Failed to initialize LittleFS, using defaults");
|
||||
setDefaults();
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to load configuration from file
|
||||
if (!loadFromFile()) {
|
||||
LOG_INFO("Config", "No config file found, using defaults");
|
||||
setDefaults();
|
||||
// Save defaults to file for future use
|
||||
saveToFile();
|
||||
} else {
|
||||
LOG_INFO("Config", "Configuration loaded from file");
|
||||
}
|
||||
}
|
||||
|
||||
void Config::setDefaults() {
|
||||
// WiFi Configuration
|
||||
wifi_ssid = "shroud";
|
||||
wifi_password = "th3r31sn0sp00n";
|
||||
wifi_ssid = DEFAULT_WIFI_SSID;
|
||||
wifi_password = DEFAULT_WIFI_PASSWORD;
|
||||
|
||||
// Network Configuration
|
||||
udp_port = 4210;
|
||||
api_server_port = 80;
|
||||
udp_port = DEFAULT_UDP_PORT;
|
||||
api_server_port = DEFAULT_API_SERVER_PORT;
|
||||
|
||||
// Cluster Configuration
|
||||
discovery_interval_ms = 1000; // TODO retire this in favor of heartbeat_interval_ms
|
||||
cluster_listen_interval_ms = 10;
|
||||
heartbeat_interval_ms = 5000;
|
||||
status_update_interval_ms = 1000;
|
||||
member_info_update_interval_ms = 10000; // TODO retire this in favor of heartbeat_interval_ms
|
||||
print_interval_ms = 5000;
|
||||
discovery_interval_ms = DEFAULT_DISCOVERY_INTERVAL_MS; // TODO retire this in favor of heartbeat_interval_ms
|
||||
cluster_listen_interval_ms = DEFAULT_CLUSTER_LISTEN_INTERVAL_MS;
|
||||
heartbeat_interval_ms = DEFAULT_HEARTBEAT_INTERVAL_MS;
|
||||
status_update_interval_ms = DEFAULT_STATUS_UPDATE_INTERVAL_MS;
|
||||
member_info_update_interval_ms = DEFAULT_MEMBER_INFO_UPDATE_INTERVAL_MS; // TODO retire this in favor of heartbeat_interval_ms
|
||||
print_interval_ms = DEFAULT_PRINT_INTERVAL_MS;
|
||||
|
||||
// Node Status Thresholds
|
||||
node_active_threshold_ms = 10000;
|
||||
node_inactive_threshold_ms = 60000;
|
||||
node_dead_threshold_ms = 120000;
|
||||
node_active_threshold_ms = DEFAULT_NODE_ACTIVE_THRESHOLD_MS;
|
||||
node_inactive_threshold_ms = DEFAULT_NODE_INACTIVE_THRESHOLD_MS;
|
||||
node_dead_threshold_ms = DEFAULT_NODE_DEAD_THRESHOLD_MS;
|
||||
|
||||
// WiFi Connection
|
||||
wifi_connect_timeout_ms = 15000;
|
||||
wifi_retry_delay_ms = 500;
|
||||
wifi_connect_timeout_ms = DEFAULT_WIFI_CONNECT_TIMEOUT_MS;
|
||||
wifi_retry_delay_ms = DEFAULT_WIFI_RETRY_DELAY_MS;
|
||||
|
||||
// System Configuration
|
||||
restart_delay_ms = 10;
|
||||
json_doc_size = 1024;
|
||||
restart_delay_ms = DEFAULT_RESTART_DELAY_MS;
|
||||
json_doc_size = DEFAULT_JSON_DOC_SIZE;
|
||||
|
||||
// Memory Management
|
||||
low_memory_threshold_bytes = 10000; // 10KB
|
||||
critical_memory_threshold_bytes = 5000; // 5KB
|
||||
max_concurrent_http_requests = 3;
|
||||
low_memory_threshold_bytes = DEFAULT_LOW_MEMORY_THRESHOLD_BYTES; // 10KB
|
||||
critical_memory_threshold_bytes = DEFAULT_CRITICAL_MEMORY_THRESHOLD_BYTES; // 5KB
|
||||
max_concurrent_http_requests = DEFAULT_MAX_CONCURRENT_HTTP_REQUESTS;
|
||||
}
|
||||
|
||||
bool Config::saveToFile(const String& filename) {
|
||||
if (!LittleFS.begin()) {
|
||||
LOG_ERROR("Config", "LittleFS not initialized, cannot save config");
|
||||
return false;
|
||||
}
|
||||
|
||||
File file = LittleFS.open(filename, "w");
|
||||
if (!file) {
|
||||
LOG_ERROR("Config", "Failed to open config file for writing: " + filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
JsonDocument doc;
|
||||
|
||||
// WiFi Configuration
|
||||
doc["wifi"]["ssid"] = wifi_ssid;
|
||||
doc["wifi"]["password"] = wifi_password;
|
||||
doc["wifi"]["connect_timeout_ms"] = wifi_connect_timeout_ms;
|
||||
doc["wifi"]["retry_delay_ms"] = wifi_retry_delay_ms;
|
||||
|
||||
// Network Configuration
|
||||
doc["network"]["udp_port"] = udp_port;
|
||||
doc["network"]["api_server_port"] = api_server_port;
|
||||
|
||||
// Cluster Configuration
|
||||
doc["cluster"]["discovery_interval_ms"] = discovery_interval_ms;
|
||||
doc["cluster"]["heartbeat_interval_ms"] = heartbeat_interval_ms;
|
||||
doc["cluster"]["cluster_listen_interval_ms"] = cluster_listen_interval_ms;
|
||||
doc["cluster"]["status_update_interval_ms"] = status_update_interval_ms;
|
||||
doc["cluster"]["member_info_update_interval_ms"] = member_info_update_interval_ms;
|
||||
doc["cluster"]["print_interval_ms"] = print_interval_ms;
|
||||
|
||||
// Node Status Thresholds
|
||||
doc["thresholds"]["node_active_threshold_ms"] = node_active_threshold_ms;
|
||||
doc["thresholds"]["node_inactive_threshold_ms"] = node_inactive_threshold_ms;
|
||||
doc["thresholds"]["node_dead_threshold_ms"] = node_dead_threshold_ms;
|
||||
|
||||
// System Configuration
|
||||
doc["system"]["restart_delay_ms"] = restart_delay_ms;
|
||||
doc["system"]["json_doc_size"] = json_doc_size;
|
||||
|
||||
// Memory Management
|
||||
doc["memory"]["low_memory_threshold_bytes"] = low_memory_threshold_bytes;
|
||||
doc["memory"]["critical_memory_threshold_bytes"] = critical_memory_threshold_bytes;
|
||||
doc["memory"]["max_concurrent_http_requests"] = max_concurrent_http_requests;
|
||||
|
||||
// Add metadata
|
||||
doc["_meta"]["version"] = "1.0";
|
||||
doc["_meta"]["saved_at"] = millis();
|
||||
|
||||
size_t bytesWritten = serializeJson(doc, file);
|
||||
file.close();
|
||||
|
||||
if (bytesWritten > 0) {
|
||||
LOG_INFO("Config", "Configuration saved to " + filename + " (" + String(bytesWritten) + " bytes)");
|
||||
return true;
|
||||
} else {
|
||||
LOG_ERROR("Config", "Failed to write configuration to file");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Config::loadFromFile(const String& filename) {
|
||||
if (!LittleFS.begin()) {
|
||||
LOG_ERROR("Config", "LittleFS not initialized, cannot load config");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!LittleFS.exists(filename)) {
|
||||
LOG_DEBUG("Config", "Config file does not exist: " + filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
File file = LittleFS.open(filename, "r");
|
||||
if (!file) {
|
||||
LOG_ERROR("Config", "Failed to open config file for reading: " + filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
JsonDocument doc;
|
||||
DeserializationError error = deserializeJson(doc, file);
|
||||
file.close();
|
||||
|
||||
if (error) {
|
||||
LOG_ERROR("Config", "Failed to parse config file: " + String(error.c_str()));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load WiFi Configuration with defaults
|
||||
wifi_ssid = doc["wifi"]["ssid"] | DEFAULT_WIFI_SSID;
|
||||
wifi_password = doc["wifi"]["password"] | DEFAULT_WIFI_PASSWORD;
|
||||
wifi_connect_timeout_ms = doc["wifi"]["connect_timeout_ms"] | DEFAULT_WIFI_CONNECT_TIMEOUT_MS;
|
||||
wifi_retry_delay_ms = doc["wifi"]["retry_delay_ms"] | DEFAULT_WIFI_RETRY_DELAY_MS;
|
||||
|
||||
// Load Network Configuration with defaults
|
||||
udp_port = doc["network"]["udp_port"] | DEFAULT_UDP_PORT;
|
||||
api_server_port = doc["network"]["api_server_port"] | DEFAULT_API_SERVER_PORT;
|
||||
|
||||
// Load Cluster Configuration with defaults
|
||||
discovery_interval_ms = doc["cluster"]["discovery_interval_ms"] | DEFAULT_DISCOVERY_INTERVAL_MS;
|
||||
heartbeat_interval_ms = doc["cluster"]["heartbeat_interval_ms"] | DEFAULT_HEARTBEAT_INTERVAL_MS;
|
||||
cluster_listen_interval_ms = doc["cluster"]["cluster_listen_interval_ms"] | DEFAULT_CLUSTER_LISTEN_INTERVAL_MS;
|
||||
status_update_interval_ms = doc["cluster"]["status_update_interval_ms"] | DEFAULT_STATUS_UPDATE_INTERVAL_MS;
|
||||
member_info_update_interval_ms = doc["cluster"]["member_info_update_interval_ms"] | DEFAULT_MEMBER_INFO_UPDATE_INTERVAL_MS;
|
||||
print_interval_ms = doc["cluster"]["print_interval_ms"] | DEFAULT_PRINT_INTERVAL_MS;
|
||||
|
||||
// Load Node Status Thresholds with defaults
|
||||
node_active_threshold_ms = doc["thresholds"]["node_active_threshold_ms"] | DEFAULT_NODE_ACTIVE_THRESHOLD_MS;
|
||||
node_inactive_threshold_ms = doc["thresholds"]["node_inactive_threshold_ms"] | DEFAULT_NODE_INACTIVE_THRESHOLD_MS;
|
||||
node_dead_threshold_ms = doc["thresholds"]["node_dead_threshold_ms"] | DEFAULT_NODE_DEAD_THRESHOLD_MS;
|
||||
|
||||
// Load System Configuration with defaults
|
||||
restart_delay_ms = doc["system"]["restart_delay_ms"] | DEFAULT_RESTART_DELAY_MS;
|
||||
json_doc_size = doc["system"]["json_doc_size"] | DEFAULT_JSON_DOC_SIZE;
|
||||
|
||||
// Load Memory Management with defaults
|
||||
low_memory_threshold_bytes = doc["memory"]["low_memory_threshold_bytes"] | DEFAULT_LOW_MEMORY_THRESHOLD_BYTES;
|
||||
critical_memory_threshold_bytes = doc["memory"]["critical_memory_threshold_bytes"] | DEFAULT_CRITICAL_MEMORY_THRESHOLD_BYTES;
|
||||
max_concurrent_http_requests = doc["memory"]["max_concurrent_http_requests"] | DEFAULT_MAX_CONCURRENT_HTTP_REQUESTS;
|
||||
|
||||
LOG_DEBUG("Config", "Loaded WiFi SSID: " + wifi_ssid);
|
||||
LOG_DEBUG("Config", "Config file version: " + String(doc["_meta"]["version"] | "unknown"));
|
||||
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user