#include "mqtt_config.hpp" MQTTConfig g_mqtt_config; MQTTConfig MQTTConfig::load_from_file(const std::string &filepath) { MQTTConfig cfg; std::ifstream ifs(filepath); if (!ifs) { LOG_ERROR("[Config] Failed to open config file: " + filepath); throw std::runtime_error("Failed to open config file: " + filepath); } nlohmann::json j; ifs >> j; cfg.device_id = j["device_id"]; cfg.vin = j["vin"]; cfg.server_ip = j["server_ip"]; cfg.server_port = j["server_port"]; cfg.client_id = j["client_id"]; cfg.username = j["username"]; cfg.password = j["password"]; cfg.qos = j["qos"]; cfg.keep_alive = j["keep_alive"]; cfg.clean_session = j["clean_session"]; const std::string &id = cfg.device_id; cfg.topics.uplink_1 = j["topics"]["uplink_1"].get() + id; cfg.topics.uplink_2 = j["topics"]["uplink_2"].get() + id; cfg.topics.downlink = j["topics"]["downlink"].get() + id; cfg.ports.config_port = j["ports"]["config_port"]; cfg.ports.uplink_1_port = j["ports"]["uplink_1_port"]; cfg.ports.uplink_2_port = j["ports"]["uplink_2_port"]; cfg.ports.downlink_port = j["ports"]["downlink_port"]; LOG_INFO("[Config] Loaded config from " + filepath); LOG_INFO(" - MQTT Server: " + cfg.server_ip + ":" + std::to_string(cfg.server_port)); LOG_INFO(" - Client ID: " + cfg.client_id); LOG_INFO(" - Topics: " + cfg.topics.uplink_1 + ", " + cfg.topics.uplink_2 + ", " + cfg.topics.downlink); return cfg; } bool MQTTConfig::update_mqtt_config(const ordered_json &new_values, const std::string &filepath) { std::ifstream ifs(filepath); if (!ifs.is_open()) { LOG_ERROR("[ConfigEdit] Failed to open config file: " + filepath); return false; } ordered_json j; try { ifs >> j; ifs.close(); } catch (const std::exception &e) { LOG_ERROR(std::string("[ConfigEdit] JSON parse error: ") + e.what()); return false; } for (auto it = new_values.begin(); it != new_values.end(); ++it) { const std::string &key = it.key(); const auto &val = it.value(); if (j.contains(key)) { LOG_INFO("[ConfigEdit] Updating key: " + key + " -> " + val.dump()); j[key] = val; } else { LOG_WARN("[ConfigEdit] Warning: key '" + key + "' not found in config, ignored."); } } std::ofstream ofs(filepath); if (!ofs.is_open()) { LOG_ERROR("[ConfigEdit] Failed to write updated config file: " + filepath); return false; } ofs << j.dump(4); ofs.close(); return true; } std::string get_executable_dir() { char result[PATH_MAX] = {0}; ssize_t count = readlink("/proc/self/exe", result, PATH_MAX); if (count == -1) { throw std::runtime_error("Failed to read /proc/self/exe"); } std::string full_path(result, count); auto pos = full_path.find_last_of('/'); if (pos == std::string::npos) { throw std::runtime_error("Failed to find executable directory"); } return full_path.substr(0, pos); } std::string get_executable_dir_file_path(const std::string &filename) { std::string dir = get_executable_dir(); if (dir.back() != '/') dir += '/'; return dir + filename; }