#pragma once // Minimal WebSocket client (RFC 6455, ws:// only, no TLS) tailored for the // Monitor tab. Background thread does connect + handshake + read loop and // pushes incoming text payloads into a thread-safe queue that the ImGui // thread drains each frame. // // Issue 0086 — first consumer of WS in the C++ dashboards. If a second app // needs WS later, extract this file to cpp/functions/network/ via // fn-constructor. #include #include #include #include #include #include #include #include class WsClient { public: WsClient(); ~WsClient(); // Non-blocking. Spawns background thread that connects to ws://host:port/path // and keeps the connection alive with exponential reconnect backoff. // Safe to call multiple times — second call is a no-op once running. void start(const std::string& host, int port, const std::string& path); // Stop the background thread and tear down the connection. void stop(); // True while a WS connection is up and handshake completed. bool is_connected() const { return state_.load() == State::Connected; } // Epoch seconds of last received frame (for the live LED in the Monitor). long long last_event_ts() const { return last_event_ts_.load(); } // Drain at most `max` queued text payloads. Returns the number drained. // Called once per frame from the render thread. int drain(std::vector& out, int max = 64); // Send a text frame to the server. Returns true if queued for sending. // Used to send {"watermark": N} commands on reconnect. bool send_text(const std::string& payload); private: enum class State { Idle, Connecting, Connected, Backoff, Stopped }; void run(); bool connect_once(); bool handshake(); bool read_loop(); bool send_frame(int opcode, const std::string& payload); std::string host_; int port_ = 0; std::string path_; std::atomic state_{State::Idle}; std::atomic last_event_ts_{0}; std::atomic sock_{-1}; std::thread worker_; std::atomic stop_flag_{false}; std::mutex in_mu_; std::deque in_queue_; std::mutex out_mu_; std::deque out_queue_; // For waking the writer side of read_loop when send_text is called. std::condition_variable out_cv_; };