YARP
Yet Another Robot Platform
MultiNameSpace.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2006-2020 Istituto Italiano di Tecnologia (IIT)
3  * All rights reserved.
4  *
5  * This software may be modified and distributed under the terms of the
6  * BSD-3-Clause license. See the accompanying LICENSE file for details.
7  */
8 
10 
11 #include <yarp/os/RosNameSpace.h>
12 #include <yarp/os/Time.h>
13 #include <yarp/os/YarpNameSpace.h>
16 
17 #include <vector>
18 
19 using namespace yarp::os;
20 using namespace yarp::os::impl;
21 
22 namespace {
23 YARP_OS_LOG_COMPONENT(MULTINAMESPACE, "yarp.os.MultiNamespace" )
24 } // namespace
25 
26 using SpaceList = std::vector<NameSpace*>;
27 
28 // private implementation of a namespace container
30 {
31 public:
32  SpaceList spaces; // list of all namespaces
33 
34  // a cache for common flags once we compute them
35  bool _localOnly;
39 
41  {
42  clear();
43  }
44 
46  {
47  clear();
48  }
49 
50  void clear()
51  {
52  // remove all namespaces and reset flags
53  for (auto ns : spaces) {
54  if (ns != nullptr) {
55  delete ns;
56  ns = nullptr;
57  }
58  }
59  spaces.clear();
60  _localOnly = true;
61  _usesCentralServer = false;
62  _serverAllocatesPortNumbers = false;
63  _connectionHasNameOfEndpoints = true;
64  }
65 
66  void scan()
67  {
68  // reset flags
69  _localOnly = true;
70  _usesCentralServer = false;
71  _serverAllocatesPortNumbers = true;
72  // now scan each namespace
73  for (auto ns : spaces) {
74  if (ns == nullptr) {
75  continue;
76  }
77  // if any namespace is nonlocal, combination is nonlocal
78  if (!ns->localOnly()) {
79  _localOnly = false;
80  }
81  // if any namespace uses a central server, combination also does
82  if (ns->usesCentralServer()) {
83  _usesCentralServer = true;
84  }
85  // if any namespace doesn't allocate port numbers, combination
86  // cannot be relied on to do so either
87  if (!ns->serverAllocatesPortNumbers()) {
88  _serverAllocatesPortNumbers = false;
89  }
90  // if any namespace lacks informed connections, combination
91  // cannot be relied on to be informed either
92  if (!ns->connectionHasNameOfEndpoints()) {
93  _connectionHasNameOfEndpoints = false;
94  }
95  }
96  }
97 
98  bool setLocalMode(bool flag)
99  {
100  // remove any existing namespaces
101  clear();
102  if (flag) {
103  // add a dummy local namespace
104  NameSpace* ns = new YarpDummyNameSpace;
105  spaces.push_back(ns);
106  }
107  // cache flags
108  scan();
109  return true;
110  }
111 
112  bool activate(bool force = false)
113  {
114  if (force) {
115  // wipe if forced
116  clear();
117  }
118  // return if namespaces already present
119  if (!spaces.empty()) {
120  return true;
121  }
122  // read namespace list from config file
123  NameConfig conf;
124  if (!conf.fromFile()) {
125  double now = SystemClock::nowSystem();
126  static double last_shown = now - 10;
127  if (now - last_shown > 3) {
128  last_shown = now;
129  yCWarning(MULTINAMESPACE, "YARP name server(s) not configured, ports will be anonymous\n");
130  yCWarning(MULTINAMESPACE, "check your namespace and settings with 'yarp detect'\n");
131  }
132  return false;
133  }
134  Bottle ns = conf.getNamespaces();
135  // loop through namespaces
136  for (size_t i = 0; i < ns.size(); i++) {
137  std::string n = ns.get(i).asString();
138  NameConfig conf2;
139  // read configuration of individual namespace
140  if (!conf2.fromFile(n.c_str())) {
141  yCWarning(MULTINAMESPACE, "Could not find namespace %s\n", n.c_str());
142  continue;
143  }
144  std::string mode = conf2.getMode();
145  Contact address = conf2.getAddress();
146  address.setName(n);
147  if (mode == "yarp" || mode == "//") {
148  // add a yarp namespace
149  NameSpace* ns = new YarpNameSpace(address);
150  spaces.push_back(ns);
151  } else if (mode == "ros") {
152  // add a ros namespace
153  NameSpace* ns = new RosNameSpace(address);
154  spaces.push_back(ns);
155  } else if (mode == "local") {
156  NameSpace* ns = new YarpDummyNameSpace;
157  spaces.push_back(ns);
158  } else {
159  // shrug
160  yCError(MULTINAMESPACE, "cannot deal with namespace of type %s", mode.c_str());
161  return false;
162  }
163  }
164  // cache flags
165  scan();
166  return true;
167  }
168 
170  {
171  activate(); // make sure we've loaded namespace(s)
172  if (!spaces.empty()) {
173  // return first name server
174  return spaces[0]->getNameServerContact();
175  }
176  return Contact();
177  }
178 
179  Contact queryName(const std::string& name)
180  {
181  activate();
182  // try query against each namespace in order
183  for (auto ns : spaces) {
184  if (ns == nullptr) {
185  continue;
186  }
187  if (ns->getNameServerName() == name) {
188  // optimization: return cached server address for
189  // port names that match name of namespace
190  return ns->getNameServerContact();
191  }
192  Contact result = ns->queryName(name);
193  // return a result once we get one, skipping any remaining
194  // namespaces
195  if (result.isValid()) {
196  return result;
197  }
198  }
199  return Contact();
200  }
201 
202  // return one namespace, any namespace (in fact always first)
204  {
205  activate();
206  if (spaces.empty()) {
207  return nullptr;
208  }
209  return spaces[0];
210  }
211 
212  // return full list of namespaces
214  {
215  activate();
216  return spaces;
217  }
218 };
219 
220 #define HELPER(x) (*((MultiNameSpaceHelper*)((x)->system_resource)))
221 
223 {
224  altStore = nullptr;
225  system_resource = new MultiNameSpaceHelper;
226  yCAssert(MULTINAMESPACE, system_resource != nullptr);
227 }
228 
230 {
231  if (system_resource != nullptr) {
232  delete &HELPER(this);
233  system_resource = nullptr;
234  }
235 }
236 
238 {
239  return HELPER(this).setLocalMode(flag);
240 }
241 
243 {
244  HELPER(this).activate();
245  return HELPER(this)._localOnly;
246 }
247 
249 {
250  HELPER(this).activate();
251  return HELPER(this)._usesCentralServer;
252 }
253 
255 {
256  HELPER(this).activate();
257  return HELPER(this)._connectionHasNameOfEndpoints;
258 }
259 
261 {
262  altStore = store;
263 }
264 
266 {
267  return altStore;
268 }
269 
271 {
272  HELPER(this).activate();
273  return HELPER(this)._serverAllocatesPortNumbers;
274 }
275 
276 bool MultiNameSpace::activate(bool force)
277 {
278  return HELPER(this).activate(force);
279 }
280 
282 {
283  return ((MultiNameSpaceHelper*)system_resource)->getNameServerContact();
284 }
285 
286 Contact MultiNameSpace::queryName(const std::string& name)
287 {
288  return HELPER(this).queryName(name);
289 }
290 
292  const Contact& dest,
293  const ContactStyle& style)
294 {
295  NameSpace* ns = HELPER(this).getOne();
296  if (ns == nullptr) {
297  return false;
298  }
299  return ns->connectPortToTopic(src, dest, style);
300 }
301 
303  const Contact& dest,
304  const ContactStyle& style)
305 {
306  NameSpace* ns = HELPER(this).getOne();
307  if (ns == nullptr) {
308  return false;
309  }
310  return ns->connectTopicToPort(src, dest, style);
311 }
312 
314  const Contact& dest,
315  const ContactStyle& style)
316 {
317  NameSpace* ns = HELPER(this).getOne();
318  if (ns == nullptr) {
319  return false;
320  }
321  return ns->disconnectPortFromTopic(src, dest, style);
322 }
323 
325  const Contact& dest,
326  const ContactStyle& style)
327 {
328  NameSpace* ns = HELPER(this).getOne();
329  if (ns == nullptr) {
330  return false;
331  }
332  return ns->disconnectTopicFromPort(src, dest, style);
333 }
334 
336  const Contact& dest,
337  const ContactStyle& style)
338 {
339  NameSpace* ns = HELPER(this).getOne();
340  if (ns == nullptr) {
341  return false;
342  }
343  return ns->connectPortToPortPersistently(src, dest, style);
344 }
345 
347  const Contact& dest,
348  const ContactStyle& style)
349 {
350  NameSpace* ns = HELPER(this).getOne();
351  if (ns == nullptr) {
352  return false;
353  }
354  return ns->disconnectPortToPortPersistently(src, dest, style);
355 }
356 
357 Contact MultiNameSpace::registerName(const std::string& name)
358 {
359  SpaceList lst = HELPER(this).getAll();
360  Contact result;
361  // loop through namespaces
362  for (size_t i = 0; i < lst.size(); i++) {
363  Contact iresult;
364  // Register name with namespace. If contact information is
365  // fleshed out while registering, we carry that along for
366  // registration with the next namespace.
367  if (result.getPort() <= 0) {
368  iresult = lst[i]->registerName(name);
369  } else {
370  iresult = lst[i]->registerContact(result);
371  }
372  if (i == 0 || result.getPort() <= 0) {
373  result = iresult;
374  }
375  }
376  return result;
377 }
378 
380 {
381  SpaceList lst = HELPER(this).getAll();
382  Contact result;
383  for (size_t i = 0; i < lst.size(); i++) {
384  // we register in *all* namespaces (and query in *any*)
385  Contact iresult = lst[i]->registerContact(contact);
386  if (i == 0) {
387  result = iresult;
388  }
389  }
390  return result;
391 }
392 
393 Contact MultiNameSpace::unregisterName(const std::string& name)
394 {
395  SpaceList lst = HELPER(this).getAll();
396  Contact result;
397  for (size_t i = 0; i < lst.size(); i++) {
398  // we unregister in *all* namespaces
399  Contact iresult = lst[i]->unregisterName(name);
400  if (i == 0) {
401  result = iresult;
402  }
403  }
404  return result;
405 }
406 
408 {
409  SpaceList lst = HELPER(this).getAll();
410  Contact result;
411  for (size_t i = 0; i < lst.size(); i++) {
412  // we unregister in *all* namespaces
413  Contact iresult = lst[i]->unregisterContact(contact);
414  if (i == 0) {
415  result = iresult;
416  }
417  }
418  return result;
419 }
420 
421 bool MultiNameSpace::setProperty(const std::string& name, const std::string& key, const Value& value)
422 {
423  NameSpace* ns = HELPER(this).getOne();
424  if (ns == nullptr) {
425  return false;
426  }
427  return ns->setProperty(name, key, value);
428 }
429 
430 Value* MultiNameSpace::getProperty(const std::string& name, const std::string& key)
431 {
432  NameSpace* ns = HELPER(this).getOne();
433  if (ns == nullptr) {
434  return nullptr;
435  }
436  return ns->getProperty(name, key);
437 }
438 
440  bool& scanNeeded,
441  bool& serverUsed)
442 {
443  // This code looks like a placeholder that never got replaced.
444  // It is using a heuristic that namespaces with "/ros" in the
445  // name are ros namespaces. There's no need for guesswork like
446  // that anymore. Also, code duplication. Should spin this
447  // off into a proper plugin mechanism for namespaces.
448  std::string name = NetworkBase::getNameServerName();
449  Contact fake;
450  Contact r;
451  if (name.find("/ros") != std::string::npos) {
452  RosNameSpace ns(fake);
453  r = ns.detectNameServer(useDetectedServer, scanNeeded, serverUsed);
454  if (r.isValid() && useDetectedServer && scanNeeded) {
455  HELPER(this).activate(true);
456  }
457  } else {
458  YarpNameSpace ns(fake);
459  r = ns.detectNameServer(useDetectedServer, scanNeeded, serverUsed);
460  if (r.isValid() && useDetectedServer && scanNeeded) {
461  HELPER(this).activate(true);
462  }
463  }
464  return r;
465 }
466 
467 
469  PortReader& reply,
470  const ContactStyle& style)
471 {
472  NameSpace* ns = HELPER(this).getOne();
473  if (ns == nullptr) {
474  return false;
475  }
476  return ns->writeToNameServer(cmd, reply, style);
477 }
std::vector< NameSpace * > SpaceList
#define HELPER(x)
bool setLocalMode(bool flag)
Contact queryName(const std::string &name)
bool activate(bool force=false)
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:73
size_type size() const
Gets the number of elements in the bottle.
Definition: Bottle.cpp:254
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
Definition: Bottle.cpp:249
Preferences for how to communicate with a contact.
Definition: ContactStyle.h:27
Represents how to reach a part of a YARP network.
Definition: Contact.h:39
bool isValid() const
Checks if a Contact is tagged as valid.
Definition: Contact.cpp:301
int getPort() const
Get the port number associated with this Contact for socket communication.
Definition: Contact.cpp:242
void setName(const std::string &name)
Set the name associated with this Contact.
Definition: Contact.cpp:225
Contact getNameServerContact() const override
Get an address for a name server that manages the name space, if available.
Contact registerName(const std::string &name) override
Record contact information to tie to a port name.
virtual bool connectPortToTopic(const Contact &src, const Contact &dest, const ContactStyle &style) override
Publish a port to a topic.
virtual NameStore * getQueryBypass()
Get any alternative place to make name queries, if one was set by queryBypass()
virtual bool connectTopicToPort(const Contact &src, const Contact &dest, const ContactStyle &style) override
Subscribe a port to a topic.
virtual bool connectPortToPortPersistently(const Contact &src, const Contact &dest, const ContactStyle &style) override
Connect two ports with persistence.
Value * getProperty(const std::string &name, const std::string &key) override
Get the value of a named key from a named port.
virtual bool disconnectTopicFromPort(const Contact &src, const Contact &dest, const ContactStyle &style) override
Stop subscribing a port to a topic.
virtual bool disconnectPortToPortPersistently(const Contact &src, const Contact &dest, const ContactStyle &style) override
Disconnect two ports, removing any persistence.
virtual void queryBypass(NameStore *store)
Set an alternative place to make name queries.
bool connectionHasNameOfEndpoints() const override
When connections are made involving ports managed by this NameSpace do the ports involved end up know...
Contact queryName(const std::string &name) override
Map from port name to contact information.
bool localOnly() const override
Check if the NameSpace is only valid for the current process ("local").
bool serverAllocatesPortNumbers() const override
Check if a central server is responsible for allocating port numbers, or if this should be left up to...
bool usesCentralServer() const override
Check if a central server is involved in managing the NameSpace.
bool activate(bool force=false)
virtual bool disconnectPortFromTopic(const Contact &src, const Contact &dest, const ContactStyle &style) override
Stop publishing a port to a topic.
Contact unregisterName(const std::string &name) override
Disassociate contact information from a port name.
Contact registerContact(const Contact &contact) override
Record contact information (should include a port name).
Contact unregisterContact(const Contact &contact) override
Disassociate contact information (should include a port name).
virtual bool setProperty(const std::string &name, const std::string &key, const Value &value) override
Associate a key/value pair with a named port.
bool setLocalMode(bool flag)
virtual Contact detectNameServer(bool useDetectedServer, bool &scanNeeded, bool &serverUsed) override
Find a name server for this NameSpace, if applicable.
virtual bool writeToNameServer(PortWriter &cmd, PortReader &reply, const ContactStyle &style) override
Write a message to a name server for this NameSpace, if applicable.
An abstract name space for ports.
Definition: NameSpace.h:26
virtual bool disconnectTopicFromPort(const Contact &src, const Contact &dest, const ContactStyle &style)=0
Stop subscribing a port to a topic.
virtual bool disconnectPortFromTopic(const Contact &src, const Contact &dest, const ContactStyle &style)=0
Stop publishing a port to a topic.
virtual bool connectPortToPortPersistently(const Contact &src, const Contact &dest, const ContactStyle &style)=0
Connect two ports with persistence.
virtual bool connectTopicToPort(const Contact &src, const Contact &dest, const ContactStyle &style)=0
Subscribe a port to a topic.
virtual bool writeToNameServer(PortWriter &cmd, PortReader &reply, const ContactStyle &style)=0
Write a message to a name server for this NameSpace, if applicable.
virtual bool connectPortToTopic(const Contact &src, const Contact &dest, const ContactStyle &style)=0
Publish a port to a topic.
virtual bool setProperty(const std::string &name, const std::string &key, const Value &value)=0
Associate a key/value pair with a named port.
virtual Value * getProperty(const std::string &name, const std::string &key)=0
Get the value of a named key from a named port.
virtual bool disconnectPortToPortPersistently(const Contact &src, const Contact &dest, const ContactStyle &style)=0
Disconnect two ports, removing any persistence.
Abstract interface for a database of port names.
Definition: NameStore.h:23
static std::string getNameServerName()
Get the name of the port associated with the nameserver (usually "/root", but this can be overwritten...
Definition: Network.cpp:1355
Interface implemented by all objects that can read themselves from the network, such as Bottle object...
Definition: PortReader.h:28
Interface implemented by all objects that can write themselves to the network, such as Bottle objects...
Definition: PortWriter.h:27
virtual Contact detectNameServer(bool useDetectedServer, bool &scanNeeded, bool &serverUsed) override
Find a name server for this NameSpace, if applicable.
static double nowSystem()
Definition: SystemClock.cpp:37
A single value (typically within a Bottle).
Definition: Value.h:47
virtual std::string asString() const
Get string value.
Definition: Value.cpp:237
virtual Contact detectNameServer(bool useDetectedServer, bool &scanNeeded, bool &serverUsed) override
Find a name server for this NameSpace, if applicable.
Small helper class to help deal with legacy YARP configuration files.
Definition: NameConfig.h:28
bool fromFile(const char *ns=nullptr)
Definition: NameConfig.cpp:157
yarp::os::Bottle getNamespaces(bool refresh=false)
Definition: NameConfig.cpp:461
#define yCError(component,...)
Definition: LogComponent.h:157
#define yCAssert(component, x)
Definition: LogComponent.h:172
#define yCWarning(component,...)
Definition: LogComponent.h:146
#define YARP_OS_LOG_COMPONENT(name, name_string)
Definition: LogComponent.h:37
double now()
Return the current time in seconds, relative to an arbitrary starting point.
Definition: Time.cpp:124
The components from which ports and connections are built.
An interface to the operating system, including Port based communication.