OSSIA
Open Scenario System for Interactive Application
Loading...
Searching...
No Matches
qml_ws_outbound_socket.hpp
1#pragma once
2#include <ossia/network/context.hpp>
3#include <ossia/network/sockets/udp_socket.hpp>
4#include <ossia/network/sockets/websocket_client.hpp>
5
6#include <ossia-qt/protocols/utils.hpp>
7
8#include <QJSValue>
9#include <QObject>
10#include <QQmlEngine>
11
12#include <nano_observer.hpp>
13
14#include <verdigris>
15
16namespace ossia::qt
17{
18class qml_websocket_outbound_socket
19 : public QObject
20 , public Nano::Observer
21 , public protocols_sender
22{
23 W_OBJECT(qml_websocket_outbound_socket)
24public:
25 struct state
26 {
27 std::string url;
28 std::unique_ptr<ossia::net::websocket_client> client;
29 std::atomic_bool alive{true};
30 };
31
32 qml_websocket_outbound_socket(
33 const ossia::net::outbound_socket_configuration& conf,
34 boost::asio::io_context& ctx)
35 : m_state{std::make_shared<state>()}
36 {
37 m_state->url = "ws://" + conf.host + ":" + std::to_string(conf.port); // FIXME wss
38 auto st = m_state;
39 auto self = QPointer{this};
40 m_state->client = std::make_unique<ossia::net::websocket_client>(
41 ctx, [st, self](auto hdl, auto opcode, const std::string& msg) {
42 if(!st->alive)
43 return;
44 if(auto* ptr = self.get())
45 ptr->on_message(hdl, opcode, msg);
46 });
47
48 if(onOpen.isCallable())
49 m_state->client->on_open.connect<&qml_websocket_outbound_socket::on_open>(this);
50 if(onClose.isCallable())
51 m_state->client->on_close.connect<&qml_websocket_outbound_socket::on_close>(this);
52 if(onError.isCallable())
53 m_state->client->on_fail.connect<&qml_websocket_outbound_socket::on_fail>(this);
54 }
55
56 ~qml_websocket_outbound_socket() { m_state->alive = false; }
57
58 inline boost::asio::io_context& context() noexcept
59 {
60 return m_state->client->context();
61 }
62
63 void open() { m_state->client->connect(m_state->url); }
64
65 void on_message(auto hdl, auto opcode, const std::string& msg)
66 {
67 if(opcode == websocketpp::frame::opcode::text && onTextMessage.isCallable())
68 {
69 onTextMessage.call({QString::fromStdString(msg)});
70 }
71 else if(opcode == websocketpp::frame::opcode::binary && onBinaryMessage.isCallable())
72 {
73 onBinaryMessage.call(
74 {qjsEngine(this)->toScriptValue(QByteArray(msg.data(), msg.size()))});
75 }
76 }
77
78 void on_open()
79 {
80 if(!m_state->alive)
81 return;
82 ossia::qt::run_async(
83 this, [=, this] { onOpen.call({qjsEngine(this)->newQObject(this)}); },
84 Qt::AutoConnection);
85 }
86 void on_fail()
87 {
88 if(!m_state->alive)
89 return;
90 ossia::qt::run_async(this, [=, this] { onError.call(); }, Qt::AutoConnection);
91 }
92 void on_close()
93 {
94 if(!m_state->alive)
95 return;
96 ossia::qt::run_async(this, [=, this] { onClose.call(); }, Qt::AutoConnection);
97 }
98
99 void write(QString message)
100 {
101 auto st = m_state;
102 boost::asio::dispatch(
103 st->client->context(),
104 [st, msg = message.toStdString()] {
105 if(st->alive)
106 st->client->send_message(msg);
107 });
108 }
109 W_SLOT(write)
110
111 void writeBinary(QByteArray buffer)
112 {
113 auto st = m_state;
114 boost::asio::dispatch(
115 st->client->context(),
116 [st, buf = std::string(buffer.data(), buffer.size())] {
117 if(st->alive)
118 st->client->send_binary_message(buf);
119 });
120 }
121 W_SLOT(writeBinary)
122
123 void close()
124 {
125 m_state->client->stop();
126 if(onClose.isCallable())
127 onClose.call();
128 }
129 W_SLOT(close)
130
131 // FIXME
132 // void osc(QByteArray address, QJSValueList values) { this->send_osc(address, values); }
133 // W_SLOT(osc)
134
135 QJSValue onOpen;
136 QJSValue onClose;
137 QJSValue onError;
138 QJSValue onTextMessage;
139 QJSValue onBinaryMessage;
140
141private:
142 std::shared_ptr<state> m_state;
143};
144
145}
Definition qml_device.cpp:43