OSVR Framework (Internal Development Docs)  0.6-1962-g59773924
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
ServerImpl.cpp
Go to the documentation of this file.
1 
11 // Copyright 2014 Sensics, Inc.
12 //
13 // Licensed under the Apache License, Version 2.0 (the "License");
14 // you may not use this file except in compliance with the License.
15 // You may obtain a copy of the License at
16 //
17 // http://www.apache.org/licenses/LICENSE-2.0
18 //
19 // Unless required by applicable law or agreed to in writing, software
20 // distributed under the License is distributed on an "AS IS" BASIS,
21 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 // See the License for the specific language governing permissions and
23 // limitations under the License.
24 
25 // Internal Includes
26 #include "ServerImpl.h"
27 #include "../Connection/VrpnConnectionKind.h"
33 #include <osvr/Common/Tracing.h>
38 #include <osvr/Util/LogNames.h>
39 #include <osvr/Util/Logger.h>
40 #include <osvr/Util/MessageKeys.h>
41 #include <osvr/Util/Microsleep.h>
42 #include <osvr/Util/PortFlags.h>
44 #include <osvr/Util/Verbosity.h>
45 
46 #include "osvr/Server/display_json.h"
47 
48 // Library/third-party includes
49 #include <boost/variant.hpp>
50 #include <json/reader.h>
51 #include <vrpn_ConnectionPtr.h>
52 
53 // Standard includes
54 #include <functional>
55 #include <stdexcept>
56 
57 namespace osvr {
58 namespace server {
59  static vrpn_ConnectionPtr
61  vrpn_ConnectionPtr ret;
62  if (std::string(conn->getConnectionKindID()) ==
63  osvr::connection::getVRPNConnectionKindID()) {
64  ret = vrpn_ConnectionPtr(
65  static_cast<vrpn_Connection *>(conn->getUnderlyingObject()));
66  }
67  return ret;
68  }
70  boost::optional<std::string> const &host,
71  boost::optional<int> const &port)
72  : m_conn(conn), m_ctx(make_shared<pluginhost::RegistrationContext>()),
73  m_host(host.get_value_or("localhost")),
74  m_port(port.get_value_or(util::UseDefaultPort)),
75  m_log(util::log::make_logger(util::log::OSVR_SERVER_LOG)) {
76  if (!m_conn) {
77  throw std::logic_error(
78  "Can't pass a null ConnectionPtr into Server constructor!");
79  }
81 
82  // Get the underlying VRPN connection, and make sure it's OK.
83  auto vrpnConn = getVRPNConnection(m_conn);
84 
85  if (!(vrpnConn->doing_okay())) {
86  throw ServerCreationFailure();
87  }
88 
89  // Set up system device/system component
90  m_systemDevice = common::createServerDevice(
92 
93  m_systemComponent =
94  m_systemDevice->addComponent(common::SystemComponent::create());
95  m_systemComponent->registerClientRouteUpdateHandler(
96  &ServerImpl::m_handleUpdatedRoute, this);
97 
98  // Things to do when we get a new incoming connection
99  // No longer doing hardware detect unconditionally here - see
100  // triggerHardwareDetect()
101  m_commonComponent =
102  m_systemDevice->addComponent(common::CommonComponent::create());
103  m_commonComponent->registerPingHandler([&] { m_queueTreeSend(); });
104 
105  // Set up the default display descriptor.
106  m_tree.getNodeByPath("/display").value() =
108  // Deal with updated device descriptors.
109  m_conn->registerDescriptorHandler([&] { m_handleDeviceDescriptors(); });
110 
111  // Set up handlers to enter/exit idle sleep mode.
112  // Can't do this with the nice wrappers on the CommonComponent of the
113  // system device, I suppose since people aren't really connecting to
114  // that device.
115  vrpnConn->register_handler(
116  vrpnConn->register_message_type(vrpn_got_first_connection),
117  &ServerImpl::m_exitIdle, this);
118  vrpnConn->register_handler(
119  vrpnConn->register_message_type(vrpn_dropped_last_connection),
120  &ServerImpl::m_enterIdle, this);
121  }
122 
124  stop();
125  // Not unregistering idle handlers because doing so caused crashes, and
126  // I think that this object should outlive the connection anyway.
127  }
128 
130  boost::unique_lock<boost::mutex> lock(m_runControl);
131  if (!m_ctx || !m_conn) {
132  throw std::logic_error("Cannot start server - context or "
133  "connection destroyed (probably attempting "
134  "to restart a stopped server)");
135  }
136  m_everStarted = true;
137  m_running = true;
138 
139  // Use a lambda to run the loop.
140  m_thread = boost::thread([&] {
141  bool keepRunning = true;
142  m_mainThreadId = m_thread.get_id();
143  ::util::LoopGuard guard(m_run);
144  do {
145  keepRunning = this->m_loop();
146  } while (keepRunning);
147  m_orderedDestruction();
148  m_running = false;
149  });
150  m_run.signalAndWaitForStart();
151  }
152 
154  start();
155  awaitShutdown();
156  }
157 
158  void ServerImpl::awaitShutdown() { m_thread.join(); }
159 
161  boost::unique_lock<boost::mutex> lock(m_runControl);
162  if (m_everStarted) {
163  m_run.signalAndWaitForShutdown();
164  m_thread.join();
165  m_thread = boost::thread();
166  } else {
167  m_orderedDestruction();
168  }
169  }
170 
172  boost::unique_lock<boost::mutex> lock(m_runControl);
173  m_run.signalShutdown();
174  }
175 
176  void ServerImpl::loadPlugin(std::string const &pluginName) {
177  m_callControlled([&, pluginName] { m_ctx->loadPlugin(pluginName); });
178  }
179 
180  void ServerImpl::loadAutoPlugins() { m_ctx->loadPlugins(); }
181 
183  m_commonComponent->registerPingHandler(
184  [&] { triggerHardwareDetect(); });
185  }
186 
187  void ServerImpl::instantiateDriver(std::string const &plugin,
188  std::string const &driver,
189  std::string const &params) {
190  BOOST_ASSERT_MSG(m_inServerThread(),
191  "This method is only available in the server thread!");
192  m_ctx->instantiateDriver(plugin, driver, params);
193  }
194 
196  m_callControlled([&] { m_triggeredDetect = true; });
197  }
198 
200  if (f) {
201  m_callControlled([&] { m_mainloopMethods.push_back(f); });
202  }
203  }
204 
206  boost::unique_lock<boost::mutex> lock(m_runControl);
207  if (m_everStarted) {
208  throw std::logic_error("Can't call update() if you've ever started "
209  "the server in its own thread!");
210  }
211  m_update();
212  }
213  void ServerImpl::m_update() {
215  m_conn->process();
216  m_systemDevice->update();
217  for (auto &f : m_mainloopMethods) {
218  f();
219  }
220  if (m_triggeredDetect) {
221  m_log->info() << "Performing hardware auto-detection.";
222  common::tracing::markHardwareDetect();
223  m_ctx->triggerHardwareDetect();
224  m_triggeredDetect = false;
225  }
226  if (m_treeDirty) {
227  m_log->debug() << "Path tree updated or connection detected";
228  m_sendTree();
229  m_treeDirty.reset();
230  }
231  }
232 
233  bool ServerImpl::m_loop() {
234  bool shouldContinue;
235  {
238  boost::unique_lock<boost::mutex> lock(m_mainThreadMutex);
239  m_update();
240  shouldContinue = m_run.shouldContinue();
241  }
242 
243  if (m_currentSleepTime > 0) {
244  osvr::util::time::microsleep(m_currentSleepTime);
245  }
246  return shouldContinue;
247  }
248 
249  bool ServerImpl::addRoute(std::string const &routingDirective) {
250  bool wasNew;
251  m_callControlled([&] { wasNew = m_addRoute(routingDirective); });
252  return wasNew;
253  }
254 
255  bool ServerImpl::addAlias(std::string const &path,
256  std::string const &source,
257  common::AliasPriority priority) {
258 
259  bool wasChanged;
260  m_callControlled(
261  [&] { wasChanged = m_addAlias(path, source, priority); });
262  return wasChanged;
263  }
264 
265  bool ServerImpl::addAliases(Json::Value const &aliases,
266  common::AliasPriority priority) {
267  bool wasChanged;
268  m_callControlled([&] { wasChanged = m_addAliases(aliases, priority); });
269  return wasChanged;
270  }
271 
272  void ServerImpl::addExternalDevice(std::string const &path,
273  std::string const &deviceName,
274  std::string const &server,
275  std::string const &descriptor) {
276  Json::Value descriptorVal;
277  Json::Reader reader;
278  if (!reader.parse(descriptor, descriptorVal)) {
279  BOOST_ASSERT_MSG(0, "Should never get here - string descriptor "
280  "handed to us should have been generated from "
281  "a JSON value.");
282  return;
283  }
284  m_callControlled([&] {
286  auto &node = m_tree.getNodeByPath(path);
287 
290  auto elt = common::elements::DeviceElement::createVRPNDeviceElement(
291  deviceName, server);
292  elt.getDescriptor() = descriptorVal;
293  node.value() = elt;
294  m_treeDirty.set();
295 
298  });
299  }
300 
301  bool ServerImpl::addString(std::string const &path,
302  std::string const &value) {
303  bool wasChanged = false;
304  auto newElement =
306  m_callControlled([&] {
307  auto &node = m_tree.getNodeByPath(path);
308  if (!(newElement == node.value())) {
309  m_treeDirty.set();
310  wasChanged = true;
311  node.value() = newElement;
312  }
313  });
314  return wasChanged;
315  }
316 
317  void ServerImpl::m_orderedDestruction() {
318  m_ctx.reset();
319  m_systemComponent = nullptr; // non-owning pointer
320  m_systemDevice.reset();
321  m_conn.reset();
322  }
323 
324  int ServerImpl::m_handleUpdatedRoute(void *userdata, vrpn_HANDLERPARAM p) {
325  auto self = static_cast<ServerImpl *>(userdata);
326  BOOST_ASSERT_MSG(
327  self->m_inServerThread(),
328  "This callback should never happen outside the server thread!");
329 
330  self->m_log->info() << "Got an updated route from a client.";
331  self->m_addRoute(std::string(p.buffer, p.payload_len));
332  return 0;
333  }
334 
335  bool ServerImpl::m_addRoute(std::string const &routingDirective) {
336  bool change =
337  common::addAliasFromRoute(m_tree.getRoot(), routingDirective);
338  m_treeDirty += change;
339  return change;
340  }
341 
342  bool ServerImpl::m_addAlias(std::string const &path,
343  std::string const &source,
344  common::AliasPriority priority) {
346  auto &node = m_tree.getNodeByPath(path);
347  bool change = common::addAlias(node, source, priority);
348  m_treeDirty += change;
349  return change;
350  }
351 
352  bool ServerImpl::m_addAliases(Json::Value const &aliases,
353  common::AliasPriority priority) {
354  bool change = common::AliasProcessor()
355  .setDefaultPriority(priority)
356  .enableWildcard()
357  .process(m_tree.getRoot(), aliases);
358  m_treeDirty += change;
359  return change;
360  }
361  void ServerImpl::m_queueTreeSend() {
362  m_callControlled([&] { m_treeDirty += true; });
363  }
364  void ServerImpl::m_sendTree() {
365 
366  common::tracing::markPathTreeBroadcast();
367  m_systemComponent->sendReplacementTree(m_tree);
368  m_log->info() << "Sent path tree to clients.";
369  }
370 
371  void ServerImpl::setSleepTime(int microseconds) {
372  m_sleepTime = microseconds;
373  }
374 #if 0
375  int ServerImpl::getSleepTime() const { return m_sleepTime; }
376 #endif
377  void ServerImpl::m_handleDeviceDescriptors() {
378  for (auto const &dev : m_conn->getDevices()) {
379  auto const &descriptor = dev->getDeviceDescriptor();
380  if (descriptor.empty()) {
381  m_log->warn() << "Developer Warning: No device descriptor for "
382  << dev->getName();
383  } else {
385  m_tree, dev->getName(), descriptor, m_port, m_host);
386  }
387  }
388  }
389 
390  int ServerImpl::m_exitIdle(void *userdata, vrpn_HANDLERPARAM) {
391  auto self = static_cast<ServerImpl *>(userdata);
394  if (self->m_currentSleepTime > self->m_sleepTime) {
395 
396  self->m_log->debug(
397  "Got first client connection, exiting idle mode.");
398  self->m_currentSleepTime = self->m_sleepTime;
399  }
401  self->m_lowLatency.reset(new common::LowLatency);
402  return 0;
403  }
404 
405  int ServerImpl::m_enterIdle(void *userdata, vrpn_HANDLERPARAM) {
406  auto self = static_cast<ServerImpl *>(userdata);
407 
410  if (self->m_currentSleepTime < IDLE_SLEEP_TIME) {
411  self->m_log->debug(
412  "Dropped last client connection, entering idle mode.");
413  self->m_currentSleepTime = IDLE_SLEEP_TIME;
414  }
415 
417  self->m_lowLatency.reset();
418  return 0;
419  }
420 
421 } // namespace server
422 } // namespace osvr
static shared_ptr< CommonComponent > create()
Factory method.
Header.
Header defining some special values that may be passed to some functions that request a port number t...
void stop()
Signal the server to stop, and block until it does so.
Definition: ServerImpl.cpp:160
bool addString(std::string const &path, std::string const &value)
Add a string entry to the tree.
Definition: ServerImpl.cpp:301
Header including PathTree.h and all additional headers needed to define related types.
std::string makeString(const char(&arrayLiteral)[N])
Safely and easily convert a literal array of characters (like from osvr_json_to_c) into a std::string...
void setSleepTime(int microseconds)
Sets the amount of time (in microseconds) that the server loop will sleep each loop when a client is ...
Definition: ServerImpl.cpp:371
void registerMainloopMethod(MainloopMethod f)
Register a method to run during every time through the main loop.
Definition: ServerImpl.cpp:199
bool addAlias(std::string const &path, std::string const &source, common::AliasPriority priority)
Add an alias entry to the tree.
Definition: ServerImpl.cpp:255
std::function< void()> MainloopMethod
A function that can be registered by the server app to run in each mainloop iteration.
Definition: Server.h:52
void update()
The method to just do the update stuff, not in a thread.
Definition: ServerImpl.cpp:205
shared_ptr< Connection > ConnectionPtr
How one must hold a Connection.
Definition: ConnectionPtr.h:40
Header.
Header with a convenience function to make a std::string out of a non-null-terminated char array (str...
void addExternalDevice(std::string const &path, std::string const &deviceName, std::string const &server, std::string const &descriptor)
Add an external device entry manually to the tree.
Definition: ServerImpl.cpp:272
void loadAutoPlugins()
Load all auto-loadable plugins.
Definition: ServerImpl.cpp:180
PathNode & getNodeByPath(std::string const &path)
Returns the node indicated by the path, which must be absolute (begin with a /). Any non-existent nod...
Definition: PathTree.cpp:47
BaseDevicePtr createServerDevice(std::string const &name, vrpn_ConnectionPtr const &conn)
Factory function for a bare server device with no components/interfaces registered by default...
void instantiateDriver(std::string const &plugin, std::string const &driver, std::string const &params)
Instantiate the named driver with parameters.
Definition: ServerImpl.cpp:187
void registerPingHandler(Handler const &handler)
Register a ping handler: a ping is sent from a client device to the corresponding server device upon ...
void reset()
Reset the flag back to false.
Definition: Flag.h:70
bool processDeviceDescriptorForPathTree(PathTree &tree, std::string const &deviceName, std::string const &jsonDescriptor, int listenPort, std::string const &host)
Set up a path tree based on a device descriptor.
ServerImpl(connection::ConnectionPtr const &conn, boost::optional< std::string > const &host, boost::optional< int > const &port)
Constructor.
Definition: ServerImpl.cpp:69
The element type corresponding to a string value such as a JSON string.
vrpn_Connection * getVRPNConnection(OSVR_PluginRegContext ctx)
Retrieves the vrpn_Connection pointer from an OSVR_PluginRegContext.
static void storeConnection(pluginhost::RegistrationContext &ctx, ConnectionPtr conn)
Store a connection pointer in a RegistrationContext.
Definition: Connection.cpp:79
bool addAlias(PathNode &node, std::string const &source, AliasPriority priority)
Definition: PathTree.cpp:103
Header.
static shared_ptr< SystemComponent > create()
Factory method.
Header to include for OSVR-internal usage of the logging mechanism: provides the needed definition of...
Internal, configured header file for verbosity macros.
static const char * deviceName()
Get the special device name to be used with this component.
Header.
void signalStop()
Signal the server to stop (if it is running) but return immediately.
Definition: ServerImpl.cpp:171
void startAndAwaitShutdown()
Launch a thread running the server, and block until the server shuts down.
Definition: ServerImpl.cpp:153
boost::variant< NullElement, AliasElement, SensorElement, InterfaceElement, DeviceElement, PluginElement, StringElement > PathElement
The variant type containing a particular kind of path element.
"Guard"-type class to trace the region of a server update
Definition: Tracing.h:109
~ServerImpl()
Destructor (stops the thread first)
Definition: ServerImpl.cpp:123
bool addAliases(Json::Value const &aliases, common::AliasPriority priority)
Add alias entries to the tree from JSON.
Definition: ServerImpl.cpp:265
bool addRoute(std::string const &routingDirective)
Register a JSON string as a routing directive.
Definition: ServerImpl.cpp:249
OSVR_ReturnCode microsleep(uint64_t microseconds)
Request a thread sleep for at least the given number of microseconds.
Definition: Microsleep.h:56
void set()
Set the flag to true.
Definition: Flag.h:55
void awaitShutdown()
Block until the server shuts down.
Definition: ServerImpl.cpp:158
void setHardwareDetectOnConnection()
Adds the behavior that hardware detection should take place on client connection. ...
Definition: ServerImpl.cpp:182
Header.
void triggerHardwareDetect()
Run all hardware detect callbacks.
Definition: ServerImpl.cpp:195
void start()
Launch a thread running the server.
Definition: ServerImpl.cpp:129
bool processDeviceDescriptorFromExistingDevice(PathNode &devNode, elements::DeviceElement const &devElt)
Set up a path tree based on a device descriptor from an existing DeviceElement node.
void loadPlugin(std::string const &pluginName)
Load named plugin.
Definition: ServerImpl.cpp:176