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