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