OSVR Framework (Internal Development Docs)  0.6-1962-g59773924
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
PureClientContext.cpp
Go to the documentation of this file.
1 
11 // Copyright 2015 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 "PureClientContext.h"
33 #include <osvr/Util/Verbosity.h>
35 
36 #include <boost/algorithm/string.hpp>
37 
38 // Library/third-party includes
39 #include <json/value.h>
40 
41 // Standard includes
42 #include <unordered_set>
43 #include <thread>
44 
45 namespace osvr {
46 namespace client {
47  inline void replaceLocalhostServers(Json::Value &nodes,
48  std::string const &host) {
49  BOOST_ASSERT_MSG(host.length() > 0,
50  "Cannot replace localhost with an empty host name!");
51  const auto deviceElementTypeName =
52  common::elements::getTypeName<common::elements::DeviceElement>();
53  static const auto LOCALHOST = "localhost";
54  for (auto &node : nodes) {
55  if (node["type"].asString() == deviceElementTypeName) {
56  auto &serverRef = node["server"];
57  auto server = serverRef.asString();
58 
59  auto it = server.find(LOCALHOST);
60 
61  if (it != server.npos) {
62  // Do a bit of surgery, only the "localhost" must be
63  // replaced, keeping the ":xxxx" part with the port number
64  // (or even the potential "tcp://" prefix) - the host could
65  // be running a local VRPN/OSVR service on another port!
66 
67  // We have to do it like this, because
68  // std::string::replace() has a silly undefined corner case
69  // when the string we are replacing localhost with is
70  // shorter than the length of string being replaced (see
71  // http://www.cplusplus.com/reference/string/string/replace/
72  // )
73  // Better be safe than sorry :(
74 
75  serverRef = boost::algorithm::ireplace_first_copy(
76  server, LOCALHOST,
77  host); // Go through a copy, just to be extra safe
78  }
79  }
80  }
81  }
82 
83  static const std::chrono::milliseconds STARTUP_CONNECT_TIMEOUT(200);
84  static const std::chrono::milliseconds STARTUP_TREE_TIMEOUT(1000);
85  static const std::chrono::milliseconds STARTUP_LOOP_SLEEP(1);
86 
87  PureClientContext::PureClientContext(const char appId[], const char host[],
88  common::ClientContextDeleter del)
89  : ::OSVR_ClientContextObject(appId, del), m_host(host),
90  m_ifaceMgr(m_pathTreeOwner, m_factory,
91  *static_cast<common::ClientContext *>(this)) {
92 
93  if (!m_network.isUp()) {
94  throw std::runtime_error("Network error: " + m_network.getError());
95  }
96 
98  populateRemoteHandlerFactory(m_factory, m_vrpnConns);
99 
100  std::string sysDeviceName =
101  std::string(common::SystemComponent::deviceName()) + "@" + host;
102  m_mainConn = m_vrpnConns.getConnection(
104 
106  m_systemDevice = common::createClientDevice(sysDeviceName, m_mainConn);
107  m_systemComponent =
108  m_systemDevice->addComponent(common::SystemComponent::create());
109  using DedupJsonFunction =
111 
112  m_systemComponent->registerReplaceTreeHandler(
113  DedupJsonFunction([&](Json::Value nodes) {
114  logger()->debug("Got updated path tree, processing");
115  // Replace localhost before we even convert the json to a tree.
116  // replace the @localhost with the correct host name
117  // in case we are a remote client, otherwise the connection
118  // would fail
119  replaceLocalhostServers(nodes, m_host);
120 
121  // Tree observers will handle destruction/creation of remote
122  // handlers.
123  m_pathTreeOwner.replaceTree(nodes);
124  }));
125 
126  typedef std::chrono::system_clock clock;
127  auto begin = clock::now();
128 
129  // Spin the update to get a connection
130  auto connEnd = begin + STARTUP_CONNECT_TIMEOUT;
131  while (clock::now() < connEnd && !m_gotConnection) {
132  m_update();
133  std::this_thread::sleep_for(STARTUP_LOOP_SLEEP);
134  }
135  if (!m_gotConnection) {
136  logger()->notice()
137  << "Could not connect to OSVR server in the timeout period "
138  "allotted of "
139  << std::chrono::duration_cast<std::chrono::milliseconds>(
140  STARTUP_CONNECT_TIMEOUT)
141  .count()
142  << "ms";
143  return; // Bail early if we don't even have a connection
144  }
145 
146  // Spin the update to get a path tree
147  auto treeEnd = begin + STARTUP_TREE_TIMEOUT;
148  while (clock::now() < treeEnd && !m_pathTreeOwner) {
149  m_update();
150  std::this_thread::sleep_for(STARTUP_LOOP_SLEEP);
151  }
152  auto timeToStartup = (clock::now() - begin);
153 
154  // this message is just "info" if we're all good, but "notice" if we
155  // aren't fully set up yet.
156  logger()->log(m_pathTreeOwner ? util::log::LogLevel::info
157  : util::log::LogLevel::notice)
158  << "Connection process took "
159  << std::chrono::duration_cast<std::chrono::milliseconds>(
160  timeToStartup)
161  .count()
162  << "ms: " << (m_gotConnection ? "have connection to server, "
163  : "don't have connection to server, ")
164  << (m_pathTreeOwner ? "have path tree" : "don't have path tree");
165  }
166 
167  PureClientContext::~PureClientContext() {}
168 
169  void PureClientContext::m_update() {
171  m_vrpnConns.updateAll();
172 
173  if (!m_gotConnection && m_mainConn->connected()) {
174  logger()->info("Got connection to main OSVR server");
175  m_gotConnection = true;
176  }
177 
179  m_systemDevice->update();
181  m_ifaceMgr.updateHandlers();
182  }
183 
184  void PureClientContext::m_sendRoute(std::string const &route) {
185  m_systemComponent->sendClientRouteUpdate(route);
186  m_update();
187  }
188 
189  void PureClientContext::m_handleNewInterface(
190  common::ClientInterfacePtr const &iface) {
191  m_ifaceMgr.addInterface(iface);
192  }
193 
194  void PureClientContext::m_handleReleasingInterface(
195  common::ClientInterfacePtr const &iface) {
196  m_ifaceMgr.releaseInterface(iface);
197  }
198 
199  bool PureClientContext::m_getStatus() const {
200  return m_gotConnection && m_pathTreeOwner;
201  }
202 
203  common::PathTree const &PureClientContext::m_getPathTree() const {
204  return m_pathTreeOwner.get();
205  }
206 
207  common::Transform const &
208  PureClientContext::m_getRoomToWorldTransform() const {
209  return m_roomToWorld;
210  }
211 
212  void PureClientContext::m_setRoomToWorldTransform(
213  common::Transform const &xform) {
214  m_roomToWorld = xform;
215  }
216 } // namespace client
217 } // namespace osvr
void replaceTree(Json::Value const &nodes)
Replace the entirety of the path tree from the given serialized array of nodes.
shared_ptr< ClientInterface > ClientInterfacePtr
Pointer for holding ClientInterface objects safely.
BaseDevicePtr createClientDevice(std::string const &name, vrpn_ConnectionPtr const &conn)
Factory function for a bare client device with no components/interfaces registered by default...
Header including PathTree.h and all additional headers needed to define related types.
std::string const & getError() const
Get error message, if any.
static shared_ptr< SystemComponent > create()
Factory method.
void addInterface(common::ClientInterfacePtr const &iface, bool verboseFailure=true)
Internal, configured header file for verbosity macros.
static const char * deviceName()
Get the special device name to be used with this component.
osvr::util::log::LoggerPtr const & logger() const
Provides logger access for related internal classes.
void updateHandlers()
run update on all remote handlers
A wrapper for a unary function that only calls its contained function when the input has changed...
bool isUp() const
Get whether the networking system is successfully "up".
PathTree & get()
Access the path tree object itself.
Definition: PathTreeOwner.h:73