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
34 ~qml_websocket_outbound_socket()
35 {
36 if(m_state)
37 {
38 m_state->alive = false;
39 close();
40 }
41 }
42
43 bool isOpen() const noexcept { return m_state && m_state->client; }
44
45 void open(
46 const ossia::net::outbound_socket_configuration& conf,
47 boost::asio::io_context& ctx)
48 {
49 m_state = std::make_shared<state>();
50 m_state->url = "ws://" + conf.host + ":" + std::to_string(conf.port); // FIXME wss
51 auto st = m_state;
52 auto self = QPointer{this};
53 m_state->client = std::make_unique<ossia::net::websocket_client>(
54 ctx, [st, self](auto hdl, auto opcode, const std::string& msg) {
55 if(!st->alive)
56 return;
57 if(auto* ptr = self.get())
58 ptr->on_message(hdl, opcode, msg);
59 });
60
61 if(onOpen.isCallable())
62 m_state->client->on_open.connect<&qml_websocket_outbound_socket::on_open>(this);
63 if(onClose.isCallable())
64 m_state->client->on_close.connect<&qml_websocket_outbound_socket::on_close>(this);
65 if(onError.isCallable())
66 m_state->client->on_fail.connect<&qml_websocket_outbound_socket::on_fail>(this);
67
68 m_state->client->connect(m_state->url);
69 }
70
71 void on_message(auto hdl, auto opcode, const std::string& msg)
72 {
73 if(opcode == websocketpp::frame::opcode::text && onTextMessage.isCallable())
74 {
75 onTextMessage.call({QString::fromStdString(msg)});
76 }
77 else if(opcode == websocketpp::frame::opcode::binary && onBinaryMessage.isCallable())
78 {
79 onBinaryMessage.call(
80 {qjsEngine(this)->toScriptValue(QByteArray(msg.data(), msg.size()))});
81 }
82 }
83
84 void on_open()
85 {
86 if(!m_state || !m_state->alive)
87 return;
88 ossia::qt::run_async(
89 this, [=, this] { onOpen.call({qjsEngine(this)->newQObject(this)}); },
90 Qt::AutoConnection);
91 }
92 void on_fail()
93 {
94 if(!m_state || !m_state->alive)
95 return;
96 ossia::qt::run_async(this, [=, this] { onError.call(); }, Qt::AutoConnection);
97 }
98 void on_close()
99 {
100 if(!m_state || !m_state->alive)
101 return;
102 ossia::qt::run_async(this, [=, this] { onClose.call(); }, Qt::AutoConnection);
103 }
104
105 void write(QString message)
106 {
107 if(!m_state)
108 return;
109 auto st = m_state;
110 boost::asio::dispatch(
111 st->client->context(),
112 [st, msg = message.toStdString()] {
113 if(st->alive)
114 st->client->send_message(msg);
115 });
116 }
117 W_SLOT(write)
118
119 void writeBinary(QByteArray buffer)
120 {
121 if(!m_state)
122 return;
123 auto st = m_state;
124 boost::asio::dispatch(
125 st->client->context(),
126 [st, buf = std::string(buffer.data(), buffer.size())] {
127 if(st->alive)
128 st->client->send_binary_message(buf);
129 });
130 }
131 W_SLOT(writeBinary)
132
133 void close()
134 {
135 if(!m_state)
136 return;
137 m_state->client->stop();
138 if(onClose.isCallable())
139 onClose.call();
140 }
141 W_SLOT(close)
142
143 // FIXME
144 // void osc(QByteArray address, QJSValueList values) { this->send_osc(address, values); }
145 // W_SLOT(osc)
146
147 QJSValue onOpen;
148 QJSValue onClose;
149 QJSValue onError;
150 QJSValue onTextMessage;
151 QJSValue onBinaryMessage;
152
153private:
154 std::shared_ptr<state> m_state;
155};
156
157}
Definition qml_device.cpp:43