2#include <ossia/detail/variant.hpp>
3#include <ossia/network/context.hpp>
4#include <ossia/network/sockets/configuration.hpp>
5#include <ossia/network/sockets/encoding.hpp>
6#include <ossia/network/sockets/cobs_framing.hpp>
7#include <ossia/network/sockets/fixed_length_framing.hpp>
8#include <ossia/network/sockets/line_framing.hpp>
9#include <ossia/network/sockets/no_framing.hpp>
10#include <ossia/network/sockets/size_prefix_framing.hpp>
11#include <ossia/network/sockets/slip_framing.hpp>
12#include <ossia/network/sockets/stx_etx_framing.hpp>
13#include <ossia/network/sockets/tcp_socket.hpp>
14#include <ossia/network/sockets/var_size_prefix_framing.hpp>
16#include <ossia-qt/protocols/utils.hpp>
22#include <nano_observer.hpp>
30class qml_tcp_outbound_socket :
public QObject
32 W_OBJECT(qml_tcp_outbound_socket)
34 using socket_t = boost::asio::ip::tcp::socket;
35 using decoder_type = ossia::slow_variant<
36 ossia::net::no_framing::decoder<socket_t>,
37 ossia::net::slip_decoder<socket_t>,
38 ossia::net::size_prefix_decoder<socket_t>,
39 ossia::net::line_framing_decoder<socket_t>,
40 ossia::net::cobs_decoder<socket_t>,
41 ossia::net::stx_etx_framing::decoder<socket_t>,
42 ossia::net::size_prefix_1byte_framing::decoder<socket_t>,
43 ossia::net::size_prefix_2byte_be_framing::decoder<socket_t>,
44 ossia::net::size_prefix_2byte_le_framing::decoder<socket_t>,
45 ossia::net::size_prefix_4byte_le_framing::decoder<socket_t>,
46 ossia::net::fixed_length_decoder<socket_t>>;
58 ossia::net::tcp_client socket;
59 std::atomic_bool alive{
true};
62 std::atomic_bool closed{
false};
63 ossia::net::framing framing{ossia::net::framing::none};
64 ossia::net::encoding enc{ossia::net::encoding::none};
65 char line_delimiter[8] = {};
69 const ossia::net::outbound_socket_configuration& conf,
70 boost::asio::io_context& ctx,
71 ossia::net::framing f = ossia::net::framing::none,
72 const std::string& delim = {},
73 ossia::net::encoding e = ossia::net::encoding::none)
75 , decoder{ossia::in_place_index<0>, socket.m_socket}
81 auto sz = std::min(delim.size(), (
size_t)7);
82 std::copy_n(delim.begin(), sz, line_delimiter);
88 case ossia::net::framing::none:
90 case ossia::net::framing::slip:
91 decoder.template emplace<1>(socket.m_socket);
93 case ossia::net::framing::size_prefix:
94 decoder.template emplace<2>(socket.m_socket);
96 case ossia::net::framing::line_delimiter:
97 decoder.template emplace<3>(socket.m_socket);
99 auto& dec = ossia::get<3>(decoder);
100 std::copy_n(line_delimiter, 8, dec.delimiter);
103 case ossia::net::framing::cobs:
104 decoder.template emplace<4>(socket.m_socket);
106 case ossia::net::framing::stx_etx:
107 decoder.template emplace<5>(socket.m_socket);
109 case ossia::net::framing::size_prefix_1byte:
110 decoder.template emplace<6>(socket.m_socket);
112 case ossia::net::framing::size_prefix_2byte_be:
113 decoder.template emplace<7>(socket.m_socket);
115 case ossia::net::framing::size_prefix_2byte_le:
116 decoder.template emplace<8>(socket.m_socket);
118 case ossia::net::framing::size_prefix_4byte_le:
119 decoder.template emplace<9>(socket.m_socket);
121 case ossia::net::framing::fixed_length:
122 decoder.template emplace<10>(socket.m_socket);
124 ossia::get<10>(decoder).frame_size = std::stoul(delim);
129 void write_encoded(
const char* data, std::size_t sz)
134 case ossia::net::framing::none:
135 socket.write(data, sz);
137 case ossia::net::framing::slip:
138 ossia::net::slip_encoder<socket_t>{socket.m_socket}.write(data, sz);
140 case ossia::net::framing::size_prefix:
141 ossia::net::size_prefix_encoder<socket_t>{socket.m_socket}.write(data, sz);
143 case ossia::net::framing::line_delimiter: {
144 ossia::net::line_framing_encoder<socket_t> enc{socket.m_socket};
145 std::copy_n(line_delimiter, 8, enc.delimiter);
149 case ossia::net::framing::cobs:
150 ossia::net::cobs_encoder<socket_t>{socket.m_socket}.write(data, sz);
152 case ossia::net::framing::stx_etx:
153 ossia::net::stx_etx_framing::encoder<socket_t>{socket.m_socket}.write(data, sz);
155 case ossia::net::framing::size_prefix_1byte:
156 ossia::net::size_prefix_1byte_framing::encoder<socket_t>{socket.m_socket}.write(
159 case ossia::net::framing::size_prefix_2byte_be:
160 ossia::net::size_prefix_2byte_be_framing::encoder<socket_t>{socket.m_socket}
163 case ossia::net::framing::size_prefix_2byte_le:
164 ossia::net::size_prefix_2byte_le_framing::encoder<socket_t>{socket.m_socket}
167 case ossia::net::framing::size_prefix_4byte_le:
168 ossia::net::size_prefix_4byte_le_framing::encoder<socket_t>{socket.m_socket}
171 case ossia::net::framing::fixed_length:
172 ossia::net::fixed_length_encoder<socket_t>{socket.m_socket}.write(data, sz);
181 qml_tcp_outbound_socket* self{};
189 template <
typename F>
190 void post_to_qt(F&& f)
194 ossia::qt::run_async(self, std::forward<F>(f), Qt::AutoConnection);
205 if(alive && !closed.exchange(
true))
210 struct receive_callback
212 std::shared_ptr<state> st;
213 QPointer<qml_tcp_outbound_socket> self;
219 void operator()(
const unsigned char* data, std::size_t sz)
const
221 if(!st->alive || !target)
223 auto buf = apply_decoding(st->enc, data, sz);
227 if(buf.isEmpty() && st->enc != ossia::net::encoding::none)
230 st->post_to_qt([self = self, buf, cb] {
235 auto engine = qjsEngine(self.get());
237 cb->call({engine->toScriptValue(buf)});
242 bool validate_stream(boost::system::error_code ec)
const
246 if(ec == boost::asio::error::operation_aborted)
257 qml_tcp_outbound_socket() { }
259 ~qml_tcp_outbound_socket()
263 m_state->alive =
false;
271 bool isOpen() const noexcept {
return m_state !=
nullptr; }
274 const ossia::net::outbound_socket_configuration& conf,
275 boost::asio::io_context& ctx,
276 ossia::net::framing f = ossia::net::framing::none,
277 const std::string& delim = {},
278 ossia::net::encoding e = ossia::net::encoding::none)
280 m_state = std::make_shared<state>(conf, ctx, f, delim, e);
281 m_state->self =
this;
285 if(onOpen.isCallable())
287 if(onClose.isCallable())
288 m_state->socket.on_close.connect<&state::fire_close>(m_state.get());
289 if(onError.isCallable())
290 m_state->socket.on_fail.connect<&state::fire_fail>(m_state.get());
291 m_state->socket.connect();
293 catch(
const std::exception& e)
295 if(onError.isCallable())
297 onError.call({QString::fromStdString(e.what())});
302 void write(QByteArray buffer)
307 if(st->enc != ossia::net::encoding::none)
308 buffer = apply_encoding(st->enc, buffer);
309 boost::asio::dispatch(st->socket.m_context, [st, buffer = std::move(buffer)] {
311 st->write_encoded(buffer.data(), buffer.size());
321 boost::asio::post(st->socket.m_context, [st] {
324 st->socket.m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
329 st->socket.m_socket.close();
339 if(!m_state || !m_state->alive)
347 auto self = QPointer{
this};
350 if(onMessage.isCallable())
353 [cb = receive_callback{st, self, &self.data()->onMessage}](
354 auto& decoder)
mutable { decoder.receive(std::move(cb)); },
359 QJSValue* target = onBytes.isCallable() ? &self.data()->onBytes :
nullptr;
360 st->decoder.template emplace<0>(st->socket.m_socket);
361 ossia::get<0>(st->decoder).receive(receive_callback{st, self, target});
364 ossia::qt::run_async(
365 this, [=,
this] { onOpen.call({qjsEngine(
this)->newQObject(
this)}); },
370 if(!m_state || !m_state->alive)
372 ossia::qt::run_async(
this, [=,
this] { onError.call(); }, Qt::AutoConnection);
376 if(!m_state || !m_state->alive)
378 ossia::qt::run_async(
this, [=,
this] { onClose.call(); }, Qt::AutoConnection);
381 void osc(QByteArray address, QJSValueList values)
387 buffer_writer bw{packet};
388 using send_visitor = ossia::net::osc_value_send_visitor<
392 const std::string addr = address.toStdString();
394 switch(values.size())
397 ossia::value{ossia::impulse{}}.apply(send_visitor{p, addr, bw});
401 auto v = ossia::qt::value_from_js(values[0]);
402 v.apply(send_visitor{p, addr, bw});
406 std::vector<ossia::value> vec;
407 vec.reserve(values.size());
408 for(
const auto& v : values)
409 vec.push_back(
ossia::qt::value_from_js(v));
411 vvec.apply(send_visitor{p, addr, bw});
426 std::shared_ptr<state> m_state;
429inline void qml_tcp_outbound_socket::state::fire_open()
431 std::lock_guard g{qt_mutex};
436inline void qml_tcp_outbound_socket::state::fire_fail()
438 std::lock_guard g{qt_mutex};
443inline void qml_tcp_outbound_socket::state::fire_close()
445 std::lock_guard g{qt_mutex};
The value class.
Definition value.hpp:173
Definition qml_device.cpp:43
Full information about a parameter.
Definition parameter_data.hpp:61
Definition qml_tcp_outbound_socket.hpp:57
void fire_open()
Nano slots, always on the asio thread.
Definition qml_tcp_outbound_socket.hpp:429
std::mutex qt_mutex
Definition qml_tcp_outbound_socket.hpp:180
void notify_closed()
Single-shot close notification, whoever observed it first.
Definition qml_tcp_outbound_socket.hpp:203