YARP
Yet Another Robot Platform
xmlapploader.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 #include <yarp/manager/utility.h>
8 #include <yarp/conf/filesystem.h>
9 #include <dirent.h>
10 #include <tinyxml.h>
11 #include <yarp/os/Value.h>
12 #ifdef WITH_GEOMETRY
13 #include <yarp/os/Property.h> // for parsing geometry information
14 #endif
15 
16 #include <algorithm>
17 #include <cctype>
18 #include <string>
19 #include <fstream>
21 
22 
23 
24 using namespace yarp::manager;
25 
26 
32 XmlAppLoader::XmlAppLoader(const char* szPath, const char* szAppName)
33 {
34  parser = new(TextParser);
35  app.clear();
36  if (szAppName) {
37  strAppName = szAppName;
38  }
39 
40  if(strlen(szPath))
41  {
42  const std::string directorySeparator{yarp::conf::filesystem::preferred_separator};
43  strPath = szPath;
44  if ((strPath.rfind(directorySeparator) == std::string::npos) || (strPath.rfind(directorySeparator) != strPath.size() - 1)) {
45  strPath = strPath + std::string(directorySeparator);
46  }
47  }
48 }
49 
53 XmlAppLoader::XmlAppLoader(const char* szFileName)
54 {
55  parser = new(TextParser);
56  app.clear();
57  if (szFileName) {
58  strFileName = szFileName;
59  }
60 }
61 
62 
64 {
65  if(parser)
66  {
67  delete parser;
68  }
69 }
70 
71 
73 {
74  app.clear();
75  fileNames.clear();
77 
81  if(!strFileName.empty())
82  {
83  fileNames.push_back(strFileName);
84  return true;
85  }
86 
87  if(strPath.empty())
88  {
89  logger->addError("No application path is introduced.");
90  return false;
91  }
92 
93  DIR *dir;
94  struct dirent *entry;
95  if ((dir = opendir(strPath.c_str())) == nullptr)
96  {
97  OSTRINGSTREAM err;
98  err<<"Cannot access "<<strPath;
99  logger->addError(err);
100  return false;
101  }
102 
103  /* we need to load all xml apps */
104  while((entry = readdir(dir)))
105  {
106  std::string name = entry->d_name;
107  if(name.size() > 3)
108  {
109  std::string ext = name.substr(name.size()-3,3);
110  if (compareString(ext.c_str(), "xml")) {
111  fileNames.push_back(strPath + name);
112  }
113  }
114  }
115  closedir(dir);
116 /*
117  if(fileNames.empty())
118  {
119  OSTRINGSTREAM err;
120  err<<"No xml application file found in "<<strPath;
121  logger->addWarning(err);
122  //return false;
123  }
124 */
125  return true;
126 }
127 
129 {
130  fini();
131  init();
132 }
133 
134 
136 {
137  fileNames.clear();
138  app.clear();
139 }
140 
141 
143 {
144  if(strAppName.empty())
145  {
146  Application* app = nullptr;
147  while(!app)
148  {
149  if (fileNames.empty()) {
150  return nullptr;
151  }
152  std::string fname = fileNames.back();
153  fileNames.pop_back();
154  app = parsXml(fname.c_str());
155  }
156  return app;
157  }
158  else
159  {
160  std::vector<std::string>::iterator itr;
161  for(itr=fileNames.begin(); itr<fileNames.end(); itr++)
162  {
163  Application* app = parsXml((*itr).c_str());
164  if (app && (std::string(app->getName()) == strAppName)) {
165  return app;
166  }
167  }
168  }
169  return nullptr;
170 }
171 
172 Application* XmlAppLoader::parsXml(const char* szFile)
173 {
174  app.clear();
175 
177 
178  TiXmlDocument doc(szFile);
179  if(!doc.LoadFile())
180  {
181  OSTRINGSTREAM err;
182  err<<"Syntax error while loading "<<szFile<<" at line "\
183  <<doc.ErrorRow()<<": ";
184  err<<doc.ErrorDesc();
185  logger->addError(err);
186  return nullptr;
187  }
188 
189  /* retrieving root element */
190  TiXmlElement *root = doc.RootElement();
191  if(!root)
192  {
193  OSTRINGSTREAM err;
194  err<<"Syntax error while loading "<<szFile<<" . ";
195  err<<"No root element.";
196  logger->addError(err);
197  return nullptr;
198  }
199 
200  if(!compareString(root->Value(), "application"))
201  {
202  //OSTRINGSTREAM err;
203  //err<<"File "<<szFile<<" has no tag <application>.";
204  //logger->addError(err);
205  return nullptr;
206  }
207 
208  /* retrieving name */
209  auto* name = (TiXmlElement*) root->FirstChild("name");
210  if(!name || !name->GetText())
211  {
212  OSTRINGSTREAM err;
213  err<<"Module from "<<szFile<<" has no name.";
214  logger->addError(err);
215  //return NULL;
216  }
217 
218  for(TiXmlElement* var = root->FirstChildElement("var"); var; var = var->NextSiblingElement())
219  {
220  if(var->Attribute("name") && var->GetText())
221  {
222  parser->addVariable(var->Attribute("name"), var->GetText());
223  }
224  }
225 
226  app.setXmlFile(szFile);
227 
228  if(name)
229  {
230  std::string strname = parser->parseText(name->GetText());
231  for (char& i : strname) {
232  if (i == ' ') {
233  i = '_';
234  }
235  }
236  app.setName(strname.c_str());
237  }
238 
239  /* retrieving description */
240  TiXmlElement* desc;
241  if ((desc = (TiXmlElement*)root->FirstChild("description"))) {
242  app.setDescription(parser->parseText(desc->GetText()).c_str());
243  }
244 
245  /* retrieving version */
246  TiXmlElement* ver;
247  if ((ver = (TiXmlElement*)root->FirstChild("version"))) {
248  app.setVersion(parser->parseText(ver->GetText()).c_str());
249  }
250 
251  /*
252  * TODO: setting prefix of the main application is inactivated.
253  * Check this should be supported in future or not!
254  */
255  /*
256  //retrieving application prefix
257  TiXmlElement* pref;
258  if((pref = (TiXmlElement*) root->FirstChild("prefix")))
259  app.setPrefix(pref->GetText());
260  */
261 
262  /* retrieving authors information*/
263  TiXmlElement* authors;
264  if ((authors = (TiXmlElement*)root->FirstChild("authors"))) {
265  for(TiXmlElement* ath = authors->FirstChildElement(); ath;
266  ath = ath->NextSiblingElement())
267  {
268  if(compareString(ath->Value(), "author"))
269  {
270  Author author;
271  if (ath->GetText()) {
272  author.setName(parser->parseText(ath->GetText()).c_str());
273  }
274  if (ath->Attribute("email")) {
275  author.setEmail(ath->Attribute("email"));
276  }
277  app.addAuthor(author);
278  }
279  else
280  {
281  OSTRINGSTREAM war;
282  war<<"Unrecognized tag from "<<szFile<<" at line "\
283  <<ath->Row()<<".";
284  logger->addWarning(war);
285  }
286  }
287  }
288 
289  /* retrieving resources information*/
290  TiXmlElement* resources;
291  if ((resources = (TiXmlElement*)root->FirstChild("dependencies"))) {
292  for(TiXmlElement* res = resources->FirstChildElement(); res;
293  res = res->NextSiblingElement())
294  {
295  if(compareString(res->Value(), "port"))
296  {
297  if(res->GetText())
298  {
299  ResYarpPort resource(parser->parseText(res->GetText()).c_str());
300  resource.setPort(parser->parseText(res->GetText()).c_str());
301  app.addResource(resource);
302  }
303  }
304  else
305  {
306  OSTRINGSTREAM war;
307  war<<"Unrecognized tag from "<<szFile<<" at line "\
308  <<res->Row()<<".";
309  logger->addWarning(war);
310  }
311  }
312  }
313 
314  /* retrieving modules information*/
315  using setter = void (ModuleInterface::*)(const char*);
316 
317  std::vector<std::pair<const char*, setter> > modList;
318  std::pair<const char*, setter> pairNode;
319 
320  pairNode.first = "node"; pairNode.second = &ModuleInterface::setHost; modList.push_back(pairNode);
321  pairNode.first = "parameters"; pairNode.second = &ModuleInterface::setParam; modList.push_back(pairNode);
322  pairNode.first = "stdio"; pairNode.second = &ModuleInterface::setStdio; modList.push_back(pairNode);
323  pairNode.first = "workdir"; pairNode.second = &ModuleInterface::setWorkDir; modList.push_back(pairNode);
324  pairNode.first = "deployer"; pairNode.second = &ModuleInterface::setBroker; modList.push_back(pairNode);
325  pairNode.first = "prefix"; pairNode.second = &ModuleInterface::setPrefix; modList.push_back(pairNode);
326  pairNode.first = "environment"; pairNode.second = &ModuleInterface::setEnvironment; modList.push_back(pairNode);
327  pairNode.first = "display"; pairNode.second = &ModuleInterface::setDisplay; modList.push_back(pairNode);
328  for(TiXmlElement* mod = root->FirstChildElement(); mod; mod = mod->NextSiblingElement())
329  {
330  if(compareString(mod->Value(), "module"))
331  {
332  TiXmlElement* element;
333  if((element = (TiXmlElement*) mod->FirstChild("name")))
334  {
335  std::string elemText;
336  const char* text;
337 
338 
339  text = nullptr;
340  if(element->GetText())
341  {
342  elemText = parser->parseText(element->GetText());
343  text = elemText.c_str();
344  }
345 
346  ModuleInterface module(text);
347 
348  for(auto& i : modList)
349  {
350  if((element = (TiXmlElement*) mod->FirstChild(i.first)))
351  {
352  text = nullptr;
353  if(element->GetText())
354  {
355  elemText = parser->parseText(element->GetText());
356  text = elemText.c_str();
357  }
358 
359  (module.*(i.second))(text);
360  }
361  }
362 
363  if((element = (TiXmlElement*) mod->FirstChild("rank")))
364  {
365  if(element->GetText())
366  {
367  elemText = parser->parseText(element->GetText());
368  text = elemText.c_str();
369  }
370 
371  module.setRank(atoi(text));
372  }
373 
374 #ifdef WITH_GEOMETRY
375  element = (TiXmlElement*) mod->FirstChild("geometry");
376  if(element && element->GetText())
377  {
378  yarp::os::Property prop(parser->parseText(element->GetText()).c_str());
379  GraphicModel model;
380  GyPoint pt;
381  if(prop.check("Pos"))
382  {
383  pt.x = prop.findGroup("Pos").find("x").asFloat64();
384  pt.y = prop.findGroup("Pos").find("y").asFloat64();
385  model.points.push_back(pt);
386  module.setModelBase(model);
387  }
388  }
389 #endif
390  /* retrieving resources information*/
391  TiXmlElement* resources;
392  if((resources = (TiXmlElement*) mod->FirstChild("dependencies")))
393  {
394  for(TiXmlElement* res = resources->FirstChildElement(); res;
395  res = res->NextSiblingElement())
396  {
397  if(compareString(res->Value(), "port"))
398  {
399  if(res->GetText())
400  {
401  ResYarpPort resource(parser->parseText(res->GetText()).c_str());
402  resource.setPort(parser->parseText(res->GetText()).c_str());
403  if (res->Attribute("timeout")) {
404  resource.setTimeout(atof(res->Attribute("timeout")));
405  }
406  if (res->Attribute("request")) {
407  resource.setRequest(res->Attribute("request"));
408  }
409  if (res->Attribute("reply")) {
410  resource.setReply(res->Attribute("reply"));
411  }
412  module.addResource(resource);
413  }
414  }
415  else
416  {
417  OSTRINGSTREAM war;
418  war<<"Unrecognized tag from "<<szFile<<" at line "\
419  <<res->Row()<<".";
420  logger->addWarning(war);
421  }
422  }
423  }
424 
425  /* retrieving resources information*/
426  TiXmlElement* ensure;
427  if((ensure = (TiXmlElement*) mod->FirstChild("ensure")))
428  {
429  for(TiXmlElement* res = ensure->FirstChildElement(); res;
430  res = res->NextSiblingElement())
431  {
432  if(compareString(res->Value(), "wait"))
433  {
434  if (res->Attribute("when") && compareString(res->Attribute("when"), "start")) {
435  if (parser->parseText(res->GetText()).c_str()) {
436  module.setPostExecWait(atof(parser->parseText(res->GetText()).c_str()));
437  }
438  }
439  else if (res->Attribute("when") && compareString(res->Attribute("when"), "stop")) {
440  if (parser->parseText(res->GetText()).c_str()) {
441  module.setPostStopWait(atof(parser->parseText(res->GetText()).c_str()));
442  }
443  }
444  else if (res->Attribute("when") && strlen(res->Attribute("when"))) {
445  OSTRINGSTREAM war;
446  war << "Unrecognized value for 'when' property from " << szFile << " at line "<< res->Row() << ".";
447  logger->addWarning(war);
448  }
449  else { // if "when" has not been specified, use setPostExecWait!
450  if (parser->parseText(res->GetText()).c_str()) {
451  module.setPostExecWait(atof(parser->parseText(res->GetText()).c_str()));
452  }
453  }
454  }
455  else
456  {
457  OSTRINGSTREAM war;
458  war<<"Unrecognized tag from "<<szFile<<" at line "\
459  <<res->Row()<<".";
460  logger->addWarning(war);
461  }
462  }
463  }
464  /* retrieving portmaps */
465  for (TiXmlElement* map = mod->FirstChildElement(); map;
466  map = map->NextSiblingElement()) {
467  if(compareString(map->Value(), "portmap"))
468  {
469  TiXmlElement* first;
470  TiXmlElement* second;
471  if((first=(TiXmlElement*) map->FirstChild("old")) &&
472  (second=(TiXmlElement*) map->FirstChild("new")) )
473  {
474  Portmap portmap(parser->parseText(first->GetText()).c_str(), parser->parseText(second->GetText()).c_str());
475  module.addPortmap(portmap);
476  }
477  }
478  }
479 
480  app.addImodule(module);
481  }
482  else
483  {
484  OSTRINGSTREAM war;
485  war<<"Module from "<<szFile<<" at line "\
486  <<mod->Row()<<" has not name tag.";
487  logger->addWarning(war);
488  }
489 
490  }
491  }
492 
493  /* retrieving embedded application information*/
494  for(TiXmlElement* embApp = root->FirstChildElement(); embApp;
495  embApp = embApp->NextSiblingElement())
496  {
497  if(compareString(embApp->Value(), "application"))
498  {
499  TiXmlElement* name;
500  TiXmlElement* prefix;
501  if((name=(TiXmlElement*) embApp->FirstChild("name")))
502  {
503  ApplicationInterface IApp(parser->parseText(name->GetText()).c_str());
504  if ((prefix = (TiXmlElement*)embApp->FirstChild("prefix"))) {
505  IApp.setPrefix(parser->parseText(prefix->GetText()).c_str());
506  }
507 #ifdef WITH_GEOMETRY
508  auto* element = (TiXmlElement*) embApp->FirstChild("geometry");
509  if(element && element->GetText())
510  {
511  yarp::os::Property prop(parser->parseText(element->GetText()).c_str());
512  GraphicModel model;
513  GyPoint pt;
514  if(prop.check("Pos"))
515  {
516  pt.x = prop.findGroup("Pos").find("x").asFloat64();
517  pt.y = prop.findGroup("Pos").find("y").asFloat64();
518  model.points.push_back(pt);
519  IApp.setModelBase(model);
520  }
521  }
522 #endif
523  app.addIapplication(IApp);
524  }
525  else
526  {
527  OSTRINGSTREAM war;
528  war<<"Incomplete application tag from "<<szFile<<" at line "\
529  <<embApp->Row()<<". (no name)";
530  logger->addWarning(war);
531  }
532  }
533  }
534 
535 
536  /* retrieving arbitrator information*/
537  for(TiXmlElement* arb = root->FirstChildElement(); arb;
538  arb = arb->NextSiblingElement())
539  {
540  if(compareString(arb->Value(), "arbitrator"))
541  {
542  auto* port = (TiXmlElement*) arb->FirstChild("port");
543  if(port && port->GetText())
544  {
545  Arbitrator arbitrator(parser->parseText(port->GetText()).c_str());
546 
547  // retrieving rules
548  for(TiXmlElement* rule = arb->FirstChildElement(); rule;
549  rule = rule->NextSiblingElement())
550  {
551  if(compareString(rule->Value(), "rule"))
552  {
553  if (rule->Attribute("connection")) {
554  arbitrator.addRule(rule->Attribute("connection"), parser->parseText(rule->GetText()).c_str());
555  }
556  }
557  }
558 #ifdef WITH_GEOMETRY
559  auto* geometry = (TiXmlElement*) arb->FirstChild("geometry");
560  if(geometry && geometry->GetText())
561  {
562  yarp::os::Property prop(parser->parseText(geometry->GetText()).c_str());
563  GraphicModel model;
564  if(prop.check("Pos"))
565  {
566  yarp::os::Bottle pos = prop.findGroup("Pos");
567  for(size_t i=1; i<pos.size(); i++)
568  {
569  GyPoint pt;
570  pt.x = pos.get(i).find("x").asFloat64();
571  pt.y = pos.get(i).find("y").asFloat64();
572  model.points.push_back(pt);
573  }
574  arbitrator.setModelBase(model);
575  }
576  }
577 #endif
578  app.addArbitrator(arbitrator);
579  }
580  else
581  {
582  OSTRINGSTREAM war;
583  war<<"Incomplete arbitrator tag from "<<szFile<<" at line "\
584  <<arb->Row()<<".";
585  logger->addWarning(war);
586  }
587  }
588  }
589 
590  /* retrieving connections information*/
591  for(TiXmlElement* cnn = root->FirstChildElement(); cnn;
592  cnn = cnn->NextSiblingElement())
593  {
594  if(compareString(cnn->Value(), "connection"))
595  {
596  auto* from = (TiXmlElement*) cnn->FirstChild("from");
597  auto* to = (TiXmlElement*) cnn->FirstChild("to");
598 
599  if (!from) {
600  from = (TiXmlElement*)cnn->FirstChild("output");
601  }
602  if (!to) {
603  to = (TiXmlElement*)cnn->FirstChild("input");
604  }
605 
606  TiXmlElement* protocol;
607  if(from && to)
608  {
609  std::string strCarrier;
610  if ((protocol = (TiXmlElement*)cnn->FirstChild("protocol")) && protocol->GetText()) {
611  strCarrier = parser->parseText(protocol->GetText());
612  }
613  Connection connection(parser->parseText(from->GetText()).c_str(),
614  parser->parseText(to->GetText()).c_str(),
615  strCarrier.c_str());
616 
617  // check if Qos is set for the connection
618  if(cnn->Attribute("qos")) {
619  connection.setQosTo(cnn->Attribute("qos"));
620  connection.setQosFrom(cnn->Attribute("qos"));
621  }
622 
623  if(from->Attribute("external") &&
624  compareString(from->Attribute("external"), "true"))
625  {
626  connection.setFromExternal(true);
627  if(from->GetText())
628  {
629  ResYarpPort resource(parser->parseText(from->GetText()).c_str());
630  resource.setPort(parser->parseText(from->GetText()).c_str());
631  app.addResource(resource);
632  }
633  }
634  if (from->Attribute("qos")) {
635  connection.setQosFrom(from->Attribute("qos"));
636  }
637  if(to->Attribute("external") &&
638  compareString(to->Attribute("external"), "true"))
639  {
640  if(to->GetText())
641  {
642  connection.setToExternal(true);
643  ResYarpPort resource(parser->parseText(to->GetText()).c_str());
644  resource.setPort(parser->parseText(to->GetText()).c_str());
645  app.addResource(resource);
646  }
647  }
648  if (to->Attribute("qos")) {
649  connection.setQosTo(to->Attribute("qos"));
650  }
651 
652  //Connections which have the same port name in Port Resources
653  // should also be set as external
654  for(int i=0; i<app.resourcesCount(); i++)
655  {
656  ResYarpPort res = app.getResourceAt(i);
657  if (compareString(res.getPort(), connection.from())) {
658  connection.setFromExternal(true);
659  }
660  if (compareString(res.getPort(), connection.to())) {
661  connection.setToExternal(true);
662  }
663  }
664 
665  if (cnn->Attribute("id")) {
666  connection.setId(cnn->Attribute("id"));
667  }
668 
669  if (cnn->Attribute("persist") && compareString(cnn->Attribute("persist"), "true")) {
670  connection.setPersistent(true);
671  }
672 
673 #ifdef WITH_GEOMETRY
674  auto* geometry = (TiXmlElement*) cnn->FirstChild("geometry");
675  if(geometry && geometry->GetText())
676  {
677  yarp::os::Property prop(parser->parseText(geometry->GetText()).c_str());
678  GraphicModel model;
679  if(prop.check("Pos"))
680  {
681  yarp::os::Bottle pos = prop.findGroup("Pos");
682  for(size_t i=1; i<pos.size(); i++)
683  {
684  GyPoint pt;
685  pt.x = pos.get(i).find("x").asFloat64();
686  pt.y = pos.get(i).find("y").asFloat64();
687  model.points.push_back(pt);
688  }
689  connection.setModelBase(model);
690  }
691  }
692 #endif
693 
694  app.addConnection(connection);
695  }
696  else
697  {
698  OSTRINGSTREAM war;
699  war<<"Incomplete connection tag from "<<szFile<<" at line "\
700  <<cnn->Row()<<".";
701  logger->addWarning(war);
702  }
703  }
704  }
705 
706 
707  return &app;
708 
709 }
static RFModule * module
Definition: RFModule.cpp:231
Class ApplicationInterface.
Definition: application.h:253
Class Application.
Definition: application.h:289
ResYarpPort & getResourceAt(int index)
Definition: application.h:327
void setName(const char *szName)
Definition: application.h:296
Arbitrator & addArbitrator(Arbitrator &arb)
bool addIapplication(ApplicationInterface &iapp)
void addAuthor(Author &author)
Definition: application.h:309
void setXmlFile(const char *szFilename)
Definition: application.h:337
void setVersion(const char *szVersion)
Definition: application.h:301
void setDescription(const char *szDesc)
Definition: application.h:302
Connection & addConnection(Connection &cnn)
bool addResource(ResYarpPort &res)
bool addImodule(ModuleInterface &imod)
Class port Arbitrator.
Definition: arbitrator.h:25
void setName(const char *name)
Definition: module.h:30
void setEmail(const char *email)
Definition: module.h:31
Class Connection.
Definition: application.h:57
Singleton class ErrorLogger.
Definition: utility.h:57
void addError(const char *szError)
Definition: utility.cpp:118
void addWarning(const char *szWarning)
Definition: utility.cpp:104
static ErrorLogger * Instance()
Singleton class ErrorLogger.
Definition: utility.cpp:98
std::vector< GyPoint > points
Definition: node.h:30
Class ModuleInterface.
Definition: application.h:161
void setDisplay(const char *szDisplay)
Definition: application.h:183
void setHost(const char *szHost)
Definition: application.h:174
void setWorkDir(const char *szWDir)
Definition: application.h:177
void setParam(const char *szParam)
Definition: application.h:175
void setStdio(const char *szStdio)
Definition: application.h:178
void setPrefix(const char *szPrefix)
Definition: application.h:180
void setEnvironment(const char *szEnv)
Definition: application.h:181
void setBroker(const char *szBroker)
Definition: application.h:179
Class Portmap.
Definition: application.h:27
bool addVariable(const std::string &key, const std::string &value)
Definition: textparser.h:30
std::string parseText(const char *element)
Definition: textparser.h:44
Application * getNextApplication() override
XmlAppLoader(const char *szFileName)
load only one application indicated by its xml file name
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:74
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
Bottle & findGroup(const std::string &key) const override
Gets a list corresponding to a given keyword.
Definition: Bottle.cpp:302
A class for storing options and configuration information.
Definition: Property.h:34
virtual yarp::conf::float64_t asFloat64() const
Get 64-bit floating point value.
Definition: Value.cpp:222
Value & find(const std::string &key) const override
Gets a value corresponding to a given keyword.
Definition: Value.cpp:327
static constexpr value_type preferred_separator
Definition: filesystem.h:23
bool compareString(const char *szFirst, const char *szSecond)
Definition: utility.cpp:310
std::stringstream OSTRINGSTREAM
Definition: utility.h:49