From b9ac373242067f14ea4d22177e79525ddc24da15 Mon Sep 17 00:00:00 2001 From: aexzhou Date: Sat, 14 Feb 2026 14:31:04 -0800 Subject: [PATCH] Changed User Config saves to use json instead of SQlite --- client/CMakeLists.txt | 4 - client/include/client/config_manager.hpp | 13 +- client/src/config_manager.cpp | 256 ++++++++++------------- client/src/main.cpp | 14 +- 4 files changed, 119 insertions(+), 168 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 58c8f3c5..55c6c955 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -51,10 +51,6 @@ target_include_directories(panorama-client PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ) -# SQLite3 - Required for config database -find_package(SQLite3 REQUIRED) -target_link_libraries(panorama-client SQLite::SQLite3) - if(WIN32) target_link_libraries(panorama-client PRIVATE ws2_32) endif() diff --git a/client/include/client/config_manager.hpp b/client/include/client/config_manager.hpp index 5c186ec9..5363e1d8 100644 --- a/client/include/client/config_manager.hpp +++ b/client/include/client/config_manager.hpp @@ -2,7 +2,7 @@ #include #include -#include +#include class ConfigManager { public: @@ -18,14 +18,14 @@ class ConfigManager { std::string getRuntimeDirectory() const; bool hasRuntimeDirectory() const; - // Database operations - bool initializeDatabase(); + // Config operations + bool initializeConfig(); bool saveConfig(const std::string& key, const std::string& value); std::string getConfig(const std::string& key); // Path helpers std::string getDataLogPath() const; - std::string getDatabasePath() const; + std::string getConfigPath() const; // TCP settings bool saveTcpSettings(const std::string& host, int port, bool autoReconnect, int reconnectDelay); @@ -38,10 +38,11 @@ class ConfigManager { ConfigManager(); std::string runtimeDir_; - sqlite3* db_; + std::map configData_; mutable std::mutex mutex_; bool createDirectoryStructure(); bool testDirectoryWritable(const std::string& path); - bool executeSql(const std::string& sql); + bool loadFromJson(); + bool saveToJson(); }; diff --git a/client/src/config_manager.cpp b/client/src/config_manager.cpp index 25866345..c598eb57 100644 --- a/client/src/config_manager.cpp +++ b/client/src/config_manager.cpp @@ -3,15 +3,11 @@ #include #include #include +#include -ConfigManager::ConfigManager() : db_(nullptr) {} +ConfigManager::ConfigManager() {} -ConfigManager::~ConfigManager() { - if (db_) { - sqlite3_close(db_); - db_ = nullptr; - } -} +ConfigManager::~ConfigManager() {} ConfigManager& ConfigManager::getInstance() { static ConfigManager instance; @@ -67,12 +63,12 @@ std::string ConfigManager::getDataLogPath() const { return runtimeDir_ + "/data/tcp_data.jsonl"; } -std::string ConfigManager::getDatabasePath() const { +std::string ConfigManager::getConfigPath() const { std::lock_guard lock(mutex_); if (runtimeDir_.empty()) { return ""; } - return runtimeDir_ + "/config.db"; + return runtimeDir_ + "/config.json"; } bool ConfigManager::testDirectoryWritable(const std::string& path) { @@ -97,199 +93,157 @@ bool ConfigManager::createDirectoryStructure() { } } -bool ConfigManager::initializeDatabase() { - std::string dbPath = getDatabasePath(); - if (dbPath.empty()) { +bool ConfigManager::initializeConfig() { + std::string configPath = getConfigPath(); + if (configPath.empty()) { std::cerr << "Runtime directory not set" << std::endl; return false; } - // Open database - int rc = sqlite3_open(dbPath.c_str(), &db_); - if (rc != SQLITE_OK) { - std::cerr << "Failed to open database: " << sqlite3_errmsg(db_) << std::endl; - return false; - } - - // Enable thread-safety - sqlite3_busy_timeout(db_, 5000); - - // Create schema - const char* schema = R"( - CREATE TABLE IF NOT EXISTS config ( - key TEXT PRIMARY KEY, - value TEXT NOT NULL, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ); - - CREATE TABLE IF NOT EXISTS tcp_settings ( - id INTEGER PRIMARY KEY CHECK (id = 1), - host TEXT NOT NULL DEFAULT '127.0.0.1', - port INTEGER NOT NULL DEFAULT 3000, - auto_reconnect BOOLEAN DEFAULT 1, - reconnect_delay_sec INTEGER DEFAULT 5 - ); - - CREATE TABLE IF NOT EXISTS logging_settings ( - id INTEGER PRIMARY KEY CHECK (id = 1), - max_file_size_mb INTEGER DEFAULT 10, - max_file_duration_min INTEGER DEFAULT 60, - log_format TEXT DEFAULT 'jsonl' - ); - - CREATE TABLE IF NOT EXISTS sensors ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL, - type TEXT NOT NULL, - enabled BOOLEAN DEFAULT 1, - color TEXT, - unit TEXT - ); - - CREATE TABLE IF NOT EXISTS metadata ( - key TEXT PRIMARY KEY, - value TEXT - ); - - INSERT OR IGNORE INTO metadata (key, value) VALUES - ('db_version', '1.0'), - ('created_at', datetime('now')); - - INSERT OR IGNORE INTO tcp_settings (id, host, port) VALUES (1, '127.0.0.1', 3000); - INSERT OR IGNORE INTO logging_settings (id) VALUES (1); - )"; - - if (!executeSql(schema)) { - return false; + // Check if theres an existing config file, if so use that instead since this would mean + // user loaded up a pre-exisiting project + if (std::filesystem::exists(configPath)) { + if (!loadFromJson()) { + std::cerr << "Failed to load config from JSON" << std::endl; + return false; + } + } else { + // default init + configData_["runtime_directory"] = runtimeDir_; + configData_["tcp_host"] = "127.0.0.1"; + configData_["tcp_port"] = "3000"; + configData_["tcp_auto_reconnect"] = "true"; + configData_["tcp_reconnect_delay_sec"] = "5"; + + if (!saveToJson()) { + std::cerr << "Failed to save initial config" << std::endl; + return false; + } } - // Save runtime directory to config saveConfig("runtime_directory", runtimeDir_); return true; } -bool ConfigManager::executeSql(const std::string& sql) { - char* errMsg = nullptr; - int rc = sqlite3_exec(db_, sql.c_str(), nullptr, nullptr, &errMsg); +bool ConfigManager::loadFromJson() { + std::string configPath = getConfigPath(); + std::ifstream file(configPath); - if (rc != SQLITE_OK) { - std::cerr << "SQL error: " << errMsg << std::endl; - sqlite3_free(errMsg); + if (!file.is_open()) { return false; } - return true; -} - -bool ConfigManager::saveConfig(const std::string& key, const std::string& value) { - std::lock_guard lock(mutex_); + std::string line; + std::stringstream buffer; + buffer << file.rdbuf(); + std::string content = buffer.str(); - if (!db_) { + size_t start = content.find('{'); + size_t end = content.rfind('}'); + if (start == std::string::npos || end == std::string::npos) { return false; } - const char* sql = "INSERT OR REPLACE INTO config (key, value, updated_at) VALUES (?, ?, datetime('now'))"; - sqlite3_stmt* stmt; + content = content.substr(start + 1, end - start - 1); - int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr); - if (rc != SQLITE_OK) { - std::cerr << "Failed to prepare statement: " << sqlite3_errmsg(db_) << std::endl; - return false; - } + std::istringstream stream(content); + std::string pair; - sqlite3_bind_text(stmt, 1, key.c_str(), -1, SQLITE_TRANSIENT); - sqlite3_bind_text(stmt, 2, value.c_str(), -1, SQLITE_TRANSIENT); + while (std::getline(stream, pair, ',')) { + size_t colonPos = pair.find(':'); + if (colonPos == std::string::npos) { + continue; + } - rc = sqlite3_step(stmt); - sqlite3_finalize(stmt); + std::string key = pair.substr(0, colonPos); + std::string value = pair.substr(colonPos + 1); - return rc == SQLITE_DONE; -} + auto trim = [](std::string& s) { + s.erase(0, s.find_first_not_of(" \t\n\r\"")); + s.erase(s.find_last_not_of(" \t\n\r\"") + 1); + }; -std::string ConfigManager::getConfig(const std::string& key) { - std::lock_guard lock(mutex_); + trim(key); + trim(value); - if (!db_) { - return ""; + if (!key.empty()) { + configData_[key] = value; + } } - const char* sql = "SELECT value FROM config WHERE key = ?"; - sqlite3_stmt* stmt; - std::string result; + return true; +} - int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr); - if (rc != SQLITE_OK) { - return ""; - } +bool ConfigManager::saveToJson() { + std::string configPath = getConfigPath(); + std::ofstream file(configPath); - sqlite3_bind_text(stmt, 1, key.c_str(), -1, SQLITE_TRANSIENT); + if (!file.is_open()) { + std::cerr << "Failed to open config file for writing: " << configPath << std::endl; + return false; + } - if (sqlite3_step(stmt) == SQLITE_ROW) { - const char* value = (const char*)sqlite3_column_text(stmt, 0); - if (value) { - result = value; + // Write out json + file << "{\n"; + bool first = true; + for (const auto& [key, value] : configData_) { + if (!first) { + file << ",\n"; } + file << " \"" << key << "\": \"" << value << "\""; + first = false; } + file << "\n}\n"; - sqlite3_finalize(stmt); - return result; + file.close(); + return true; } -bool ConfigManager::saveTcpSettings(const std::string& host, int port, bool autoReconnect, int reconnectDelay) { +bool ConfigManager::saveConfig(const std::string& key, const std::string& value) { std::lock_guard lock(mutex_); - if (!db_) { - return false; - } + configData_[key] = value; + return saveToJson(); +} - const char* sql = "INSERT OR REPLACE INTO tcp_settings (id, host, port, auto_reconnect, reconnect_delay_sec) VALUES (1, ?, ?, ?, ?)"; - sqlite3_stmt* stmt; +std::string ConfigManager::getConfig(const std::string& key) { + std::lock_guard lock(mutex_); - int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr); - if (rc != SQLITE_OK) { - return false; + auto it = configData_.find(key); + if (it != configData_.end()) { + return it->second; } + return ""; +} - sqlite3_bind_text(stmt, 1, host.c_str(), -1, SQLITE_TRANSIENT); - sqlite3_bind_int(stmt, 2, port); - sqlite3_bind_int(stmt, 3, autoReconnect ? 1 : 0); - sqlite3_bind_int(stmt, 4, reconnectDelay); +bool ConfigManager::saveTcpSettings(const std::string& host, int port, bool autoReconnect, int reconnectDelay) { + std::lock_guard lock(mutex_); - rc = sqlite3_step(stmt); - sqlite3_finalize(stmt); + configData_["tcp_host"] = host; + configData_["tcp_port"] = std::to_string(port); + configData_["tcp_auto_reconnect"] = autoReconnect ? "true" : "false"; + configData_["tcp_reconnect_delay_sec"] = std::to_string(reconnectDelay); - return rc == SQLITE_DONE; + return saveToJson(); } bool ConfigManager::getTcpSettings(std::string& host, int& port, bool& autoReconnect, int& reconnectDelay) { std::lock_guard lock(mutex_); - if (!db_) { - return false; - } - - const char* sql = "SELECT host, port, auto_reconnect, reconnect_delay_sec FROM tcp_settings WHERE id = 1"; - sqlite3_stmt* stmt; + auto hostIt = configData_.find("tcp_host"); + auto portIt = configData_.find("tcp_port"); + auto autoReconnectIt = configData_.find("tcp_auto_reconnect"); + auto reconnectDelayIt = configData_.find("tcp_reconnect_delay_sec"); - int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr); - if (rc != SQLITE_OK) { + if (hostIt == configData_.end() || portIt == configData_.end()) { return false; } - if (sqlite3_step(stmt) == SQLITE_ROW) { - const char* hostStr = (const char*)sqlite3_column_text(stmt, 0); - if (hostStr) { - host = hostStr; - } - port = sqlite3_column_int(stmt, 1); - autoReconnect = sqlite3_column_int(stmt, 2) != 0; - reconnectDelay = sqlite3_column_int(stmt, 3); + host = hostIt->second; + port = std::stoi(portIt->second); + autoReconnect = (autoReconnectIt != configData_.end() && autoReconnectIt->second == "true"); + reconnectDelay = (reconnectDelayIt != configData_.end()) ? std::stoi(reconnectDelayIt->second) : 5; - sqlite3_finalize(stmt); - return true; - } - - sqlite3_finalize(stmt); - return false; + return true; } diff --git a/client/src/main.cpp b/client/src/main.cpp index 8c93f493..72e3caa1 100644 --- a/client/src/main.cpp +++ b/client/src/main.cpp @@ -93,13 +93,13 @@ class PanoramaClient : public wxApp { return false; } - // Initialize database - if (!config.initializeDatabase()) { + // Init config + if (!config.initializeConfig()) { if (!parser.isNoGuiMode()) { - wxMessageBox("Failed to initialize configuration database.", + wxMessageBox("Failed to initialize configuration.", "Error", wxOK | wxICON_ERROR); } else { - std::cerr << "Failed to initialize configuration database" << std::endl; + std::cerr << "Failed to initialize configuration" << std::endl; } return false; } @@ -115,9 +115,9 @@ class PanoramaClient : public wxApp { return false; } - // Open existing database - if (!config.initializeDatabase()) { - std::cerr << "Failed to open configuration database" << std::endl; + // Open existing config + if (!config.initializeConfig()) { + std::cerr << "Failed to open configuration" << std::endl; return false; } }