YARP
Yet Another Robot Platform
XMLReaderFileV1.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 
17 
18 #include <yarp/os/LogStream.h>
19 #include <yarp/os/Network.h>
20 #include <yarp/os/Property.h>
21 
22 #include <algorithm>
23 #include <iterator>
24 #include <sstream>
25 #include <string>
26 #include <tinyxml.h>
27 #include <utility>
28 #include <vector>
29 
30 #define SYNTAX_ERROR(line) yFatal() << "Syntax error while loading" << curr_filename << "at line" << line << "."
31 #define SYNTAX_WARNING(line) yWarning() << "Invalid syntax while loading" << curr_filename << "at line" << line << "."
32 
33 // BUG in TinyXML, see
34 // https://sourceforge.net/tracker/?func=detail&aid=3567726&group_id=13559&atid=113559
35 // When this bug is fixed upstream we can enable this
36 #define TINYXML_UNSIGNED_INT_BUG 0
37 
39 {
40 public:
41  explicit Private(XMLReaderFileV1* parent);
42  virtual ~Private();
43 
47 
52 
60 
65 
67  std::string filename;
68  std::string path;
69 #ifdef USE_DTD
71 #endif
72 
74  std::string curr_filename;
75  unsigned int minorVersion;
76  unsigned int majorVersion;
77 };
78 
79 
81  parent(p),
82  minorVersion(0),
83  majorVersion(0)
84 {
85  verbose_output = false;
86 }
87 
89 
91 {
92  filename = fileName;
93 #ifdef WIN32
94  std::replace(filename.begin(), filename.end(), '/', '\\');
95 #endif
96 
97  curr_filename = fileName;
98 #ifdef WIN32
99  path = filename.substr(0, filename.rfind("\\"));
100 #else // WIN32
101  path = filename.substr(0, filename.rfind('/'));
102 #endif //WIN32
103 
104  yDebug() << "Reading file" << filename.c_str();
105  auto* doc = new TiXmlDocument(filename.c_str());
106  if (!doc->LoadFile()) {
107  SYNTAX_ERROR(doc->ErrorRow()) << doc->ErrorDesc();
109  }
110 
111  if (!doc->RootElement()) {
112  SYNTAX_ERROR(doc->Row()) << "No root element.";
114  }
115 
116 #ifdef USE_DTD
117  for (TiXmlNode* childNode = doc->FirstChild(); childNode != 0; childNode = childNode->NextSibling()) {
118  if (childNode->Type() == TiXmlNode::TINYXML_UNKNOWN) {
119  if (dtd.parse(childNode->ToUnknown(), curr_filename)) {
120  break;
121  }
122  }
123  }
124 
125  if (!dtd.valid()) {
126  SYNTAX_WARNING(doc->Row()) << "No DTD found. Assuming version yarprobotinterfaceV1.0";
127  dtd.setDefault();
129  }
130 
131  if (dtd.type != RobotInterfaceDTD::DocTypeRobot) {
132  SYNTAX_WARNING(doc->Row()) << "Expected document of type" << DocTypeToString(RobotInterfaceDTD::DocTypeRobot)
133  << ". Found" << DocTypeToString(dtd.type);
134  }
135 
136  if (dtd.majorVersion != 1 || dtd.minorVersion != 0) {
137  SYNTAX_WARNING(doc->Row()) << "Only yarprobotinterface DTD version 1.0 is supported";
138  }
139 #endif
140 
141  yarp::robotinterface::experimental::XMLReaderResult result = readRobotTag(doc->RootElement());
142  delete doc;
143 
144  // yDebug() << robot;
145 
146  return result;
147 }
148 
150 {
151  curr_filename = " XML runtime string ";
152  auto* doc = new TiXmlDocument();
153  if (!doc->Parse(xmlString.c_str())) {
154  SYNTAX_ERROR(doc->ErrorRow()) << doc->ErrorDesc();
156  }
157 
158  if (!doc->RootElement()) {
159  SYNTAX_ERROR(doc->Row()) << "No root element.";
161  }
162 
163  yarp::robotinterface::experimental::XMLReaderResult result = readRobotTag(doc->RootElement());
164  delete doc;
165 
166  return result;
167 }
168 
169 
171 {
173  result.parsingIsSuccessful = true;
174 
175  if (robotElem->ValueStr() != "robot") {
176  SYNTAX_ERROR(robotElem->Row()) << "Root element should be \"robot\". Found" << robotElem->ValueStr();
178  }
179 
180  if (robotElem->QueryStringAttribute("name", &result.robot.name()) != TIXML_SUCCESS) {
181  SYNTAX_ERROR(robotElem->Row()) << R"("robot" element should contain the "name" attribute)";
183  }
184 
185 #if TINYXML_UNSIGNED_INT_BUG
186  if (robotElem->QueryUnsignedAttribute("build", &robot.build()) != TIXML_SUCCESS) {
187  // No build attribute. Assuming build="0"
188  SYNTAX_WARNING(robotElem->Row()) << "\"robot\" element should contain the \"build\" attribute [unsigned int]. Assuming 0";
189  }
190 #else
191  int tmp;
192  if (robotElem->QueryIntAttribute("build", &tmp) != TIXML_SUCCESS || tmp < 0) {
193  // No build attribute. Assuming build="0"
194  SYNTAX_WARNING(robotElem->Row()) << R"("robot" element should contain the "build" attribute [unsigned int]. Assuming 0)";
195  tmp = 0;
196  }
197  result.robot.build() = (unsigned)tmp;
198 #endif
199 
200  if (robotElem->QueryStringAttribute("portprefix", &result.robot.portprefix()) != TIXML_SUCCESS) {
201  SYNTAX_WARNING(robotElem->Row()) << R"("robot" element should contain the "portprefix" attribute. Using "name" attribute)";
202  result.robot.portprefix() = result.robot.name();
203  }
204 
205  // yDebug() << "Found robot [" << robot.name() << "] build [" << robot.build() << "] portprefix [" << robot.portprefix() << "]";
206 
207  for (TiXmlElement* childElem = robotElem->FirstChildElement(); childElem != nullptr; childElem = childElem->NextSiblingElement()) {
208  if (childElem->ValueStr() == "device" || childElem->ValueStr() == "devices") {
209  yarp::robotinterface::experimental::DeviceList childDevices = readDevices(childElem, result);
210  for (yarp::robotinterface::experimental::DeviceList::const_iterator it = childDevices.begin(); it != childDevices.end(); ++it) {
211  result.robot.devices().push_back(*it);
212  }
213  } else {
214  yarp::robotinterface::experimental::ParamList childParams = readParams(childElem, result);
215  for (yarp::robotinterface::experimental::ParamList::const_iterator it = childParams.begin(); it != childParams.end(); ++it) {
216  result.robot.params().push_back(*it);
217  }
218  }
219  }
220 
221  return result;
222 }
223 
224 
227 {
228  const std::string& valueStr = devicesElem->ValueStr();
229 
230  if (valueStr == "device") {
231  // yDebug() << valueStr;
233  deviceList.push_back(readDeviceTag(devicesElem, result));
234  return deviceList;
235  } else if (valueStr == "devices") {
236  // "devices"
237  return readDevicesTag(devicesElem, result);
238  } else {
239  SYNTAX_ERROR(devicesElem->Row()) << R"(Expected "device" or "devices". Found)" << valueStr;
240  result.parsingIsSuccessful = false;
241  }
243 }
244 
247 {
248  const std::string& valueStr = deviceElem->ValueStr();
249 
250  if (valueStr != "device") {
251  SYNTAX_ERROR(deviceElem->Row()) << "Expected \"device\". Found" << valueStr;
252  result.parsingIsSuccessful = false;
253  }
254 
256 
257  if (deviceElem->QueryStringAttribute("name", &device.name()) != TIXML_SUCCESS) {
258  SYNTAX_ERROR(deviceElem->Row()) << R"("device" element should contain the "name" attribute)";
259  result.parsingIsSuccessful = false;
260  }
261 
262  // yDebug() << "Found device [" << device.name() << "]";
263 
264  if (deviceElem->QueryStringAttribute("type", &device.type()) != TIXML_SUCCESS) {
265  SYNTAX_ERROR(deviceElem->Row()) << R"("device" element should contain the "type" attribute)";
266  result.parsingIsSuccessful = false;
267  }
268 
269  device.params().push_back(yarp::robotinterface::experimental::Param("robotName", result.robot.portprefix()));
270 
271  for (TiXmlElement* childElem = deviceElem->FirstChildElement(); childElem != nullptr; childElem = childElem->NextSiblingElement()) {
272  if (childElem->ValueStr() == "action" || childElem->ValueStr() == "actions") {
273  yarp::robotinterface::experimental::ActionList childActions = readActions(childElem, result);
274  for (yarp::robotinterface::experimental::ActionList::const_iterator it = childActions.begin(); it != childActions.end(); ++it) {
275  device.actions().push_back(*it);
276  }
277  } else {
278  yarp::robotinterface::experimental::ParamList childParams = readParams(childElem, result);
279  for (yarp::robotinterface::experimental::ParamList::const_iterator it = childParams.begin(); it != childParams.end(); ++it) {
280  device.params().push_back(*it);
281  }
282  }
283  }
284 
285  // yDebug() << device;
286  return device;
287 }
288 
291 {
292  //const std::string &valueStr = devicesElem->ValueStr();
293 
294  std::string filename;
295  if (devicesElem->QueryStringAttribute("file", &filename) == TIXML_SUCCESS) {
296  // yDebug() << "Found devices file [" << filename << "]";
297 #ifdef WIN32
298  std::replace(filename.begin(), filename.end(), '/', '\\');
299  filename = path + "\\" + filename;
300 #else // WIN32
301  filename = path + "/" + filename;
302 #endif //WIN32
303  return readDevicesFile(filename, result);
304  }
305 
306  /*
307  if (valueStr.compare("devices") != 0) {
308  SYNTAX_ERROR(devicesElem->Row()) << "Expected \"devices\". Found" << valueStr;
309  }
310  */
311  /*
312  std::string robotName;
313  if (devicesElem->QueryStringAttribute("robot", &robotName) != TIXML_SUCCESS) {
314  SYNTAX_WARNING(devicesElem->Row()) << "\"devices\" element should contain the \"robot\" attribute";
315  }
316 
317  if (robotName != robot.name()) {
318  SYNTAX_WARNING(devicesElem->Row()) << "Trying to import a file for the wrong robot. Found" << robotName << "instead of" << robot.name();
319  }
320  */
321  /*
322  unsigned int build;
323 #if TINYXML_UNSIGNED_INT_BUG
324  if (devicesElem->QueryUnsignedAttribute("build", &build()) != TIXML_SUCCESS) {
325  // No build attribute. Assuming build="0"
326  SYNTAX_WARNING(devicesElem->Row()) << "\"devices\" element should contain the \"build\" attribute [unsigned int]. Assuming 0";
327  }
328 #else
329  int tmp;
330  if (devicesElem->QueryIntAttribute("build", &tmp) != TIXML_SUCCESS || tmp < 0) {
331  // No build attribute. Assuming build="0"
332  SYNTAX_WARNING(devicesElem->Row()) << "\"devices\" element should contain the \"build\" attribute [unsigned int]. Assuming 0";
333  tmp = 0;
334  }
335  build = (unsigned)tmp;
336 #endif
337 
338  if (build != robot.build()) {
339  SYNTAX_WARNING(devicesElem->Row()) << "Import a file for a different robot build. Found" << build << "instead of" << robot.build();
340  }
341  */
343  for (TiXmlElement* childElem = devicesElem->FirstChildElement(); childElem != nullptr; childElem = childElem->NextSiblingElement()) {
344  yarp::robotinterface::experimental::DeviceList childDevices = readDevices(childElem, result);
345  for (yarp::robotinterface::experimental::DeviceList::const_iterator it = childDevices.begin(); it != childDevices.end(); ++it) {
346  devices.push_back(*it);
347  }
348  }
349 
350  return devices;
351 }
352 
355 {
356  std::string old_filename = curr_filename;
357  curr_filename = fileName;
358 
359  yDebug() << "Reading file" << fileName.c_str();
360  auto* doc = new TiXmlDocument(fileName.c_str());
361  if (!doc->LoadFile()) {
362  SYNTAX_ERROR(doc->ErrorRow()) << doc->ErrorDesc();
363  }
364 
365  if (!doc->RootElement()) {
366  SYNTAX_ERROR(doc->Row()) << "No root element.";
367  }
368 
369 #ifdef USE_DTD
370  RobotInterfaceDTD devicesFileDTD;
371  for (TiXmlNode* childNode = doc->FirstChild(); childNode != 0; childNode = childNode->NextSibling()) {
372  if (childNode->Type() == TiXmlNode::TINYXML_UNKNOWN) {
373  if (devicesFileDTD.parse(childNode->ToUnknown(), curr_filename)) {
374  break;
375  }
376  }
377  }
378 
379  if (!devicesFileDTD.valid()) {
380  SYNTAX_WARNING(doc->Row()) << "No DTD found. Assuming version yarprobotinterfaceV1.0";
381  devicesFileDTD.setDefault();
382  devicesFileDTD.type = RobotInterfaceDTD::DocTypeDevices;
383  }
384 
385  if (devicesFileDTD.type != RobotInterfaceDTD::DocTypeDevices) {
386  SYNTAX_ERROR(doc->Row()) << "Expected document of type" << DocTypeToString(RobotInterfaceDTD::DocTypeDevices)
387  << ". Found" << DocTypeToString(devicesFileDTD.type);
388  }
389 
390  if (devicesFileDTD.majorVersion != dtd.majorVersion) {
391  SYNTAX_ERROR(doc->Row()) << "Trying to import a file with a different yarprobotinterface DTD version";
392  }
393 #endif
394 
395  yarp::robotinterface::experimental::DeviceList devices = readDevicesTag(doc->RootElement(), result);
396  delete doc;
397  curr_filename = old_filename;
398  return devices;
399 }
400 
401 
404 {
405  const std::string& valueStr = paramsElem->ValueStr();
406 
407  if (valueStr == "param") {
409  params.push_back(readParamTag(paramsElem, result));
410  return params;
411  } else if (valueStr == "group") {
413  params.push_back(readGroupTag(paramsElem, result));
414  return params;
415  } else if (valueStr == "paramlist") {
416  return readParamListTag(paramsElem, result);
417  } else if (valueStr == "subdevice") {
418  return readSubDeviceTag(paramsElem, result);
419  } else if (valueStr == "params") {
420  return readParamsTag(paramsElem, result);
421  } else {
422  SYNTAX_ERROR(paramsElem->Row()) << R"(Expected "param", "group", "paramlist", "subdevice", or "params". Found)" << valueStr;
423  }
425 }
426 
427 
430 {
431  if (paramElem->ValueStr() != "param") {
432  SYNTAX_ERROR(paramElem->Row()) << "Expected \"param\". Found" << paramElem->ValueStr();
433  result.parsingIsSuccessful = false;
434  }
435 
437 
438  if (paramElem->QueryStringAttribute("name", &param.name()) != TIXML_SUCCESS) {
439  SYNTAX_ERROR(paramElem->Row()) << R"("param" element should contain the "name" attribute)";
440  result.parsingIsSuccessful = false;
441  }
442 
443  // yDebug() << "Found param [" << param.name() << "]";
444 
445  const char* valueText = paramElem->GetText();
446  if (!valueText) {
447  SYNTAX_ERROR(paramElem->Row()) << R"("param" element should have a value [ "name" = )" << param.name() << "]";
448  } else {
449  param.value() = valueText;
450  }
451 
452  // yDebug() << param;
453  return param;
454 }
455 
458 {
459  if (groupElem->ValueStr() != "group") {
460  SYNTAX_ERROR(groupElem->Row()) << "Expected \"group\". Found" << groupElem->ValueStr();
461  result.parsingIsSuccessful = false;
462  }
463 
465 
466  if (groupElem->QueryStringAttribute("name", &group.name()) != TIXML_SUCCESS) {
467  SYNTAX_ERROR(groupElem->Row()) << R"("group" element should contain the "name" attribute)";
468  result.parsingIsSuccessful = false;
469  }
470 
471  // yDebug() << "Found group [" << group.name() << "]";
472 
474  for (TiXmlElement* childElem = groupElem->FirstChildElement(); childElem != nullptr; childElem = childElem->NextSiblingElement()) {
475  yarp::robotinterface::experimental::ParamList childParams = readParams(childElem, result);
476  for (yarp::robotinterface::experimental::ParamList::const_iterator it = childParams.begin(); it != childParams.end(); ++it) {
477  params.push_back(*it);
478  }
479  }
480  if (params.empty()) {
481  SYNTAX_ERROR(groupElem->Row()) << "\"group\" cannot be empty";
482  }
483 
484  std::string groupString;
485  for (auto& param : params) {
486  if (!groupString.empty()) {
487  groupString += " ";
488  }
489  groupString += "(" + param.name() + " " + param.value() + ")";
490  }
491 
492  group.value() = groupString;
493 
494  return group;
495 }
496 
499 {
500  if (paramListElem->ValueStr() != "paramlist") {
501  SYNTAX_ERROR(paramListElem->Row()) << "Expected \"paramlist\". Found" << paramListElem->ValueStr();
502  result.parsingIsSuccessful = false;
503  }
504 
507 
508  if (paramListElem->QueryStringAttribute("name", &mainparam.name()) != TIXML_SUCCESS) {
509  SYNTAX_ERROR(paramListElem->Row()) << R"("paramlist" element should contain the "name" attribute)";
510  result.parsingIsSuccessful = false;
511  }
512 
513  params.push_back(mainparam);
514 
515  // yDebug() << "Found paramlist [" << params.at(0).name() << "]";
516 
517  for (TiXmlElement* childElem = paramListElem->FirstChildElement(); childElem != nullptr; childElem = childElem->NextSiblingElement()) {
518  if (childElem->ValueStr() != "elem") {
519  SYNTAX_ERROR(childElem->Row()) << "Expected \"elem\". Found" << childElem->ValueStr();
520  }
521 
523 
524  if (childElem->QueryStringAttribute("name", &childParam.name()) != TIXML_SUCCESS) {
525  SYNTAX_ERROR(childElem->Row()) << R"("elem" element should contain the "name" attribute)";
526  result.parsingIsSuccessful = false;
527  }
528 
529  const char* valueText = childElem->GetText();
530  if (!valueText) {
531  SYNTAX_ERROR(childElem->Row()) << R"("elem" element should have a value [ "name" = )" << childParam.name() << "]";
532  result.parsingIsSuccessful = false;
533  } else {
534  childParam.value() = valueText;
535  }
536 
537  params.push_back(childParam);
538  }
539 
540  if (params.empty()) {
541  SYNTAX_ERROR(paramListElem->Row()) << "\"paramlist\" cannot be empty";
542  result.parsingIsSuccessful = false;
543  }
544 
545  // +1 skips the first element, that is the main param
546  for (auto it = params.begin() + 1; it != params.end(); ++it) {
548  params.at(0).value() += (params.at(0).value().empty() ? "(" : " ") + param.name();
549  }
550  params.at(0).value() += ")";
551 
552  // yDebug() << params;
553  return params;
554 }
555 
558 {
559  if (subDeviceElem->ValueStr() != "subdevice") {
560  SYNTAX_ERROR(subDeviceElem->Row()) << "Expected \"subdevice\". Found" << subDeviceElem->ValueStr();
561  result.parsingIsSuccessful = false;
562  }
563 
565 
566  //FIXME Param featIdParam;
568 
569  //FIXME featIdParam.name() = "FeatId";
570  subDeviceParam.name() = "subdevice";
571 
572  //FIXME if (subDeviceElem->QueryStringAttribute("name", &featIdParam.value()) != TIXML_SUCCESS) {
573  // SYNTAX_ERROR(subDeviceElem->Row()) << "\"subdevice\" element should contain the \"name\" attribute";
574  // }
575 
576  if (subDeviceElem->QueryStringAttribute("type", &subDeviceParam.value()) != TIXML_SUCCESS) {
577  SYNTAX_ERROR(subDeviceElem->Row()) << R"("subdevice" element should contain the "type" attribute)";
578  }
579 
580  //FIXME params.push_back(featIdParam);
581  params.push_back(subDeviceParam);
582 
583  // yDebug() << "Found subdevice [" << params.at(0).value() << "]";
584 
585  for (TiXmlElement* childElem = subDeviceElem->FirstChildElement(); childElem != nullptr; childElem = childElem->NextSiblingElement()) {
586  yarp::robotinterface::experimental::ParamList childParams = readParams(childElem, result);
587  for (yarp::robotinterface::experimental::ParamList::const_iterator it = childParams.begin(); it != childParams.end(); ++it) {
588  params.push_back(yarp::robotinterface::experimental::Param(it->name(), it->value()));
589  }
590  }
591 
592  // yDebug() << params;
593  return params;
594 }
595 
598 {
599  //const std::string &valueStr = paramsElem->ValueStr();
600 
601  std::string filename;
602  if (paramsElem->QueryStringAttribute("file", &filename) == TIXML_SUCCESS) {
603  // yDebug() << "Found params file [" << filename << "]";
604 #ifdef WIN32
605  std::replace(filename.begin(), filename.end(), '/', '\\');
606  filename = path + "\\" + filename;
607 #else // WIN32
608  filename = path + "/" + filename;
609 #endif //WIN32
610  return readParamsFile(filename, result);
611  }
612 
613  /*std::string robotName;
614  if (paramsElem->QueryStringAttribute("robot", &robotName) != TIXML_SUCCESS) {
615  SYNTAX_WARNING(paramsElem->Row()) << "\"params\" element should contain the \"robot\" attribute";
616  }
617 
618  if (robotName != robot.name()) {
619  SYNTAX_WARNING(paramsElem->Row()) << "Trying to import a file for the wrong robot. Found" << robotName << "instead of" << robot.name();
620  }*/
621 
622  /*
623  unsigned int build;
624 #if TINYXML_UNSIGNED_INT_BUG
625  if (paramsElem->QueryUnsignedAttribute("build", &build()) != TIXML_SUCCESS) {
626  // No build attribute. Assuming build="0"
627  SYNTAX_WARNING(paramsElem->Row()) << "\"params\" element should contain the \"build\" attribute [unsigned int]. Assuming 0";
628  }
629 #else
630  int tmp;
631  if (paramsElem->QueryIntAttribute("build", &tmp) != TIXML_SUCCESS || tmp < 0) {
632  // No build attribute. Assuming build="0"
633  SYNTAX_WARNING(paramsElem->Row()) << "\"params\" element should contain the \"build\" attribute [unsigned int]. Assuming 0";
634  tmp = 0;
635  }
636  build = (unsigned)tmp;
637 #endif
638 
639  if (build != robot.build()) {
640  SYNTAX_WARNING(paramsElem->Row()) << "Import a file for a different robot build. Found" << build << "instead of" << robot.build();
641  }
642  */
644  for (TiXmlElement* childElem = paramsElem->FirstChildElement(); childElem != nullptr; childElem = childElem->NextSiblingElement()) {
645  yarp::robotinterface::experimental::ParamList childParams = readParams(childElem, result);
646  for (yarp::robotinterface::experimental::ParamList::const_iterator it = childParams.begin(); it != childParams.end(); ++it) {
647  params.push_back(*it);
648  }
649  }
650 
651  return params;
652 }
653 
656 {
657  std::string old_filename = curr_filename;
658  curr_filename = fileName;
659 
660  yDebug() << "Reading file" << fileName.c_str();
661  auto* doc = new TiXmlDocument(fileName.c_str());
662  if (!doc->LoadFile()) {
663  SYNTAX_ERROR(doc->ErrorRow()) << doc->ErrorDesc();
664  result.parsingIsSuccessful = false;
666  }
667 
668  if (!doc->RootElement()) {
669  SYNTAX_ERROR(doc->Row()) << "No root element.";
670  result.parsingIsSuccessful = false;
672  }
673 
674 #ifdef USE_DTD
675  RobotInterfaceDTD paramsFileDTD;
676  for (TiXmlNode* childNode = doc->FirstChild(); childNode != 0; childNode = childNode->NextSibling()) {
677  if (childNode->Type() == TiXmlNode::TINYXML_UNKNOWN) {
678  if (paramsFileDTD.parse(childNode->ToUnknown(), curr_filename)) {
679  break;
680  }
681  }
682  }
683 
684  if (!paramsFileDTD.valid()) {
685  SYNTAX_WARNING(doc->Row()) << "No DTD found. Assuming version yarprobotinterfaceV1.0";
686  paramsFileDTD.setDefault();
687  paramsFileDTD.type = RobotInterfaceDTD::DocTypeParams;
688  }
689 
690  if (paramsFileDTD.type != RobotInterfaceDTD::DocTypeParams) {
691  SYNTAX_ERROR(doc->Row()) << "Expected document of type" << DocTypeToString(RobotInterfaceDTD::DocTypeParams)
692  << ". Found" << DocTypeToString(paramsFileDTD.type);
693  }
694 
695  if (paramsFileDTD.majorVersion != dtd.majorVersion) {
696  SYNTAX_ERROR(doc->Row()) << "Trying to import a file with a different yarprobotinterface DTD version";
697  }
698 
699 #endif
700  yarp::robotinterface::experimental::ParamList params = readParamsTag(doc->RootElement(), result);
701  delete doc;
702  curr_filename = old_filename;
703  return params;
704 }
705 
708 {
709  const std::string& valueStr = actionsElem->ValueStr();
710 
711  if (valueStr != "action" && valueStr != "actions") {
712  SYNTAX_ERROR(actionsElem->Row()) << R"(Expected "action" or "actions". Found)" << valueStr;
713  }
714 
715  if (valueStr == "action") {
717  actionList.push_back(readActionTag(actionsElem, result));
718  return actionList;
719  }
720  // "actions"
721  return readActionsTag(actionsElem, result);
722 }
723 
726 {
727  if (actionElem->ValueStr() != "action") {
728  SYNTAX_ERROR(actionElem->Row()) << "Expected \"action\". Found" << actionElem->ValueStr();
729  }
730 
732 
733  if (actionElem->QueryValueAttribute<yarp::robotinterface::experimental::ActionPhase>("phase", &action.phase()) != TIXML_SUCCESS || action.phase() == yarp::robotinterface::experimental::ActionPhaseUnknown) {
734  SYNTAX_ERROR(actionElem->Row()) << R"("action" element should contain the "phase" attribute [startup|interrupt{1,2,3}|shutdown])";
735  }
736 
737 
738  if (actionElem->QueryValueAttribute<yarp::robotinterface::experimental::ActionType>("type", &action.type()) != TIXML_SUCCESS || action.type() == yarp::robotinterface::experimental::ActionTypeUnknown) {
739  SYNTAX_ERROR(actionElem->Row()) << R"("action" element should contain the "type" attribute [configure|calibrate|attach|abort|detach|park|custom])";
740  }
741 
742  // yDebug() << "Found action [ ]";
743 
744 #if TINYXML_UNSIGNED_INT_BUG
745  if (actionElem->QueryUnsignedAttribute("level", &action.level()) != TIXML_SUCCESS) {
746  SYNTAX_ERROR(actionElem->Row()) << "\"action\" element should contain the \"level\" attribute [unsigned int]";
747  }
748 #else
749  int tmp;
750  if (actionElem->QueryIntAttribute("level", &tmp) != TIXML_SUCCESS || tmp < 0) {
751  SYNTAX_ERROR(actionElem->Row()) << R"("action" element should contain the "level" attribute [unsigned int])";
752  }
753  action.level() = (unsigned)tmp;
754 #endif
755 
756  for (TiXmlElement* childElem = actionElem->FirstChildElement(); childElem != nullptr; childElem = childElem->NextSiblingElement()) {
757  yarp::robotinterface::experimental::ParamList childParams = readParams(childElem, result);
758  for (yarp::robotinterface::experimental::ParamList::const_iterator it = childParams.begin(); it != childParams.end(); ++it) {
759  action.params().push_back(*it);
760  }
761  }
762 
763  // yDebug() << action;
764  return action;
765 }
766 
769 {
770  //const std::string &valueStr = actionsElem->ValueStr();
771 
772  std::string filename;
773  if (actionsElem->QueryStringAttribute("file", &filename) == TIXML_SUCCESS) {
774  // yDebug() << "Found actions file [" << filename << "]";
775 #ifdef WIN32
776  std::replace(filename.begin(), filename.end(), '/', '\\');
777  filename = path + "\\" + filename;
778 #else // WIN32
779  filename = path + "/" + filename;
780 #endif //WIN32
781  return readActionsFile(filename, result);
782  }
783 
784  std::string robotName;
785  if (actionsElem->QueryStringAttribute("robot", &robotName) != TIXML_SUCCESS) {
786  SYNTAX_WARNING(actionsElem->Row()) << R"("actions" element should contain the "robot" attribute)";
787  }
788 
789  if (robotName != result.robot.name()) {
790  SYNTAX_WARNING(actionsElem->Row()) << "Trying to import a file for the wrong robot. Found" << robotName << "instead of" << result.robot.name();
791  }
792 
793  unsigned int build;
794 #if TINYXML_UNSIGNED_INT_BUG
795  if (actionsElem->QueryUnsignedAttribute("build", &build()) != TIXML_SUCCESS) {
796  // No build attribute. Assuming build="0"
797  SYNTAX_WARNING(actionsElem->Row()) << "\"actions\" element should contain the \"build\" attribute [unsigned int]. Assuming 0";
798  }
799 #else
800  int tmp;
801  if (actionsElem->QueryIntAttribute("build", &tmp) != TIXML_SUCCESS || tmp < 0) {
802  // No build attribute. Assuming build="0"
803  SYNTAX_WARNING(actionsElem->Row()) << R"("actions" element should contain the "build" attribute [unsigned int]. Assuming 0)";
804  tmp = 0;
805  }
806  build = (unsigned)tmp;
807 #endif
808 
809  if (build != result.robot.build()) {
810  SYNTAX_WARNING(actionsElem->Row()) << "Import a file for a different robot build. Found" << build << "instead of" << result.robot.build();
811  }
812 
814  for (TiXmlElement* childElem = actionsElem->FirstChildElement(); childElem != nullptr; childElem = childElem->NextSiblingElement()) {
815  yarp::robotinterface::experimental::ActionList childActions = readActions(childElem, result);
816  for (yarp::robotinterface::experimental::ActionList::const_iterator it = childActions.begin(); it != childActions.end(); ++it) {
817  actions.push_back(*it);
818  }
819  }
820 
821  return actions;
822 }
823 
826 {
827  std::string old_filename = curr_filename;
828  curr_filename = fileName;
829 
830  yDebug() << "Reading file" << fileName.c_str();
831  auto* doc = new TiXmlDocument(fileName.c_str());
832  if (!doc->LoadFile()) {
833  SYNTAX_ERROR(doc->ErrorRow()) << doc->ErrorDesc();
834  }
835 
836  if (!doc->RootElement()) {
837  SYNTAX_ERROR(doc->Row()) << "No root element.";
838  }
839 
840 #ifdef USE_DTD
841  RobotInterfaceDTD actionsFileDTD;
842  for (TiXmlNode* childNode = doc->FirstChild(); childNode != 0; childNode = childNode->NextSibling()) {
843  if (childNode->Type() == TiXmlNode::TINYXML_UNKNOWN) {
844  if (actionsFileDTD.parse(childNode->ToUnknown(), curr_filename)) {
845  break;
846  }
847  }
848  }
849 
850  if (!actionsFileDTD.valid()) {
851  SYNTAX_WARNING(doc->Row()) << "No DTD found. Assuming version yarprobotinterfaceV1.0";
852  actionsFileDTD.setDefault();
853  actionsFileDTD.type = RobotInterfaceDTD::DocTypeActions;
854  }
855 
856  if (actionsFileDTD.type != RobotInterfaceDTD::DocTypeActions) {
857  SYNTAX_ERROR(doc->Row()) << "Expected document of type" << DocTypeToString(RobotInterfaceDTD::DocTypeActions)
858  << ". Found" << DocTypeToString(actionsFileDTD.type);
859  }
860 
861  if (actionsFileDTD.majorVersion != dtd.majorVersion) {
862  SYNTAX_ERROR(doc->Row()) << "Trying to import a file with a different yarprobotinterface DTD version";
863  }
864 
865 #endif
866  yarp::robotinterface::experimental::ActionList actions = readActionsTag(doc->RootElement(), result);
867  delete doc;
868  curr_filename = old_filename;
869  return actions;
870 }
871 
873 {
874  mPriv->verbose_output = verb;
875  return mPriv->readRobotFromFile(filename);
876 }
877 
879 {
880  mPriv->verbose_output = verb;
881  return mPriv->readRobotFromString(xmlString);
882 }
883 
885  mPriv(new Private(this))
886 {
887 }
888 
890 {
891  if (mPriv) {
892  delete mPriv;
893  }
894 }
#define yDebug(...)
Definition: Log.h:237
#define SYNTAX_ERROR(line)
#define SYNTAX_WARNING(line)
bool parse(TiXmlUnknown *unknownNode, const std::string &curr_filename)
Result of the parsing of XMLReader.
Definition: XMLReader.h:26
Robot robot
If parsingIsSuccessful is true, contains a valid robot instance.
Definition: XMLReader.h:43
bool parsingIsSuccessful
True if the parsing was successful, false otherwise.
Definition: XMLReader.h:38
yarp::robotinterface::experimental::DeviceList readDevicesFile(const std::string &fileName, yarp::robotinterface::experimental::XMLReaderResult &result)
yarp::robotinterface::experimental::ParamList readParamListTag(TiXmlElement *paramListElem, yarp::robotinterface::experimental::XMLReaderResult &result)
yarp::robotinterface::experimental::DeviceList readDevices(TiXmlElement *devicesElem, yarp::robotinterface::experimental::XMLReaderResult &result)
yarp::robotinterface::experimental::Device readDeviceTag(TiXmlElement *deviceElem, yarp::robotinterface::experimental::XMLReaderResult &result)
yarp::robotinterface::experimental::XMLReaderResult readRobotTag(TiXmlElement *robotElem)
yarp::robotinterface::experimental::ActionList readActions(TiXmlElement *actionsElem, yarp::robotinterface::experimental::XMLReaderResult &result)
yarp::robotinterface::experimental::Param readParamTag(TiXmlElement *paramElem, yarp::robotinterface::experimental::XMLReaderResult &result)
yarp::robotinterface::experimental::ActionList readActionsFile(const std::string &fileName, yarp::robotinterface::experimental::XMLReaderResult &result)
yarp::robotinterface::experimental::ParamList readParamsTag(TiXmlElement *paramsElem, yarp::robotinterface::experimental::XMLReaderResult &result)
yarp::robotinterface::experimental::DeviceList readDevicesTag(TiXmlElement *devicesElem, yarp::robotinterface::experimental::XMLReaderResult &result)
yarp::robotinterface::experimental::ParamList readSubDeviceTag(TiXmlElement *subDeviceElem, yarp::robotinterface::experimental::XMLReaderResult &result)
yarp::robotinterface::experimental::XMLReaderResult readRobotFromString(const std::string &xmlString)
yarp::robotinterface::experimental::XMLReaderResult readRobotFromFile(const std::string &fileName)
yarp::robotinterface::experimental::ParamList readParams(TiXmlElement *paramsElem, yarp::robotinterface::experimental::XMLReaderResult &result)
yarp::robotinterface::experimental::ActionList readActionsTag(TiXmlElement *actionsElem, yarp::robotinterface::experimental::XMLReaderResult &result)
yarp::robotinterface::experimental::Action readActionTag(TiXmlElement *actionElem, yarp::robotinterface::experimental::XMLReaderResult &result)
yarp::robotinterface::experimental::Param readGroupTag(TiXmlElement *groupElem, yarp::robotinterface::experimental::XMLReaderResult &result)
yarp::robotinterface::experimental::ParamList readParamsFile(const std::string &fileName, yarp::robotinterface::experimental::XMLReaderResult &result)
yarp::robotinterface::experimental::XMLReaderResult getRobotFromFile(const std::string &filename, bool verbose=false) override
yarp::robotinterface::experimental::XMLReaderResult getRobotFromString(const std::string &xmlString, bool verbose=false) override
std::vector< robotinterface::experimental::Action > ActionList
Definition: Types.h:35
std::vector< robotinterface::experimental::Device > DeviceList
Definition: Types.h:36
std::vector< robotinterface::experimental::Param > ParamList
Definition: Types.h:32
std::string DocTypeToString(RobotInterfaceDTD::DocType doctype)