#pragma once #include #include #include namespace fgc { // Typed application configuration. Replaces the ad-hoc std::map // lookups that were scattered through main.cpp. struct GeneralConfig { std::string tower_name = "Unnamed"; int image_interval = 5; // seconds between captures bool debug = false; // print motor telemetry each loop tick }; struct NetworkConfig { std::string broker_ip = "127.0.0.1"; // MQTT broker / ZKMS server std::string mqtt_user; // see secrets note below std::string mqtt_pw; }; struct SerialConfig { std::string device = "/dev/ttyACM0"; unsigned int baud = 115200; }; struct CameraConfig { std::vector ids; // GigE IP or USB DEV_ id, in order std::vector labels = {"RGB", "ACR", "NIR"}; // index -> output subfolder }; struct PathsConfig { // Where captured .jxl images are written. Supports leading ~ and $ENV // expansion. Empty => resolved to a sensible default at load time. std::string output_dir; }; struct FeaturesConfig { bool enable_mqtt = true; bool enable_camera = true; bool enable_serial = true; bool mock_camera = false; // use a simulated camera instead of Vimba X bool mock_serial = false; // use a simulated motor controller }; struct LoggingConfig { std::string level; // trace|debug|info|warn|error|off; empty => default (CLI overrides) std::string trace; // verbatim wire-trace categories: serial,mqtt,camera,control,all,none }; struct AppConfig { GeneralConfig general; NetworkConfig network; SerialConfig serial; CameraConfig camera; PathsConfig paths; FeaturesConfig features; LoggingConfig logging; // Capture rate in images/second (derived from general.image_interval). double image_rate() const; }; // Loads and validates configuration. // // Secrets: mqtt_user / mqtt_pw are taken from the environment variables // FGC_MQTT_USER / FGC_MQTT_PW when present; the INI values are a fallback. class ConfigLoader { public: // Reads the INI file at `path` (via the bundled inih parser), maps it into // a typed AppConfig, applies environment overrides, and validates it. // Throws std::runtime_error with a clear message on failure. static AppConfig loadFromFile(const std::string& path); // Same mapping/validation logic, but from an already-parsed key->value map // ("Section.name" => value). Exposed for unit testing without file IO. static AppConfig fromMap(const std::map& kv); }; } // namespace fgc