#pragma once #include "fgc/IControlChannel.h" #include #include #include #include #include namespace fgc { // A bounded, thread-safe record of recent control-channel traffic, plus the // cumulative counters behind the connectivity view. // // It lives here rather than inside MqttControlChannel for two reasons: the null // channel keeps the same history (so the view works with --no-mqtt), and holding // no MQTT types means the counter logic is unit-testable with WITH_MQTT=OFF. // // Its mutex is its own, deliberately not MqttControlChannel's: that one guards // sub_data_ and is taken by poll() on the control thread, while record() is // called from the Paho callback thread and from publish() on both the control // and ImagePipeline worker threads. Separate locks also keep the history lock // off the 3-second publish wait. class ChannelHistory { public: // Newest-first eviction bound. Enough to cover a scan's worth of waypoints // without making the per-tick snapshot copy expensive. static constexpr std::size_t kCapacity = 40; // Anything but Delivery::Ok on an outbound message means it never reached the // broker. Those are the entries worth looking for. void record(bool outbound, Delivery delivery, std::string topic, std::string payload); void noteConnect(); void noteLoss(); // What actually went out on the StatusCode topic. The connectivity view used // to infer this from the live control code, which assumes the echo rather // than reporting the send. void noteStatus(std::string payload); // Oldest first, so the view can print it top-to-bottom as it happened. std::vector recent() const; ChannelStats stats() const; private: mutable std::mutex mutex_; std::deque ring_; ChannelStats stats_; }; } // namespace fgc