2#include <ossia/detail/variant.hpp>
3#include <ossia/network/context.hpp>
4#include <ossia/network/sockets/cobs_framing.hpp>
5#include <ossia/network/sockets/configuration.hpp>
6#include <ossia/network/sockets/encoding.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/serial_socket.hpp>
11#include <ossia/network/sockets/size_prefix_framing.hpp>
12#include <ossia/network/sockets/slip_framing.hpp>
13#include <ossia/network/sockets/stx_etx_framing.hpp>
14#include <ossia/network/sockets/var_size_prefix_framing.hpp>
16#include <ossia-qt/protocols/utils.hpp>
22#include <nano_observer.hpp>
31class qml_serial_socket :
public QObject
33 W_OBJECT(qml_serial_socket)
39 using socket_type = ossia::slow_variant<
40 ossia::net::serial_socket<ossia::net::no_framing>,
41 ossia::net::serial_socket<ossia::net::size_prefix_framing>,
42 ossia::net::serial_socket<ossia::net::slip_framing>,
43 ossia::net::serial_socket<ossia::net::line_framing>,
44 ossia::net::serial_socket<ossia::net::cobs_framing>,
45 ossia::net::serial_socket<ossia::net::stx_etx_framing>,
46 ossia::net::serial_socket<ossia::net::size_prefix_1byte_framing>,
47 ossia::net::serial_socket<ossia::net::size_prefix_2byte_be_framing>,
48 ossia::net::serial_socket<ossia::net::size_prefix_2byte_le_framing>,
49 ossia::net::serial_socket<ossia::net::size_prefix_4byte_le_framing>,
50 ossia::net::serial_socket<ossia::net::fixed_length_framing>>;
61 std::atomic_bool alive{
true};
62 ossia::net::encoding enc{ossia::net::encoding::none};
67 qml_serial_socket* self{};
76 void post_to_qt(F&& f)
80 ossia::qt::run_async(self, std::forward<F>(f), Qt::AutoConnection);
89 const ossia::net::serial_configuration& conf, boost::asio::io_context& ctx,
90 ossia::net::framing f,
const std::string& delim, std::size_t frame_size,
91 ossia::net::encoding e)
94 : socket{make_socket(conf, ctx, f)}
99 case ossia::net::framing::line_delimiter: {
100 char delimiter[8] = {};
101 std::copy_n(delim.begin(), std::min(delim.size(), std::size_t(7)), delimiter);
103 auto& sock = ossia::get<3>(socket);
104 std::copy_n(delimiter, 8, sock.m_encoder.delimiter);
105 std::copy_n(delimiter, 8, sock.m_decoder.delimiter);
108 case ossia::net::framing::fixed_length:
110 ossia::get<10>(socket).m_decoder.frame_size = frame_size;
117 static socket_type make_socket(
118 const ossia::net::serial_configuration& conf, boost::asio::io_context& ctx,
119 ossia::net::framing f)
124 case ossia::net::framing::none:
125 return socket_type{ossia::in_place_index<0>, conf, ctx};
126 case ossia::net::framing::size_prefix:
127 return socket_type{ossia::in_place_index<1>, conf, ctx};
128 case ossia::net::framing::slip:
129 return socket_type{ossia::in_place_index<2>, conf, ctx};
130 case ossia::net::framing::line_delimiter:
131 return socket_type{ossia::in_place_index<3>, conf, ctx};
132 case ossia::net::framing::cobs:
133 return socket_type{ossia::in_place_index<4>, conf, ctx};
134 case ossia::net::framing::stx_etx:
135 return socket_type{ossia::in_place_index<5>, conf, ctx};
136 case ossia::net::framing::size_prefix_1byte:
137 return socket_type{ossia::in_place_index<6>, conf, ctx};
138 case ossia::net::framing::size_prefix_2byte_be:
139 return socket_type{ossia::in_place_index<7>, conf, ctx};
140 case ossia::net::framing::size_prefix_2byte_le:
141 return socket_type{ossia::in_place_index<8>, conf, ctx};
142 case ossia::net::framing::size_prefix_4byte_le:
143 return socket_type{ossia::in_place_index<9>, conf, ctx};
144 case ossia::net::framing::fixed_length:
145 return socket_type{ossia::in_place_index<10>, conf, ctx};
150 struct receive_callback
152 std::shared_ptr<state> st;
153 QPointer<qml_serial_socket> self;
160 void operator()(
const unsigned char* data, std::size_t sz)
const
162 if(!st->alive || !target)
164 auto buf = apply_decoding(st->enc, data, sz);
166 st->post_to_qt([self = self, buf, cb] {
171 if(
auto engine = qjsEngine(self.get()))
172 cb->call({engine->toScriptValue(buf)});
178 qml_serial_socket() { }
184 m_state->alive =
false;
191 bool isOpen() const noexcept {
return m_state !=
nullptr; }
194 const ossia::net::serial_configuration& conf, boost::asio::io_context& ctx,
195 ossia::net::framing f = ossia::net::framing::none,
const std::string& delim = {},
196 std::size_t frame_size = 0, ossia::net::encoding e = ossia::net::encoding::none)
201 if(!onMessage.isCallable() && onBytes.isCallable())
202 f = ossia::net::framing::none;
204 m_state = std::make_shared<state>(conf, ctx, f, delim, frame_size, e);
205 m_state->self =
this;
207 ossia::visit([st = m_state.get()](
auto& sock) {
208 sock.on_open.template connect<&state::fire_open>(st);
209 sock.on_close.template connect<&state::fire_close>(st);
210 sock.on_fail.template connect<&state::fire_fail>(st);
215 ossia::visit([](
auto& sock) { sock.connect(); }, m_state->socket);
221 m_state->alive =
false;
228 void write(QByteArray buffer)
233 if(st->enc != ossia::net::encoding::none)
234 buffer = apply_encoding(st->enc, buffer);
235 boost::asio::dispatch(context(), [st, buffer = std::move(buffer)] {
238 [&](
auto& sock) { sock.write(buffer.data(), buffer.size()); }, st->socket);
248 boost::asio::dispatch(
249 context(), [st] { ossia::visit([](
auto& sock) { sock.close(); }, st->socket); });
253 void osc(QByteArray address, QJSValueList values)
259 buffer_writer bw{packet};
260 using send_visitor = ossia::net::osc_value_send_visitor<
264 const std::string addr = address.toStdString();
266 switch(values.size())
269 ossia::value{ossia::impulse{}}.apply(send_visitor{p, addr, bw});
273 auto v = ossia::qt::value_from_js(values[0]);
274 v.apply(send_visitor{p, addr, bw});
278 std::vector<ossia::value> vec;
279 vec.reserve(values.size());
280 for(
const auto& v : values)
281 vec.push_back(
ossia::qt::value_from_js(v));
283 vvec.apply(send_visitor{p, addr, bw});
293 if(!m_state || !m_state->alive)
297 auto self = QPointer{
this};
302 QJSValue* target = onMessage.isCallable() ? &self.data()->onMessage
303 : onBytes.isCallable() ? &self.data()->onBytes
305 ossia::visit([cb = receive_callback{st, self, target}](
auto& sock)
mutable {
306 sock.receive(std::move(cb));
309 if(onOpen.isCallable())
310 ossia::qt::run_async(
this, [=,
this] {
311 onOpen.call({qjsEngine(
this)->newQObject(
this)});
312 }, Qt::AutoConnection);
317 if(!m_state || !m_state->alive)
319 if(onError.isCallable())
320 ossia::qt::run_async(
this, [=,
this] { onError.call(); }, Qt::AutoConnection);
325 if(!m_state || !m_state->alive)
327 if(onClose.isCallable())
328 ossia::qt::run_async(
this, [=,
this] { onClose.call(); }, Qt::AutoConnection);
338 boost::asio::io_context& context() const noexcept
340 return ossia::visit([](
auto& sock) -> boost::asio::io_context& {
341 return sock.m_context;
345 std::shared_ptr<state> m_state;
355inline void qml_serial_socket::state::fire_fail()
357 std::lock_guard g{qt_mutex};
362inline void qml_serial_socket::state::fire_close()
364 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_serial_socket.hpp:59
std::mutex qt_mutex
Definition qml_serial_socket.hpp:66
void fire_open()
Nano slots, always on the asio thread.
Definition qml_serial_socket.hpp:348