YARP
Yet Another Robot Platform
 
Loading...
Searching...
No Matches
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
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
24using namespace yarp::manager;
25
26
32XmlAppLoader::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
53XmlAppLoader::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
172Application* 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}
Class ApplicationInterface.
Class Application.
void setName(const char *szName)
Arbitrator & addArbitrator(Arbitrator &arb)
bool addIapplication(ApplicationInterface &iapp)
void addAuthor(Author &author)
void setXmlFile(const char *szFilename)
void setVersion(const char *szVersion)
void setDescription(const char *szDesc)
Connection & addConnection(Connection &cnn)
bool addResource(ResYarpPort &res)
ResYarpPort & getResourceAt(int index)
bool addImodule(ModuleInterface &imod)
Class port Arbitrator.
Definition arbitrator.h:24
void setName(const char *name)
Definition module.h:29
void setEmail(const char *email)
Definition module.h:30
Class Connection.
Definition application.h:56
Singleton class ErrorLogger.
Definition utility.h:58
void addError(const char *szError)
Definition utility.cpp:126
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:29
Class ModuleInterface.
void setDisplay(const char *szDisplay)
void setHost(const char *szHost)
void setWorkDir(const char *szWDir)
void setParam(const char *szParam)
void setStdio(const char *szStdio)
void setPrefix(const char *szPrefix)
void setEnvironment(const char *szEnv)
void setBroker(const char *szBroker)
bool addVariable(const std::string &key, const std::string &value)
Definition textparser.h:29
std::string parseText(const char *element)
Definition textparser.h:43
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:64
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:293
A class for storing options and configuration information.
Definition Property.h:33
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:21
bool compareString(const char *szFirst, const char *szSecond)
Definition utility.cpp:326
std::stringstream OSTRINGSTREAM
Definition utility.h:50