From 17860db8aec878033abe259bb9826462db0b1a5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Sat, 22 Mar 2025 13:23:02 +0100 Subject: [PATCH] feature: oscquery factory --- .../protocols/oscquery/oscquery_factory.cpp | 24 +++ .../protocols/oscquery/oscquery_factory.hpp | 32 ++++ .../oscquery/oscquery_generic_server.cpp | 0 .../oscquery/oscquery_generic_server.hpp | 139 ++++++++++++++++++ 4 files changed, 195 insertions(+) create mode 100644 src/ossia/protocols/oscquery/oscquery_factory.cpp create mode 100644 src/ossia/protocols/oscquery/oscquery_factory.hpp create mode 100644 src/ossia/protocols/oscquery/oscquery_generic_server.cpp create mode 100644 src/ossia/protocols/oscquery/oscquery_generic_server.hpp diff --git a/src/ossia/protocols/oscquery/oscquery_factory.cpp b/src/ossia/protocols/oscquery/oscquery_factory.cpp new file mode 100644 index 00000000000..c78381353fa --- /dev/null +++ b/src/ossia/protocols/oscquery/oscquery_factory.cpp @@ -0,0 +1,24 @@ +#include +#include + +namespace ossia::net +{ +std::unique_ptr make_oscquery_server_protocol( + network_context_ptr ctx, oscquery_server_protocol_configuration config) +{ + struct + { + network_context_ptr&& ctx; + oscquery_server_protocol_configuration& config; + + auto operator()(ossia::net::ws_server_configuration&& conf) const + -> std::unique_ptr + { + return std::make_unique(std::move(ctx), conf); + } + } vis{std::move(ctx), config}; + + using namespace std; + return visit(vis, std::move(config.transport)); +} +} diff --git a/src/ossia/protocols/oscquery/oscquery_factory.hpp b/src/ossia/protocols/oscquery/oscquery_factory.hpp new file mode 100644 index 00000000000..912bf1057db --- /dev/null +++ b/src/ossia/protocols/oscquery/oscquery_factory.hpp @@ -0,0 +1,32 @@ +#pragma once +#include +#include +#include +#include + +namespace ossia::net +{ +struct oscquery_server_protocol_configuration +{ + //! Which framing mechanism to use for the query information (the JSON + //! messages) + framing query_framing{framing::slip}; + + //! Which transport is used for the query information (the JSON messages) + ossia::variant< + ws_server_configuration, tcp_configuration, unix_stream_configuration, + serial_configuration> + query_transport; + + using declared_transport = ossia::variant< + udp_receiver_configuration, tcp_configuration, unix_dgram_configuration, + unix_stream_configuration, serial_configuration, ws_server_configuration>; + + //! Which open hosts/ports will be advertised by this server in the HOST_INFO + //! query for receiving OSC messages. + std::vector declared_transports; +}; + +[[nodiscard]] OSSIA_EXPORT std::unique_ptr make_oscquery_server_protocol( + network_context_ptr ctx, oscquery_server_protocol_configuration config); +} diff --git a/src/ossia/protocols/oscquery/oscquery_generic_server.cpp b/src/ossia/protocols/oscquery/oscquery_generic_server.cpp new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/ossia/protocols/oscquery/oscquery_generic_server.hpp b/src/ossia/protocols/oscquery/oscquery_generic_server.hpp new file mode 100644 index 00000000000..e5b57f4cb67 --- /dev/null +++ b/src/ossia/protocols/oscquery/oscquery_generic_server.hpp @@ -0,0 +1,139 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +namespace osc +{ +template +class sender; +class receiver; +} +namespace oscpack +{ +class ReceivedMessage; +class IpEndpointName; +} +namespace ossia +{ +namespace net +{ +class websocket_server; +} +namespace oscquery +{ +class query_answerer; +class get_query_answerer; +struct json_query_answerer; +} +namespace oscquery_asio +{ +template +struct oscquery_generic_client; +//! Implementation of an oscquery server. +class OSSIA_EXPORT oscquery_generic_server_protocol final + : public ossia::net::protocol_base +{ + friend struct oscquery_generic_client; + friend class ossia::oscquery::query_answerer; + friend class ossia::oscquery::get_query_answerer; + friend struct ossia::oscquery::json_query_answerer; + +public: + using connection_handler = std::weak_ptr; + oscquery_generic_server_protocol(ossia::net::network_context_ptr ctx); + ~oscquery_generic_server_protocol() override; + + bool pull(net::parameter_base&) override; + std::future pull_async(net::parameter_base&) override; + void request(net::parameter_base&) override; + bool push(const net::parameter_base&, const ossia::value& v) override; + bool push_raw(const ossia::net::full_parameter_data& parameter_base) override; + bool push_bundle(const std::vector&) override; + bool push_raw_bundle(const std::vector&) override; + bool echo_incoming_message( + const ossia::net::message_origin_identifier&, const ossia::net::parameter_base&, + const ossia::value& v) override; + bool observe(net::parameter_base&, bool) override; + bool observe_quietly(net::parameter_base&, bool) override; + bool update(net::node_base& b) override; + + void set_device(net::device_base& dev) override; + void stop() override; + ossia::net::device_base& get_device() const { return *m_device; } + + Nano::Signal onClientConnected; + Nano::Signal onClientDisconnected; + +private: + // List of connected clients + oscquery_generic_client* find_client(const connection_handler& hdl); + + void add_node(ossia::string_view path, const string_map& parameters); + void remove_node(ossia::string_view path, const std::string& node); + void rename_node(ossia::string_view node, const std::string& new_name); + + // OSC callback + void on_osc_message(const oscpack::ReceivedMessage& m); + void process_raw_osc_data(const char* data, std::size_t sz); + + // Websocket callbacks + void on_connectionOpen(const connection_handler& hdl); + void on_connectionClosed(const connection_handler& hdl); + + // Local device callback + void on_nodeCreated(const ossia::net::node_base&); + void on_nodeRemoved(const ossia::net::node_base&); + void on_parameterChanged(const ossia::net::parameter_base&); + void on_attributeChanged(const ossia::net::node_base&, ossia::string_view attr); + void on_nodeRenamed(const ossia::net::node_base& n, std::string oldname); + + template + bool push_impl(const T& addr, const ossia::value& v); + + bool write_impl(std::string_view data, bool critical); + + void update_zeroconf(); + // Exceptions here will be catched by the server + // which will set appropriate error codes. + ossia::net::server_reply + on_text_ws_message(const connection_handler& hdl, const std::string& message); + ossia::net::server_reply + on_binary_ws_message(const connection_handler& hdl, const std::string& message); + + ossia::net::network_context_ptr m_context; + + // Listening status of the local software + net::listened_parameters m_listening; + + // The clients connected to this server + std::vector m_tcp_clients; + std::vector m_tcp_clients; + std::vector m_serial_clients; + std::vector> m_clients; + std::atomic_int m_clientCount{}; + + ossia::net::device_base* m_device{}; + + // To lock m_clients + mutex_t m_clientsMutex; +}; +} +}