2#include <ossia/detail/variant.hpp>
3#include <ossia/network/context.hpp>
4#include <ossia/network/sockets/configuration.hpp>
5#include <ossia/network/sockets/line_framing.hpp>
6#include <ossia/network/sockets/no_framing.hpp>
7#include <ossia/network/sockets/size_prefix_framing.hpp>
8#include <ossia/network/sockets/slip_framing.hpp>
9#include <ossia/network/sockets/tcp_socket.hpp>
11#include <ossia-qt/protocols/utils.hpp>
17#include <nano_observer.hpp>
23class qml_tcp_outbound_socket
25 ,
public Nano::Observer
27 W_OBJECT(qml_tcp_outbound_socket)
29 using socket_t = boost::asio::ip::tcp::socket;
30 using decoder_type = ossia::slow_variant<
31 ossia::net::no_framing::decoder<socket_t>,
32 ossia::net::slip_decoder<socket_t>,
33 ossia::net::size_prefix_decoder<socket_t>,
34 ossia::net::line_framing_decoder<socket_t>>;
38 ossia::net::tcp_client socket;
39 std::atomic_bool alive{
true};
40 ossia::net::framing framing{ossia::net::framing::none};
41 char line_delimiter[8] = {};
45 const ossia::net::outbound_socket_configuration& conf,
46 boost::asio::io_context& ctx,
47 ossia::net::framing f = ossia::net::framing::none,
48 const std::string& delim = {})
50 , decoder{
ossia::in_place_index<0>, socket.m_socket}
55 auto sz = std::min(delim.size(), (
size_t)7);
56 std::copy_n(delim.begin(), sz, line_delimiter);
62 case ossia::net::framing::none:
64 case ossia::net::framing::slip:
65 decoder.template emplace<1>(socket.m_socket);
67 case ossia::net::framing::size_prefix:
68 decoder.template emplace<2>(socket.m_socket);
70 case ossia::net::framing::line_delimiter:
71 decoder.template emplace<3>(socket.m_socket);
73 auto& dec = ossia::get<3>(decoder);
74 std::copy_n(line_delimiter, 8, dec.delimiter);
80 void write_encoded(
const char* data, std::size_t sz)
85 case ossia::net::framing::none:
86 socket.write(data, sz);
88 case ossia::net::framing::slip: {
89 ossia::net::slip_encoder<socket_t> enc{socket.m_socket};
93 case ossia::net::framing::size_prefix: {
94 ossia::net::size_prefix_encoder<socket_t> enc{socket.m_socket};
98 case ossia::net::framing::line_delimiter: {
99 ossia::net::line_framing_encoder<socket_t> enc{socket.m_socket};
100 std::copy_n(line_delimiter, 8, enc.delimiter);
108 struct receive_callback
110 std::shared_ptr<state> st;
111 QPointer<qml_tcp_outbound_socket> self;
114 void operator()(
const unsigned char* data, std::size_t sz)
const
118 auto buf = QByteArray((
const char*)data, sz);
120 ossia::qt::run_async(
122 [self = self, buf, cb] {
127 auto engine = qjsEngine(self.get());
129 cb->call({engine->toScriptValue(buf)});
135 bool validate_stream(boost::system::error_code ec)
const
137 if(ec == boost::asio::error::operation_aborted)
139 if(ec == boost::asio::error::eof)
145 qml_tcp_outbound_socket() { }
147 ~qml_tcp_outbound_socket()
151 m_state->alive =
false;
156 bool isOpen() const noexcept {
return m_state !=
nullptr; }
159 const ossia::net::outbound_socket_configuration& conf,
160 boost::asio::io_context& ctx,
161 ossia::net::framing f = ossia::net::framing::none,
162 const std::string& delim = {})
164 m_state = std::make_shared<state>(conf, ctx, f, delim);
168 if(onOpen.isCallable())
169 m_state->socket.on_open.connect<&qml_tcp_outbound_socket::on_open>(
this);
170 if(onClose.isCallable())
171 m_state->socket.on_close.connect<&qml_tcp_outbound_socket::on_close>(
this);
172 if(onError.isCallable())
173 m_state->socket.on_fail.connect<&qml_tcp_outbound_socket::on_fail>(
this);
174 m_state->socket.connect();
176 catch(
const std::exception& e)
178 if(onError.isCallable())
180 onError.call({QString::fromStdString(e.what())});
185 void write(QByteArray buffer)
190 boost::asio::dispatch(st->socket.m_context, [st, buffer] {
192 st->write_encoded(buffer.data(), buffer.size());
202 boost::asio::post(st->socket.m_context, [st] {
205 st->socket.m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
210 st->socket.m_socket.close();
211 st->socket.on_close();
218 if(!m_state || !m_state->alive)
226 auto self = QPointer{
this};
227 if(onMessage.isCallable())
230 [cb = receive_callback{st, self, &self.data()->onMessage}](
231 auto& decoder)
mutable { decoder.receive(std::move(cb)); },
234 else if(onBytes.isCallable())
237 st->decoder.template emplace<0>(st->socket.m_socket);
238 ossia::get<0>(st->decoder)
239 .receive(receive_callback{st, self, &self.data()->onBytes});
242 ossia::qt::run_async(
243 this, [=,
this] { onOpen.call({qjsEngine(
this)->newQObject(
this)}); },
248 if(!m_state || !m_state->alive)
250 ossia::qt::run_async(
this, [=,
this] { onError.call(); }, Qt::AutoConnection);
254 if(!m_state || !m_state->alive)
256 ossia::qt::run_async(
this, [=,
this] { onClose.call(); }, Qt::AutoConnection);
259 void osc(QByteArray address, QJSValueList values)
265 buffer_writer bw{packet};
266 using send_visitor = ossia::net::osc_value_send_visitor<
270 const std::string addr = address.toStdString();
272 switch(values.size())
275 ossia::value{ossia::impulse{}}.apply(send_visitor{p, addr, bw});
279 auto v = ossia::qt::value_from_js(values[0]);
280 v.apply(send_visitor{p, addr, bw});
284 std::vector<ossia::value> vec;
285 vec.reserve(values.size());
286 for(
const auto& v : values)
287 vec.push_back(
ossia::qt::value_from_js(v));
289 vvec.apply(send_visitor{p, addr, bw});
304 std::shared_ptr<state> m_state;
The value class.
Definition value.hpp:173
Definition qml_device.cpp:43
Full information about a parameter.
Definition parameter_data.hpp:61