From d1aa763fcd6812d21f33e92fc878c1c068aaa158 Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Fri, 21 Aug 2026 12:26:54 +0800 Subject: [PATCH] =?UTF-8?q?web=5Fserver=20=E5=88=9D=E6=AD=A5=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web_server/src/Graph_WebSocket.cpp | 25 ++++++++++++++++++++++++- web_server/src/Graph_WebSocket.hpp | 6 ++++++ web_server/src/Plot.cpp | 6 ++++++ web_server/src/Plot.hpp | 18 ++++++++++++++++++ 4 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 web_server/src/Plot.cpp create mode 100644 web_server/src/Plot.hpp diff --git a/web_server/src/Graph_WebSocket.cpp b/web_server/src/Graph_WebSocket.cpp index d593316..445515a 100644 --- a/web_server/src/Graph_WebSocket.cpp +++ b/web_server/src/Graph_WebSocket.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include namespace aethera::web { namespace { std::string graph_id_from_path(std::string_view path) { const auto split = path.find_last_of('/'); return split == std::string_view::npos ? std::string{} : std::string(path.substr(split + 1)); } @@ -12,12 +14,33 @@ struct Graph_WebSocket::Private { std::shared_ptr session; /* 连接期间保持图会话存活。 */ const void* owner{}; /* 会话订阅表使用的稳定连接身份。 */ bool attached{}; /* 是否已安装像素订阅。 */ + std::mutex callback_mutex; + std::unordered_map callbacks; }; -Graph_WebSocket::Graph_WebSocket(drogon::WebSocketConnectionPtr connection, std::shared_ptr session) : d(std::make_unique(Private{connection, std::move(session), connection.get(), false})) {} +Graph_WebSocket::Graph_WebSocket(drogon::WebSocketConnectionPtr connection, std::shared_ptr session) : d(std::make_unique()) { d->connection = connection; d->session = std::move(session); d->owner = connection.get(); } Graph_WebSocket::~Graph_WebSocket() { close(); } void Graph_WebSocket::start() { if (d->attached) return; const auto weak = weak_from_this(); d->session->attach(d->owner, [weak](std::string pixels) { const auto socket = weak.lock(); if (!socket) return; const auto connection = socket->d->connection.lock(); if (connection && connection->connected()) connection->send(pixels.data(), pixels.size(), drogon::WebSocketMessageType::Binary); }); d->attached = true; } void Graph_WebSocket::receive(std::string_view message) { const auto json = nlohmann::json::parse(message, nullptr, false); if (json.is_discarded() || !json.is_object()) return; Graph_Event event; if (const auto value = json.find("time"); value != json.end() && value->is_number()) event.time_milliseconds = value->get(); if (const auto value = json.find("width"); value != json.end() && value->is_number_unsigned()) event.width = std::clamp(value->get(), 160U, 1920U); if (const auto value = json.find("height"); value != json.end() && value->is_number_unsigned()) event.height = std::clamp(value->get(), 120U, 1080U); d->session->submit(event); } void Graph_WebSocket::close() { if (!d->attached) return; d->session->detach(d->owner); d->attached = false; } +void Graph_WebSocket::register_callback(std::string key, Callback callback) { + std::lock_guard lock(d->callback_mutex); + d->callbacks.insert_or_assign(std::move(key), std::move(callback)); +} +void Graph_WebSocket::unregister_callback(std::string_view key) { + std::lock_guard lock(d->callback_mutex); + d->callbacks.erase(std::string(key)); +} +void Graph_WebSocket::send(std::string_view key, std::string_view frame) { + Callback callback; + { + std::lock_guard lock(d->callback_mutex); + const auto iterator = d->callbacks.find(std::string(key)); + if (iterator == d->callbacks.end()) return; + callback = iterator->second; + } + callback(frame); +} + Graph_WebSocket_Controller::Graph_WebSocket_Controller(std::shared_ptr value) : registry(std::move(value)) {} void Graph_WebSocket_Controller::handleNewConnection(const drogon::HttpRequestPtr& request, const drogon::WebSocketConnectionPtr& connection) { auto session = registry->acquire(graph_id_from_path(request->path())); if (!session) { connection->shutdown(drogon::CloseCode::kViolation, "Unknown Aethera graph"); return; } auto socket = std::make_shared(connection, std::move(session)); connection->setContext(socket); connection->setPingMessage("aethera-gallery", std::chrono::seconds(20)); socket->start(); } void Graph_WebSocket_Controller::handleNewMessage(const drogon::WebSocketConnectionPtr& connection, std::string&& message, const drogon::WebSocketMessageType& type) { if (type != drogon::WebSocketMessageType::Text || message.size() > 64 * 1024) return; if (const auto socket = connection->getContext()) socket->receive(message); } diff --git a/web_server/src/Graph_WebSocket.hpp b/web_server/src/Graph_WebSocket.hpp index 8230edd..0edbaa8 100644 --- a/web_server/src/Graph_WebSocket.hpp +++ b/web_server/src/Graph_WebSocket.hpp @@ -1,7 +1,9 @@ #pragma once #include "Graph_Session.hpp" #include +#include #include +#include namespace aethera::web { class Graph_WebSocket final : public std::enable_shared_from_this { public: @@ -9,9 +11,13 @@ public: ~Graph_WebSocket(); Graph_WebSocket(const Graph_WebSocket&) = delete; Graph_WebSocket& operator=(const Graph_WebSocket&) = delete; + using Callback = std::function; void start(); void receive(std::string_view message); void close(); + void register_callback(std::string key, Callback callback); + void unregister_callback(std::string_view key); + void send(std::string_view key, std::string_view frame); private: struct Private; std::unique_ptr d; /* Drogon 连接与图会话绑定。 */ diff --git a/web_server/src/Plot.cpp b/web_server/src/Plot.cpp new file mode 100644 index 0000000..83af116 --- /dev/null +++ b/web_server/src/Plot.cpp @@ -0,0 +1,6 @@ +#include "Plot.hpp" +namespace aethera::web { +Plot::Plot(std::string key_value, std::shared_ptr websocket_value) : websocket(std::move(websocket_value)), key(std::move(key_value)) { websocket->register_callback(key, [this](std::string_view event) { on_event(event); }); } +Plot::~Plot() { websocket->unregister_callback(key); } +void Plot::send_frame(std::string_view frame) { websocket->send(key, frame); } +} diff --git a/web_server/src/Plot.hpp b/web_server/src/Plot.hpp new file mode 100644 index 0000000..d89400f --- /dev/null +++ b/web_server/src/Plot.hpp @@ -0,0 +1,18 @@ +#pragma once +#include "Graph_WebSocket.hpp" +#include +#include +namespace aethera::web { +class Plot { +public: + Plot(std::string key, std::shared_ptr websocket); + virtual ~Plot(); + Plot(const Plot&) = delete; + Plot& operator=(const Plot&) = delete; +protected: + virtual void on_event(std::string_view event) = 0; + void send_frame(std::string_view frame); + std::shared_ptr websocket; + std::string key; +}; +}