#ifndef REMOTEGUIDECLIENT_H #define REMOTEGUIDECLIENT_H #include #include #include #include class IVrZeroMQSubscriber; class QUdpSocket; class QTimer; template class QFutureWatcher; class RemoteGuideClient : public QObject { Q_OBJECT public: explicit RemoteGuideClient(int discoveryPort = 5555, QObject* parent = nullptr); ~RemoteGuideClient() override; bool IsConnected() const; bool IsGuidanceRunning() const; QString ServerAddress() const; void SelectModel(const QString& modelType); void StartGuidance(const QString& modelType); void StopGuidance(); void RequestStatus(); signals: void ConnectionStateChanged(bool connected, const QString& message, const QString& deviceName, const QString& address); void StatusReceived(const QJsonObject& status); void GuidanceRunningChanged(bool running); void CommandFinished(const QString& command, bool ok, const QString& message, const QJsonObject& response); private: struct ControlReply { int generation = 0; QString command; bool transportOk = false; QString errorMessage; QJsonObject response; }; void EnsureDiscoverySocket(); void SendDiscovery(); void ReadDiscoveryDatagrams(); bool ConnectToServer(const QString& address, const QJsonObject& serverInfo); void DisconnectServer(const QString& reason); void SendControlCommand(const QString& command, const QJsonObject& arguments = QJsonObject()); void HandleControlFinished(); void HandleStatusPayload(const QByteArray& payload); void SetConnectionMessage(const QString& message); private: int m_discoveryPort = 5555; QUdpSocket* m_discoverySocket = nullptr; QTimer* m_discoveryTimer = nullptr; QTimer* m_heartbeatTimer = nullptr; IVrZeroMQSubscriber* m_subscriber = nullptr; QFutureWatcher* m_requestWatcher = nullptr; QString m_serverAddress; QString m_deviceName; QString m_topic; QString m_connectionMessage; int m_controlPort = 0; int m_publishPort = 0; int m_generation = 0; int m_discoveryAttempt = 0; bool m_connected = false; bool m_guidanceRunning = false; bool m_requestInFlight = false; }; #endif // REMOTEGUIDECLIENT_H