#pragma once #include #include namespace fgc { // Remote control input (was struct mqtt_sub_data). The *_available flags mark // whether a fresh value arrived since the last poll(). struct ControlCommand { bool control_code_available = false; int control_code = 0; // 0 = automatic sweep, 1 = directed to target heading bool heading_available = false; std::string target_heading; // kept as string; the yaw target in degrees }; // Notification published after an image is captured and saved. struct CamEvent { std::string tower; // tower / FWT name std::string camera; // "RGB" / "ACR" / "NIR" int heading_decideg = 0; // yaw heading * 10 (one decimal as integer) int pitch_decideg = 0; // pitch elevation * 10 (one decimal as integer) long long timestamp_ms = 0; // Unix epoch ms; matches the image filename }; // A console command that arrived over MQTT, on GGS/FWT//Command. It is // run by the same handleCommand() as a typed one, on the control thread. struct RemoteCommand { std::string id; // chosen by the sender, echoed in the reply; may be empty std::string cmd; // the command line, exactly as typed at the console long long received_ms = 0; bool retained = false; // a retained message: never run (it would re-run on reconnect) std::string error; // non-empty: the payload could not be read; reply, do not run }; // The tower's answer, on GGS/FWT//CommandReply. struct CommandReply { std::string id; std::string cmd; std::string level = "info"; // most severe line: info / warn / error std::vector lines; // what the command logged while it ran bool truncated = false; }; // What became of an outbound message. Dropped and Failed are separated because // they mean different things operationally: Dropped is "the link was down and we // discarded it silently", Failed is "the broker or library rejected it". // Inbound messages are always Ok. enum class Delivery { Ok, Dropped, Failed }; // One message that crossed the control channel, for the connectivity view. struct ChannelMessage { long long timestamp_ms = 0; bool outbound = true; // PUB (ours) vs RX (broker's) Delivery delivery = Delivery::Ok; std::string topic; std::string payload; }; // Cumulative channel counters since start-up. struct ChannelStats { long long published_ok = 0; long long published_dropped = 0; // attempted while disconnected: silently discarded long long published_failed = 0; // the broker/library rejected it long long received = 0; long long connects = 0; // successful (re)connections long long last_connect_ms = 0, last_loss_ms = 0; std::string last_status_payload; // what actually went out on StatusCode }; // Abstraction over the remote control/telemetry channel. Implemented by // MqttControlChannel (Eclipse Paho) and NullControlChannel (no broker; used // for development - publishes are dropped and poll() yields a default). class IControlChannel { public: virtual ~IControlChannel() = default; // Returns true once usable. NullControlChannel always succeeds. virtual bool connect() = 0; virtual void disconnect() = 0; virtual bool connected() const = 0; virtual void publishStatus(int code) = 0; virtual void publishCamEvent(const CamEvent& event) = 0; // Latest control input; clears the *_available flags so each update is // acted on once. virtual ControlCommand poll() = 0; // Commands received since the last call, oldest first; clears them. And the // answer to one. Defaults: none / dropped, for a channel that takes no commands. virtual std::vector takeCommands() { return {}; } virtual void publishCommandReply(const CommandReply& /*reply*/) {} // Recent traffic and counters for the connectivity ('m') view. Default: none, // so an implementation that keeps no history needs no changes. virtual std::vector recentMessages() const { return {}; } virtual ChannelStats channelStats() const { return {}; } }; } // namespace fgc