27 #include "../Connection/VrpnConnectionKind.h"
46 #include "osvr/Server/display_json.h"
49 #include <boost/variant.hpp>
50 #include <json/reader.h>
51 #include <vrpn_ConnectionPtr.h>
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()));
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)) {
77 throw std::logic_error(
78 "Can't pass a null ConnectionPtr into Server constructor!");
83 auto vrpnConn = getVRPNConnection(m_conn);
85 if (!(vrpnConn->doing_okay())) {
95 m_systemComponent->registerClientRouteUpdateHandler(
96 &ServerImpl::m_handleUpdatedRoute,
this);
109 m_conn->registerDescriptorHandler([&] { m_handleDeviceDescriptors(); });
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);
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)");
136 m_everStarted =
true;
140 m_thread = boost::thread([&] {
141 bool keepRunning =
true;
142 m_mainThreadId = m_thread.get_id();
143 ::util::LoopGuard guard(m_run);
145 keepRunning = this->m_loop();
146 }
while (keepRunning);
147 m_orderedDestruction();
150 m_run.signalAndWaitForStart();
161 boost::unique_lock<boost::mutex> lock(m_runControl);
163 m_run.signalAndWaitForShutdown();
165 m_thread = boost::thread();
167 m_orderedDestruction();
172 boost::unique_lock<boost::mutex> lock(m_runControl);
173 m_run.signalShutdown();
177 m_callControlled([&, pluginName] { m_ctx->loadPlugin(pluginName); });
188 std::string
const &driver,
189 std::string
const ¶ms) {
190 BOOST_ASSERT_MSG(m_inServerThread(),
191 "This method is only available in the server thread!");
192 m_ctx->instantiateDriver(plugin, driver, params);
196 m_callControlled([&] { m_triggeredDetect =
true; });
201 m_callControlled([&] { m_mainloopMethods.push_back(f); });
206 boost::unique_lock<boost::mutex> lock(m_runControl);
208 throw std::logic_error(
"Can't call update() if you've ever started "
209 "the server in its own thread!");
213 void ServerImpl::m_update() {
216 m_systemDevice->update();
217 for (
auto &f : m_mainloopMethods) {
220 if (m_triggeredDetect) {
221 m_log->info() <<
"Performing hardware auto-detection.";
222 common::tracing::markHardwareDetect();
223 m_ctx->triggerHardwareDetect();
224 m_triggeredDetect =
false;
227 m_log->debug() <<
"Path tree updated or connection detected";
233 bool ServerImpl::m_loop() {
238 boost::unique_lock<boost::mutex> lock(m_mainThreadMutex);
240 shouldContinue = m_run.shouldContinue();
243 if (m_currentSleepTime > 0) {
246 return shouldContinue;
251 m_callControlled([&] { wasNew = m_addRoute(routingDirective); });
256 std::string
const &source,
257 common::AliasPriority priority) {
261 [&] { wasChanged = m_addAlias(path, source, priority); });
266 common::AliasPriority priority) {
268 m_callControlled([&] { wasChanged = m_addAliases(aliases, priority); });
273 std::string
const &deviceName,
274 std::string
const &server,
275 std::string
const &descriptor) {
276 Json::Value descriptorVal;
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 "
284 m_callControlled([&] {
290 auto elt = common::elements::DeviceElement::createVRPNDeviceElement(
292 elt.getDescriptor() = descriptorVal;
302 std::string
const &value) {
303 bool wasChanged =
false;
306 m_callControlled([&] {
308 if (!(newElement == node.value())) {
311 node.value() = newElement;
317 void ServerImpl::m_orderedDestruction() {
319 m_systemComponent =
nullptr;
320 m_systemDevice.reset();
324 int ServerImpl::m_handleUpdatedRoute(
void *userdata, vrpn_HANDLERPARAM p) {
325 auto self =
static_cast<ServerImpl *
>(userdata);
327 self->m_inServerThread(),
328 "This callback should never happen outside the server thread!");
330 self->m_log->info() <<
"Got an updated route from a client.";
331 self->m_addRoute(std::string(p.buffer, p.payload_len));
335 bool ServerImpl::m_addRoute(std::string
const &routingDirective) {
337 common::addAliasFromRoute(m_tree.getRoot(), routingDirective);
338 m_treeDirty += change;
342 bool ServerImpl::m_addAlias(std::string
const &path,
343 std::string
const &source,
344 common::AliasPriority priority) {
348 m_treeDirty += change;
352 bool ServerImpl::m_addAliases(Json::Value
const &aliases,
353 common::AliasPriority priority) {
354 bool change = common::AliasProcessor()
355 .setDefaultPriority(priority)
357 .process(m_tree.getRoot(), aliases);
358 m_treeDirty += change;
361 void ServerImpl::m_queueTreeSend() {
362 m_callControlled([&] { m_treeDirty +=
true; });
364 void ServerImpl::m_sendTree() {
366 common::tracing::markPathTreeBroadcast();
367 m_systemComponent->sendReplacementTree(m_tree);
368 m_log->info() <<
"Sent path tree to clients.";
372 m_sleepTime = microseconds;
375 int ServerImpl::getSleepTime()
const {
return m_sleepTime; }
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 "
385 m_tree, dev->getName(), descriptor, m_port, m_host);
390 int ServerImpl::m_exitIdle(
void *userdata, vrpn_HANDLERPARAM) {
391 auto self =
static_cast<ServerImpl *
>(userdata);
394 if (self->m_currentSleepTime > self->m_sleepTime) {
397 "Got first client connection, exiting idle mode.");
398 self->m_currentSleepTime =
self->m_sleepTime;
401 self->m_lowLatency.reset(
new common::LowLatency);
405 int ServerImpl::m_enterIdle(
void *userdata, vrpn_HANDLERPARAM) {
406 auto self =
static_cast<ServerImpl *
>(userdata);
410 if (self->m_currentSleepTime < IDLE_SLEEP_TIME) {
412 "Dropped last client connection, entering idle mode.");
413 self->m_currentSleepTime = IDLE_SLEEP_TIME;
417 self->m_lowLatency.reset();
static shared_ptr< CommonComponent > create()
Factory method.
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.
bool addString(std::string const &path, std::string const &value)
Add a string entry to the tree.
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 ...
void registerMainloopMethod(MainloopMethod f)
Register a method to run during every time through the main loop.
bool addAlias(std::string const &path, std::string const &source, common::AliasPriority priority)
Add an alias entry to the tree.
std::function< void()> MainloopMethod
A function that can be registered by the server app to run in each mainloop iteration.
void update()
The method to just do the update stuff, not in a thread.
shared_ptr< Connection > ConnectionPtr
How one must hold a Connection.
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.
void loadAutoPlugins()
Load all auto-loadable plugins.
PathNode & getNodeByPath(std::string const &path)
Returns the node indicated by the path, which must be absolute (begin with a /). Any non-existent nod...
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 ¶ms)
Instantiate the named driver with parameters.
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.
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.
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.
bool addAlias(PathNode &node, std::string const &source, AliasPriority priority)
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.
void signalStop()
Signal the server to stop (if it is running) but return immediately.
void startAndAwaitShutdown()
Launch a thread running the server, and block until the server shuts down.
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
~ServerImpl()
Destructor (stops the thread first)
bool addAliases(Json::Value const &aliases, common::AliasPriority priority)
Add alias entries to the tree from JSON.
bool addRoute(std::string const &routingDirective)
Register a JSON string as a routing directive.
OSVR_ReturnCode microsleep(uint64_t microseconds)
Request a thread sleep for at least the given number of microseconds.
void set()
Set the flag to true.
void awaitShutdown()
Block until the server shuts down.
void setHardwareDetectOnConnection()
Adds the behavior that hardware detection should take place on client connection. ...
void triggerHardwareDetect()
Run all hardware detect callbacks.
void start()
Launch a thread running the server.
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.