OSSIA
Open Scenario System for Interactive Application
osc/detail/bundle.hpp
1 #pragma once
2 #include <ossia/detail/buffer_pool.hpp>
4 #include <ossia/network/base/bundle.hpp>
5 #include <ossia/network/base/osc_address.hpp>
6 #include <ossia/network/osc/detail/osc_1_0_policy.hpp>
7 #include <ossia/network/osc/detail/osc_messages.hpp>
8 #include <ossia/network/value/value.hpp>
9 
10 #include <oscpack/osc/OscOutboundPacketStream.h>
11 
12 #include <optional>
13 
14 namespace ossia::net
15 {
16 
17 // Default case
18 template <typename OscPolicy>
19 struct bundle_common_policy
20 {
21  template <typename Addr_T>
22  void
23  operator()(oscpack::OutboundPacketStream& str, ossia::value& val, const Addr_T& addr)
24  {
25  if(val = bound_value(addr, val); val.valid())
26  {
27  str << oscpack::BeginMessageN(osc_address(addr));
28  val.apply(typename OscPolicy::dynamic_policy{{str, addr.get_unit()}});
29  str << oscpack::EndMessage();
30  }
31  }
32 };
33 
34 template <typename OscPolicy>
35 struct bundle_client_policy
36 {
37  template <typename Addr_T>
38  void
39  operator()(oscpack::OutboundPacketStream& str, ossia::value& val, const Addr_T& addr)
40  {
41  if(addr.get_access() == ossia::access_mode::GET)
42  return;
43 
44  bundle_common_policy<OscPolicy>{}(str, val, addr);
45  }
46 };
47 
48 template <typename OscPolicy>
49 using bundle_server_policy = bundle_common_policy<OscPolicy>;
50 
51 // Pre-bounded case
52 template <typename OscPolicy>
53 struct bundle_bounded_common_policy
54 {
55  template <typename Addr_T>
56  void
57  operator()(oscpack::OutboundPacketStream& str, ossia::value& val, const Addr_T& addr)
58  {
59  if(val.valid())
60  {
61  str << oscpack::BeginMessageN(osc_address(addr));
62  val.apply(typename OscPolicy::dynamic_policy{{str, addr.get_unit()}});
63  str << oscpack::EndMessage();
64  }
65  }
66 };
67 
68 template <typename OscPolicy>
69 struct bundle_bounded_client_policy
70 {
71  template <typename Addr_T>
72  void
73  operator()(oscpack::OutboundPacketStream& str, ossia::value& val, const Addr_T& addr)
74  {
75  if(addr.get_access() == ossia::access_mode::GET)
76  return;
77 
78  bundle_bounded_common_policy<OscPolicy>{}(str, val, addr);
79  }
80 };
81 
82 template <typename OscPolicy>
83 using bundle_bounded_server_policy = bundle_bounded_common_policy<OscPolicy>;
84 
85 static inline auto& access_parameter(const ossia::net::parameter_base* p)
86 {
87  return *p;
88 }
89 static inline auto& access_parameter(const ossia::net::full_parameter_data& p)
90 {
91  return p;
92 }
93 static inline auto& access_parameter(const ossia::bundle_element& p)
94 {
95  return *p.parameter;
96 }
97 
98 struct bundle
99 {
100  ossia::buffer_pool::buffer data;
101  bool critical{};
102 };
103 
104 template <typename NetworkPolicy, typename Addresses>
105 std::optional<bundle>
106 make_bundle(NetworkPolicy add_element_to_bundle, const Addresses& addresses)
107 try
108 {
109  bundle ret{ossia::buffer_pool::instance().acquire(max_osc_message_size), false};
110  {
111  oscpack::OutboundPacketStream str(ret.data.data(), max_osc_message_size);
112  str << oscpack::BeginBundleImmediate();
113 
114  ossia::value val;
115  for(const auto& a : addresses)
116  {
117  auto& param = access_parameter(a);
118  ret.critical |= param.get_critical();
119  val = param.value();
120  add_element_to_bundle(str, val, param);
121  }
122  str << oscpack::EndBundle();
123  ret.data.resize(str.Size());
124 
125  // TODO useless condition for now.
126  // But if we know that we are going through ws we can increase the size
127  // beyond 65k. ret.critical |= str.Size() > max_osc_message_size;
128  }
129  return ret;
130 }
131 catch(const oscpack::OutOfBufferMemoryException&)
132 {
133  ossia::logger().error(
134  "make_bundle_client: message too large (limit is {} bytes)", max_osc_message_size);
135  return {};
136 }
137 catch(const std::runtime_error& e)
138 {
139  ossia::logger().error("make_bundle_client: {}", e.what());
140  return {};
141 }
142 catch(...)
143 {
144  ossia::logger().error("make_bundle_client: unknown error");
145  return {};
146 }
147 
148 template <typename NetworkPolicy>
149 std::optional<bundle> make_bundle(
150  NetworkPolicy add_element_to_bundle,
151  const tcb::span<ossia::bundle_element>& addresses)
152 try
153 {
154  bundle ret{ossia::buffer_pool::instance().acquire(max_osc_message_size), false};
155  {
156  oscpack::OutboundPacketStream str(ret.data.data(), max_osc_message_size);
157  str << oscpack::BeginBundleImmediate();
158 
159  for(auto& [p, v] : addresses)
160  {
161  auto& param = *p;
162  ret.critical |= param.get_critical();
163  add_element_to_bundle(str, v, param);
164  }
165  str << oscpack::EndBundle();
166  ret.data.resize(str.Size());
167 
168  // TODO useless condition for now.
169  // But if we know that we are going through ws we can increase the size
170  // beyond 65k. ret.critical |= str.Size() > max_osc_message_size;
171  }
172  return ret;
173 }
174 catch(const oscpack::OutOfBufferMemoryException&)
175 {
176  ossia::logger().error(
177  "make_bundle_client: message too large (limit is {} bytes)", max_osc_message_size);
178  return {};
179 }
180 catch(const std::runtime_error& e)
181 {
182  ossia::logger().error("make_bundle_client: {}", e.what());
183  return {};
184 }
185 catch(...)
186 {
187  ossia::logger().error("make_bundle_client: unknown error");
188  return {};
189 }
190 
191 template <typename NetworkPolicy>
192 bool make_bundle_bounded(
193  NetworkPolicy add_element_to_bundle,
194  const tcb::span<ossia::bundle_element>& addresses, auto callback)
195 {
196  bundle ret{ossia::buffer_pool::instance().acquire(max_osc_message_size), false};
197  try
198  {
199  // FIXME iterate until the size is enough
200  oscpack::OutboundPacketStream str(ret.data.data(), max_osc_message_size);
201  str << oscpack::BeginBundleImmediate();
202 
203  for(auto& [p, v] : addresses)
204  {
205  auto& param = *p;
206  ret.critical |= param.get_critical();
207  add_element_to_bundle(str, v, param);
208  }
209  str << oscpack::EndBundle();
210  ret.data.resize(str.Size());
211 
212  callback(ret);
213 
214  // TODO useless condition for now.
215  // But if we know that we are going through ws we can increase the size
216  // beyond 65k. ret.critical |= str.Size() > max_osc_message_size;
217  }
218  catch(const oscpack::OutOfBufferMemoryException&)
219  {
220  ossia::logger().error(
221  "make_bundle_client: message too large (limit is {} bytes)",
222  max_osc_message_size);
223  }
224  catch(const std::runtime_error& e)
225  {
226  ossia::logger().error("make_bundle_client: {}", e.what());
227  }
228  catch(...)
229  {
230  ossia::logger().error("make_bundle_client: unknown error");
231  }
232  ossia::buffer_pool::instance().release(std::move(ret.data));
233  return true;
234 }
235 }
The parameter_base class.
Definition: ossia/network/base/parameter.hpp:48
The value class.
Definition: value.hpp:173
spdlog::logger & logger() noexcept
Where the errors will be logged. Default is stderr.
Definition: context.cpp:104
@ GET
The value can be retrieved and changed.
bool critical
Means that the node is very important, e.g. a "play" message.
Definition: node_attributes.hpp:92
Full information about a parameter.
Definition: parameter_data.hpp:61