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>
28class qml_tcp_outbound_socket
30 ,
public Nano::Observer
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>>;
50 ossia::net::tcp_client socket;
51 std::atomic_bool alive{
true};
52 ossia::net::framing framing{ossia::net::framing::none};
53 ossia::net::encoding enc{ossia::net::encoding::none};
54 char line_delimiter[8] = {};
58 const ossia::net::outbound_socket_configuration& conf,
59 boost::asio::io_context& ctx,
60 ossia::net::framing f = ossia::net::framing::none,
61 const std::string& delim = {},
62 ossia::net::encoding e = ossia::net::encoding::none)
64 , decoder{
ossia::in_place_index<0>, socket.m_socket}
70 auto sz = std::min(delim.size(), (
size_t)7);
71 std::copy_n(delim.begin(), sz, line_delimiter);
77 case ossia::net::framing::none:
79 case ossia::net::framing::slip:
80 decoder.template emplace<1>(socket.m_socket);
82 case ossia::net::framing::size_prefix:
83 decoder.template emplace<2>(socket.m_socket);
85 case ossia::net::framing::line_delimiter:
86 decoder.template emplace<3>(socket.m_socket);
88 auto& dec = ossia::get<3>(decoder);
89 std::copy_n(line_delimiter, 8, dec.delimiter);
92 case ossia::net::framing::cobs:
93 decoder.template emplace<4>(socket.m_socket);
95 case ossia::net::framing::stx_etx:
96 decoder.template emplace<5>(socket.m_socket);
98 case ossia::net::framing::size_prefix_1byte:
99 decoder.template emplace<6>(socket.m_socket);
101 case ossia::net::framing::size_prefix_2byte_be:
102 decoder.template emplace<7>(socket.m_socket);
104 case ossia::net::framing::size_prefix_2byte_le:
105 decoder.template emplace<8>(socket.m_socket);
107 case ossia::net::framing::size_prefix_4byte_le:
108 decoder.template emplace<9>(socket.m_socket);
110 case ossia::net::framing::fixed_length:
111 decoder.template emplace<10>(socket.m_socket);
113 ossia::get<10>(decoder).frame_size = std::stoul(delim);
118 void write_encoded(
const char* data, std::size_t sz)
123 case ossia::net::framing::none:
124 socket.write(data, sz);
126 case ossia::net::framing::slip:
127 ossia::net::slip_encoder<socket_t>{socket.m_socket}.write(data, sz);
129 case ossia::net::framing::size_prefix:
130 ossia::net::size_prefix_encoder<socket_t>{socket.m_socket}.write(data, sz);
132 case ossia::net::framing::line_delimiter: {
133 ossia::net::line_framing_encoder<socket_t> enc{socket.m_socket};
134 std::copy_n(line_delimiter, 8, enc.delimiter);
138 case ossia::net::framing::cobs:
139 ossia::net::cobs_encoder<socket_t>{socket.m_socket}.write(data, sz);
141 case ossia::net::framing::stx_etx:
142 ossia::net::stx_etx_framing::encoder<socket_t>{socket.m_socket}.write(data, sz);
144 case ossia::net::framing::size_prefix_1byte:
145 ossia::net::size_prefix_1byte_framing::encoder<socket_t>{socket.m_socket}.write(
148 case ossia::net::framing::size_prefix_2byte_be:
149 ossia::net::size_prefix_2byte_be_framing::encoder<socket_t>{socket.m_socket}
152 case ossia::net::framing::size_prefix_2byte_le:
153 ossia::net::size_prefix_2byte_le_framing::encoder<socket_t>{socket.m_socket}
156 case ossia::net::framing::size_prefix_4byte_le:
157 ossia::net::size_prefix_4byte_le_framing::encoder<socket_t>{socket.m_socket}
160 case ossia::net::framing::fixed_length:
161 ossia::net::fixed_length_encoder<socket_t>{socket.m_socket}.write(data, sz);
167 struct receive_callback
169 std::shared_ptr<state> st;
170 QPointer<qml_tcp_outbound_socket> self;
173 void operator()(
const unsigned char* data, std::size_t sz)
const
177 auto buf = apply_decoding(st->enc, data, sz);
179 ossia::qt::run_async(
181 [self = self, buf, cb] {
186 auto engine = qjsEngine(self.get());
188 cb->call({engine->toScriptValue(buf)});
194 bool validate_stream(boost::system::error_code ec)
const
196 if(ec == boost::asio::error::operation_aborted)
198 if(ec == boost::asio::error::eof)
204 qml_tcp_outbound_socket() { }
206 ~qml_tcp_outbound_socket()
210 m_state->alive =
false;
215 bool isOpen() const noexcept {
return m_state !=
nullptr; }
218 const ossia::net::outbound_socket_configuration& conf,
219 boost::asio::io_context& ctx,
220 ossia::net::framing f = ossia::net::framing::none,
221 const std::string& delim = {},
222 ossia::net::encoding e = ossia::net::encoding::none)
224 m_state = std::make_shared<state>(conf, ctx, f, delim, e);
228 if(onOpen.isCallable())
229 m_state->socket.on_open.connect<&qml_tcp_outbound_socket::on_open>(
this);
230 if(onClose.isCallable())
231 m_state->socket.on_close.connect<&qml_tcp_outbound_socket::on_close>(
this);
232 if(onError.isCallable())
233 m_state->socket.on_fail.connect<&qml_tcp_outbound_socket::on_fail>(
this);
234 m_state->socket.connect();
236 catch(
const std::exception& e)
238 if(onError.isCallable())
240 onError.call({QString::fromStdString(e.what())});
245 void write(QByteArray buffer)
250 if(st->enc != ossia::net::encoding::none)
251 buffer = apply_encoding(st->enc, buffer);
252 boost::asio::dispatch(st->socket.m_context, [st, buffer = std::move(buffer)] {
254 st->write_encoded(buffer.data(), buffer.size());
264 boost::asio::post(st->socket.m_context, [st] {
267 st->socket.m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
272 st->socket.m_socket.close();
273 st->socket.on_close();
280 if(!m_state || !m_state->alive)
288 auto self = QPointer{
this};
289 if(onMessage.isCallable())
292 [cb = receive_callback{st, self, &self.data()->onMessage}](
293 auto& decoder)
mutable { decoder.receive(std::move(cb)); },
296 else if(onBytes.isCallable())
299 st->decoder.template emplace<0>(st->socket.m_socket);
300 ossia::get<0>(st->decoder)
301 .receive(receive_callback{st, self, &self.data()->onBytes});
304 ossia::qt::run_async(
305 this, [=,
this] { onOpen.call({qjsEngine(
this)->newQObject(
this)}); },
310 if(!m_state || !m_state->alive)
312 ossia::qt::run_async(
this, [=,
this] { onError.call(); }, Qt::AutoConnection);
316 if(!m_state || !m_state->alive)
318 ossia::qt::run_async(
this, [=,
this] { onClose.call(); }, Qt::AutoConnection);
321 void osc(QByteArray address, QJSValueList values)
327 buffer_writer bw{packet};
328 using send_visitor = ossia::net::osc_value_send_visitor<
332 const std::string addr = address.toStdString();
334 switch(values.size())
337 ossia::value{ossia::impulse{}}.apply(send_visitor{p, addr, bw});
341 auto v = ossia::qt::value_from_js(values[0]);
342 v.apply(send_visitor{p, addr, bw});
346 std::vector<ossia::value> vec;
347 vec.reserve(values.size());
348 for(
const auto& v : values)
349 vec.push_back(
ossia::qt::value_from_js(v));
351 vvec.apply(send_visitor{p, addr, bw});
366 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