/** * @file Hub.h * @brief Central controller: owns the WS client, the source model, trigger * state, capture, zoom caches and history info. Translates StreamHub WS * messages into model updates and emits Qt signals for the UI. */ #pragma once #include "Model.h" #include "WsClient.h" #include "Protocol.h" #include #include #include #include #include namespace shq { /** Hi-res zoom cache (per plot; mirrors the ImGui client's PlotZoomCache). */ struct PlotZoomCache { bool valid = false; bool pending = false; uint32_t reqId = 0; double t0 = 0.0, t1 = 0.0; /* range of valid data */ double reqT0 = 0.0, reqT1 = 0.0; /* range of pending request */ std::vector pts; }; class Hub : public QObject { Q_OBJECT public: explicit Hub(QObject* parent = nullptr); void start(const QString& host, uint16_t port); void reconnect(const QString& host, uint16_t port); /* ---- Model access (GUI thread only) -------------------------------- */ std::vector& sources() { return sources_; } const std::vector& sources() const { return sources_; } TriggerCfgState& trigger() { return trigger_; } const StreamHubClient::HistoryInfoMsg& historyInfo() const { return historyInfo_; } uint32_t maxPoints() const { return maxPoints_; } bool isConnected() const { return ws_.isConnected(); } WsClient& ws() { return ws_; } const StreamHubClient::CaptureFrame* capture() const { return hasCapture_ ? &capture_ : nullptr; } void clearCapture() { hasCapture_ = false; } PlotZoomCache& zoomCache(int i) { return zoomCache_[i]; } PlotZoomCache& histZoomCache(int i) { return histZoomCache_[i]; } int findSource(const std::string& id) const; int findSignal(const Source& src, const std::string& name) const; std::string slotKey(const PlotAssignment& a) const; /* ---- Commands ------------------------------------------------------ */ void sendGetSources(); void sendGetStats(); void sendGetConfig(const std::string& sourceId); void sendAddSource(const std::string& label, const std::string& addr, const std::string& mcast, uint16_t dataPort); void sendRemoveSource(const std::string& id); void sendSaveSources(); void sendSetMaxPoints(uint32_t n); void sendSetTrigger(const std::string& key, const std::string& edge, double threshold, double windowSec, double prePercent, const std::string& mode); void sendArm(); void sendDisarm(); void sendRearm(); void sendTrigStop(bool stopped); void requestZoom(int plotIdx, double t0, double t1, const std::string& csv); void requestHistoryZoom(int plotIdx, double t0, double t1, const std::string& csv); void sendHistoryInfo(); Q_SIGNALS: void connectedChanged(bool connected); void sourcesChanged(); void configChanged(const QString& sourceId); void statsChanged(); void triggerStateChanged(); void captureReceived(); void zoomReceived(int plotIdx); void historyZoomReceived(int plotIdx); void historyInfoChanged(); void maxPointsChanged(uint32_t n); private Q_SLOTS: void onConnected(bool connected); void onText(const QString& json); void onBinary(const QByteArray& data); private: void onSources(const std::string& json); void onConfig(const std::string& json); void onStats(const std::string& json); void onTriggerState(const std::string& json); void onZoom(const std::string& json); void onHistoryZoom(const std::string& json); void onHistoryInfo(const std::string& json); void onMaxPointsUpdated(const std::string& json); WsClient ws_; std::vector sources_; TriggerCfgState trigger_; uint32_t maxPoints_ = 1000000u; StreamHubClient::CaptureFrame capture_; bool hasCapture_ = false; StreamHubClient::HistoryInfoMsg historyInfo_; PlotZoomCache zoomCache_[kMaxPlotSlots]; PlotZoomCache histZoomCache_[kMaxPlotSlots]; uint32_t nextZoomReqId_ = 1; double lastHistInfoReqMs_ = 0.0; }; } /* namespace shq */