Loading...
Searching...
No Matches
Teleplot.hpp
1#pragma once
2#include <ossia/network/sockets/udp_socket.hpp>
3
4#include <boost/asio/ip/basic_resolver.hpp>
5#include <boost/asio/ip/udp.hpp>
6
7#include <QDebug>
8
9#include <AvndProcesses/AddressTools.hpp>
10namespace avnd_tools
11{
15{
16 halp_meta(name, "Teleplot")
17 halp_meta(author, "ossia team")
18 halp_meta(category, "Monitoring")
19 halp_meta(description, "Forwards a set of device explorer messages to ")
20 halp_meta(c_name, "avnd_teleplot")
21 halp_meta(manual_url, "https://ossia.io/score-docs/processes/teleplot.html")
22 halp_meta(uuid, "e1d5b9a0-4df9-4281-87a6-9f427dfb6e31")
23
24 // Populated automatically from Executor
25 boost::asio::io_context* io_context{};
26
27 struct inputs_t
28 {
29 PatternSelector pattern;
30 struct : halp::lineedit<"Host", "">
31 {
32 void update(Teleplot& self) { self.update(); }
33 } host;
34 } inputs;
35
36 struct
37 {
38 } outputs;
39
40 ~Teleplot() { clear(); }
41
42 std::pair<std::string, uint16_t> resolve_ip(const std::string& host)
43 {
44 try
45 {
46 std::string m_queryPort;
47 auto m_queryHost = host;
48 auto port_idx = m_queryHost.find_last_of(':');
49 if(port_idx != std::string::npos)
50 {
51 m_queryPort = m_queryHost.substr(port_idx + 1);
52 m_queryHost = m_queryHost.substr(0, port_idx);
53 }
54 else
55 m_queryPort = "80";
56
57 boost::asio::io_context io_service;
58 boost::asio::ip::udp::resolver resolver(io_service);
59
60 auto results = resolver.resolve(
61 boost::asio::ip::udp::v4(), m_queryHost, m_queryPort,
62 boost::asio::ip::resolver_base::numeric_service);
63
64 if(!results.empty())
65 {
66 auto addr = results.begin()->endpoint().address().to_string();
67
68 return {addr, std::stoi(m_queryPort)};
69 }
70 else
71 {
72 return {};
73 }
74 }
75 catch(const std::exception& e)
76 {
77 }
78 catch(...)
79 {
80 }
81 return {};
82 }
83
84 void clear()
85 {
86 for(auto& [param, cb] : params)
87 {
88 param->remove_callback(cb);
89 }
90 params.clear();
91 }
92
93 void update()
94 {
95 // 1. Remove existing callbacks
96 clear();
97
98 // 2. recreate socket
99 {
100 // auto split = QString::fromStdString(inputs.host.value).split(':');
101 auto [ip, port] = resolve_ip(inputs.host.value);
102 if(!ip.empty() && port > 1)
103 {
104 socket = std::make_shared<ossia::net::udp_send_socket>(
105 ossia::net::outbound_socket_configuration{.host = ip, .port = port},
106 *io_context);
107 socket->connect();
108 }
109 }
110
111 // 3. Recreate callbacks
112 for(auto nodes : this->roots)
113 {
114 if(auto p = nodes->get_parameter(); p && !params.contains(p))
115 {
116 auto it = p->add_callback(
117 [p, socket = socket](const ossia::value& v) { push(*socket, *p, v); });
118 params.emplace(p, it);
119 }
120 }
121 }
122
123 // NOTE: this function can be called from any thread
124 static void push(
125 ossia::net::udp_send_socket& socket, const ossia::net::parameter_base& param,
126 const ossia::value& v)
127 {
128 using clk = std::chrono::system_clock;
129
130 thread_local fmt::memory_buffer buf;
131 buf.clear();
132 buf.reserve(512);
133
134 struct
135 {
136 const std::string& addr;
137 int64_t t = std::chrono::duration_cast<std::chrono::milliseconds>(
138 clk::now().time_since_epoch())
139 .count();
140
141 void operator()(ossia::impulse v)
142 {
143 fmt::format_to(fmt::appender(buf), "{}:{}:1\n", addr, t);
144 }
145 void operator()(int v)
146 {
147 fmt::format_to(fmt::appender(buf), "{}:{}:{}\n", addr, t, v);
148 }
149 void operator()(float v)
150 {
151 fmt::format_to(fmt::appender(buf), "{}:{}:{}\n", addr, t, v);
152 }
153 void operator()(std::string v)
154 {
155 fmt::format_to(fmt::appender(buf), "{}:{}:{}\n", addr, t, v);
156 }
157 void operator()(bool v)
158 {
159 fmt::format_to(fmt::appender(buf), "{}:{}:{}\n", addr, t, v ? 1 : 0);
160 }
161 void operator()(ossia::vec2f v)
162 {
163 fmt::format_to(
164 fmt::appender(buf), "{}.x:{}:{}\n{}.y:{}:{}\n", addr, t, v[0], addr, t,
165 v[1]);
166 }
167 void operator()(ossia::vec3f v)
168 {
169 fmt::format_to(
170 fmt::appender(buf), "{}.x:{}:{}\n{}.y:{}:{}\n{}.z:{}:{}\n", addr, t, v[0],
171 addr, t, v[1], addr, t, v[2]);
172 }
173 void operator()(ossia::vec4f v)
174 {
175 fmt::format_to(
176 fmt::appender(buf), "{}.x:{}:{}\n{}.y:{}:{}\n{}.z:{}:{}\n{}.w:{}:{}\n", addr,
177 t, v[0], addr, t, v[1], addr, t, v[2], addr, t, v[3]);
178 }
179 void operator()(const std::vector<ossia::value>& v)
180 {
181 int i = 0;
182 for(auto& val : v)
183 {
184 // FIXME this does not handle multidimensional arrays / recursivity.
185 fmt::format_to(
186 fmt::appender(buf), "{}[{}]:{}:{}\n", addr, i, t,
187 ossia::convert<double>(val));
188 i++;
189 }
190 }
191 void operator()(const ossia::value_map_type& v)
192 {
193 for(auto& [k, val] : v)
194 {
195 // FIXME this does not handle multidimensional arrays / recursivity.
196 fmt::format_to(
197 fmt::appender(buf), "{}[{}]:{}:{}\n", addr, k, t,
198 ossia::convert<double>(val));
199 }
200 }
201 void operator()() { }
202
203 } vis{.addr = param.get_node().osc_address()};
204 v.apply(vis);
205
206 socket.write(buf.begin(), buf.size());
207 }
208
209 void operator()()
210 {
211 if(!socket)
212 {
213 socket = std::make_shared<ossia::net::udp_send_socket>(
214 ossia::net::outbound_socket_configuration{.host = "127.0.0.1", .port = 47269},
215 *io_context);
216 socket->connect();
217 }
218
219 if(!m_path)
220 return;
221
222 // FIXME do this in an update callback instead
223 // Create callbacks for added nodes
224 for(auto nodes : this->roots)
225 {
226 if(auto p = nodes->get_parameter(); p && !params.contains(p))
227 {
228 auto it = p->add_callback(
229 [p, socket = socket](const ossia::value& v) { push(*socket, *p, v); });
230 params.emplace(p, it);
231 }
232 }
233
234 // Remove callbacks for removed nodes
235 for(auto it = params.begin(); it != params.end();)
236 {
237 if(!ossia::contains(this->roots, &(it->first)->get_node()))
238 {
239 it->first->remove_callback(it->second);
240 it = params.erase(it);
241 }
242 else
243 {
244 ++it;
245 }
246 }
247 }
248
249 boost::container::flat_map<
250 ossia::net::parameter_base*,
251 ossia::callback_container<ossia::value_callback>::iterator>
252 params;
253 std::shared_ptr<ossia::net::udp_send_socket> socket;
254};
255}
Definition AddressTools.hpp:21
Definition AddressTools.hpp:28
Definition Teleplot.hpp:28
Definition Teleplot.hpp:15