ConfigParser.cpp
Go to the documentation of this file.
1 #include "ConfigParser.h"
2 
3 #include <SimoxUtility/algorithm/string/string_tools.h>
4 
6 {
7  std::string
9  {
10  std::stringstream ss;
11  ss << message << std::endl << "Context:" << std::endl;
12  ss << context.toString();
13  return ss.str();
14  }
15 
16  ConfigParser::ConfigParser(MultiNodeRapidXMLReader reader) : hardwareConfigNode(reader)
17  {
18  }
19 
21  {
22  this->hardwareConfigNode.addNode(hardwareConfigNode);
23  }
24 
25  void
27  {
28  // Clear previous errors and reset parsed data
29  errors.clear();
30  config = HardwareConfig();
31 
32  // Parse profiles first
33  for (auto& node : hardwareConfigNode.nodes())
34  {
35  std::string nodeName = node.name();
36  if (nodeName == "Profiles")
37  {
38  parseProfiles(node);
39  }
40  }
41 
42  // Parse devices
43  for (auto& node : hardwareConfigNode.nodes())
44  {
45  std::string nodeName = node.name();
46  if (nodeName == "Device")
47  {
48  std::shared_ptr<DeviceConfig> deviceConfig;
49  bool deviceValid = parseDevice(deviceConfig, node);
50  if (deviceValid)
51  {
52  config.addDeviceConfig(deviceConfig->getName(), deviceConfig);
53  }
54  }
55  else if (nodeName == "Profiles")
56  {
57  // Skip: already read
58  continue;
59  }
60  else if (nodeName == "include")
61  {
62  // Skip: parsed by file reader
63  continue;
64  }
65  else
66  {
67  errors.push_back("Unexpected node " + nodeName +
68  " at top level of HardwareConfig xml");
69  }
70  }
71 
72  // Parsing finished, inform config objects
73  config.onParsingFinished();
74 
75  // Error message handling
76  if (errors.size() > 0)
77  {
78  std::stringstream ss;
79  for (auto& error : errors)
80  {
81  ss << error << std::endl;
82  }
83  throw ParserError(ss.str());
84  }
85  }
86 
87  std::vector<std::string>&
89  {
90  return warnings;
91  }
92 
95  {
96  return config;
97  }
98 
99  bool
100  ConfigParser::parseDevice(std::shared_ptr<DeviceConfig>& deviceConfig,
101  RapidXmlReaderNode& deviceNode)
102  {
103  if (!deviceNode.has_attribute("type"))
104  {
105  errors.push_back(createErrorMessageWithContext(
106  "Device node is missing attribute \"type\"", deviceNode));
107  return false;
108  }
109  if (!deviceNode.has_attribute("name"))
110  {
111  errors.push_back(createErrorMessageWithContext(
112  "Device node is missing attribute \"name\"", deviceNode));
113  return false;
114  }
115 
116  std::string name = deviceNode.attribute_value("name");
117  std::string type = deviceNode.attribute_value("type");
118 
119 
120  std::shared_ptr<DeviceConfigBase> profile;
121  if (deviceNode.has_attribute("profile") &&
122  findProfile<DeviceConfigBase>(
123  profile, type, deviceNode.attribute_value("profile"), "Device", deviceProfiles))
124  {
125  deviceConfig = std::make_shared<DeviceConfig>(type, name, profile);
126  }
127  else
128  {
129  deviceConfig = std::make_shared<DeviceConfig>(type, name);
130  warnings.push_back("Device with type " + type + " and name " + name +
131  " does not have a profile");
132  }
133 
134  auto slaveNodes = deviceNode.nodes("Slave");
135  if (slaveNodes.size() < 1)
136  {
137  // Maybe only a warning
138  errors.push_back("Device with type " + deviceConfig->getType() + " and name " +
139  deviceConfig->getName() + " does not have any Slaves");
140  }
141  else
142  {
143  bool needsType = slaveNodes.size() != 1;
144  for (auto& node : slaveNodes)
145  {
146  std::shared_ptr<SlaveConfig> slaveConfig;
147  bool slaveValid = parseSlave(
148  slaveConfig, node, needsType ? SlaveType::WithType : SlaveType::WithoutType);
149  if (slaveValid)
150  {
151  deviceConfig->addSlaveConfig(slaveConfig);
152  }
153  }
154  }
155 
156 
157  for (auto& node : deviceNode.nodes())
158  {
159  std::string nodeName = node.name();
160  if (nodeName == "Slave")
161  {
162  // Skip all slaves because they have already been parsed
163  continue;
164  }
165 
166  if (nodeName == "Subdevice")
167  {
168  std::shared_ptr<DeviceConfigBase> subDeviceConfig;
169  bool subDeviceValid = parseSubDevice(subDeviceConfig, node);
170  if (subDeviceValid)
171  {
172  deviceConfig->addSubDeviceConfig(subDeviceConfig->getName(), subDeviceConfig);
173  }
174  }
175  else if (nodeName == "Controller")
176  {
177  parseController(*deviceConfig, node, ConfigTag::TagCustom);
178  }
179  else if (nodeName == "Config")
180  {
181  parseConfigNode(*deviceConfig, node, ConfigTag::TagCustom);
182  }
183  else
184  {
185  errors.push_back("Device with type " + deviceConfig->getType() + " and name " +
186  deviceConfig->getName() + " contains illegal " + nodeName +
187  " node");
188  }
189  }
190  return true;
191  }
192 
193  bool
194  ConfigParser::parseDeviceProfile(std::shared_ptr<DeviceConfigBase>& deviceProfileConfig,
195  RapidXmlReaderNode& deviceNode)
196  {
197  if (!deviceNode.has_attribute("type"))
198  {
199  errors.push_back(createErrorMessageWithContext(
200  "Device profile node is missing attribute \"type\"", deviceNode));
201  return false;
202  }
203  if (!deviceNode.has_attribute("profilename"))
204  {
205  errors.push_back(createErrorMessageWithContext(
206  "Device profile node is missing attribute \"profilename\"", deviceNode));
207  return false;
208  }
209 
210  std::string name = deviceNode.attribute_value("profilename");
211  deviceProfileConfig =
212  std::make_shared<DeviceConfigBase>(deviceNode.attribute_value("type"), name);
213 
214 
215  for (auto& node : deviceNode.nodes())
216  {
217  std::string nodeName = node.name();
218  if (nodeName == "Controller")
219  {
220  parseController(*deviceProfileConfig, node, ConfigTag::TagProfile);
221  }
222  else if (nodeName == "Config")
223  {
224  parseConfigNode(*deviceProfileConfig, node, ConfigTag::TagProfile);
225  }
226  else
227  {
228  errors.push_back("Device Profile with type " + deviceProfileConfig->getType() +
229  " and profilename " + deviceProfileConfig->getName() +
230  " contains illegal " + nodeName + " node");
231  }
232  }
233  return true;
234  }
235 
236  bool
237  ConfigParser::parseSubDevice(std::shared_ptr<DeviceConfigBase>& subDeviceConfig,
238  RapidXmlReaderNode& subDeviceNode)
239  {
240  if (!subDeviceNode.has_attribute("name"))
241  {
242  errors.push_back(createErrorMessageWithContext(
243  "Subdevice node does not have \"name\" attribute", subDeviceNode));
244  return false;
245  }
246  if (!subDeviceNode.has_attribute("type"))
247  {
248  errors.push_back(createErrorMessageWithContext(
249  "Subdevice node does not have \"type\" attribute", subDeviceNode));
250  return false;
251  }
252  std::string name = subDeviceNode.attribute_value("name");
253  std::string type = subDeviceNode.attribute_value("type");
254 
255  std::shared_ptr<DeviceConfigBase> profile;
256  if (subDeviceNode.has_attribute("profile") &&
257  findProfile<DeviceConfigBase>(profile,
258  type,
259  subDeviceNode.attribute_value("profile"),
260  "Subdevice",
261  deviceProfiles))
262  {
263  subDeviceConfig = std::make_shared<DeviceConfigBase>(type, name, profile);
264  }
265  else
266  {
267  subDeviceConfig = std::make_shared<DeviceConfigBase>(type, name);
268  }
269 
270 
271  for (auto& node : subDeviceNode.nodes())
272  {
273  std::string nodeName = node.name();
274  if (nodeName == "Controller")
275  {
276  parseController(*subDeviceConfig, node, ConfigTag::TagCustom);
277  }
278  else if (nodeName == "Config")
279  {
280  parseConfigNode(*subDeviceConfig, node, ConfigTag::TagCustom);
281  }
282  else
283  {
284  errors.push_back("SubDevice with type " + subDeviceConfig->getType() +
285  " and name " + subDeviceConfig->getName() + " contains illegal " +
286  nodeName + " node");
287  }
288  }
289  return true;
290  }
291 
292  bool
293  ConfigParser::parseSlave(std::shared_ptr<SlaveConfig>& slaveConfig,
294  RapidXmlReaderNode& slaveNode,
295  SlaveType slaveType)
296  {
297  std::optional<std::string> type;
298  if (!slaveNode.has_attribute("type"))
299  {
301  {
302  errors.push_back(createErrorMessageWithContext(
303  "Slave is missing a \"type\" attribute", slaveNode));
304  return false;
305  }
306  }
307  else
308  {
309  type = std::make_optional(slaveNode.attribute_value("type"));
310  }
311 
312  std::optional<std::string> name;
313  if (slaveNode.has_attribute("name"))
314  {
315  name = std::make_optional(slaveNode.attribute_value("name"));
316  }
317 
318  // Check number of identifier nodes and abort if necessary
319  auto identifierNodes = slaveNode.nodes("Identifier");
320  if (identifierNodes.size() != 1)
321  {
322  errors.push_back(createErrorMessageWithContext(
323  "Slave does not have exactly one identifier node", slaveNode));
324  return false;
325  }
326 
327  // Parse slave identifier
328  SlaveIdentifierConfig slaveIdentifier;
329 
330  // Parse profile and create config object
331  std::shared_ptr<SlaveProfile> profile;
332  if (slaveNode.has_attribute("profile") &&
333  findProfile<SlaveProfile>(profile,
334  type.value_or(""),
335  slaveNode.attribute_value("profile"),
336  "Slave",
337  slaveProfiles))
338  {
339  slaveIdentifier = profile->getIdentifier();
340  parseSlaveIdentifier(
341  slaveIdentifier, identifierNodes.at(0), type, ConfigTag::TagCustom, true);
342  slaveConfig = std::make_shared<SlaveConfig>(slaveIdentifier, type, name, profile);
343  }
344  else
345  {
346  parseSlaveIdentifier(
347  slaveIdentifier, identifierNodes.at(0), type, ConfigTag::TagCustom, true);
348  slaveConfig = std::make_shared<SlaveConfig>(slaveIdentifier, type, name);
349  if (type.has_value() || name.has_value())
350  {
351  warnings.push_back(createErrorMessageWithContext(
352  "Slave with type " + type.value_or("<no type>") + " and name " +
353  name.value_or("<no name>") + " does not have a profile",
354  slaveNode));
355  }
356  }
357 
358  // Parse the other nodes
359  for (auto& node : slaveNode.nodes())
360  {
361  std::string nodeName = node.name();
362 
363  if (nodeName == "Identifier")
364  {
365  // Skip identifier node because it has already been parsed
366  continue;
367  }
368 
369  if (nodeName == "Config")
370  {
371  parseConfigNode(*slaveConfig, node, ConfigTag::TagCustom);
372  }
373  else
374  {
375  std::stringstream ss;
376  ss << "Slave with identifier (" << slaveIdentifier << ") has illegal " << nodeName
377  << " node";
378  errors.push_back(createErrorMessageWithContext(ss.str(), slaveNode));
379  continue;
380  }
381  }
382  return true;
383  }
384 
385  bool
386  ConfigParser::parseSlaveProfile(std::shared_ptr<SlaveProfile>& slaveConfig,
387  RapidXmlReaderNode& slaveNode)
388  {
389  if (!slaveNode.has_attribute("type"))
390  {
391  errors.push_back(createErrorMessageWithContext(
392  "Slave Profile is missing a \"type\" attribute", slaveNode));
393  return false;
394  }
395  std::string type = slaveNode.attribute_value("type");
396 
397  if (!slaveNode.has_attribute("profilename"))
398  {
399  errors.push_back(createErrorMessageWithContext(
400  "Slave Profile is missing a \"profilename\" attribute", slaveNode));
401  return false;
402  }
403  std::string name = slaveNode.attribute_value("profilename");
404 
405  // Parse the Identifier node first because it is needed to create the SlaveConfig object
406  auto identifierNodes = slaveNode.nodes("Identifier");
407  if (identifierNodes.size() > 1)
408  {
409  errors.push_back(createErrorMessageWithContext(
410  "Slave Profile does have too many identifiers", slaveNode));
411  return false;
412  }
413  // Parse slave identifier
414  SlaveIdentifierConfig slaveIdentifier;
415  if (identifierNodes.size() == 1)
416  {
417  // parse idenfifier, false means that we do not check if the identifier is complete / valid
418  // because it is allowed to only define parts of the idenfitier in the profile
419  parseSlaveIdentifier(
420  slaveIdentifier, identifierNodes.at(0), {type}, ConfigTag::TagProfile, false);
421  }
422 
423  slaveConfig = std::make_shared<SlaveProfile>(slaveIdentifier, type, name);
424 
425  // Parse the other nodes
426  for (auto& node : slaveNode.nodes())
427  {
428  std::string nodeName = node.name();
429 
430  if (nodeName == "Identifier")
431  {
432  // Skip identifier node because it has already been parsed
433  continue;
434  }
435 
436  if (nodeName == "Config")
437  {
438  parseConfigNode(*slaveConfig, node, ConfigTag::TagProfile);
439  }
440  else
441  {
442  errors.push_back(createErrorMessageWithContext(
443  "Slave Profile has illegal " + nodeName + " node", slaveNode));
444  continue;
445  }
446  }
447  return true;
448  }
449 
450  void
451  ConfigParser::parseConfigNode(Config& config, RapidXmlReaderNode& configNode, ConfigTag tag)
452  {
453  for (auto& node : configNode.nodes())
454  {
455  std::string nodeName = node.name();
456 
457  if (!node.has_attribute("name"))
458  {
459  errors.push_back(createErrorMessageWithContext(
460  nodeName + " node is missing a \"name\" attribute", configNode));
461  continue;
462  }
463  std::string name = node.attribute_value("name");
464 
465  try
466  {
467  if (nodeName == "Bool")
468  {
469  std::string value = node.value();
470  config.setBool(name, strToBool(value), tag);
471  }
472  else if (nodeName == "Float")
473  {
474  config.setFloat(name, node.value_as_float(), tag);
475  }
476  else if (nodeName == "Int")
477  {
478  config.setInt(name, node.value_as_int32(), tag);
479  }
480  else if (nodeName == "UInt")
481  {
482  config.setUint(name, node.value_as_uint32(), tag);
483  }
484  else if (nodeName == "BoolList")
485  {
486  std::list<bool> list = parseList<bool>(
487  node.value(), [this](std::string s) { return strToBool(s); });
488  config.setBoolList(name, list, tag);
489  }
490  else if (nodeName == "FloatList")
491  {
492  std::list<float> list =
493  parseList<float>(node.value(), [](std::string s) { return std::stof(s); });
494  config.setFloatList(name, list, tag);
495  }
496  else if (nodeName == "IntList")
497  {
498  std::list<std::int32_t> list = parseList<std::int32_t>(
499  node.value(), [](std::string s) { return std::stol(s); });
500  config.setIntList(name, list, tag);
501  }
502  else if (nodeName == "UIntList")
503  {
504  std::list<std::uint32_t> list = parseList<std::uint32_t>(
505  node.value(), [](std::string s) { return std::stoul(s); });
506  config.set<std::list<std::uint32_t>>(name, list, tag);
507  }
508  else if (nodeName == "FloatMatrix")
509  {
510  auto mat = parseMatrix<float>(node, [](std::string s) { return std::stof(s); });
511  config.set<Eigen ::Matrix<float, Eigen::Dynamic, Eigen::Dynamic>>(
512  name, mat, tag);
513  }
514  else if (nodeName == "IntMatrix")
515  {
516  auto mat =
517  parseMatrix<std::int32_t>(node, [](std::string s) { return std::stol(s); });
519  name, mat, tag);
520  }
521  else if (nodeName == "UIntMatrix")
522  {
523  auto mat = parseMatrix<std::uint32_t>(
524  node, [](std::string s) { return std::stoul(s); });
526  name, mat, tag);
527  }
528  else if (nodeName == "LinearConvertedValue")
529  {
530  if (node.has_attribute("factor") && node.has_attribute("offset"))
531  {
532  config.setLinearConfig(
533  name,
534  types::LinearConfig(node.attribute_as_float("factor"),
535  node.attribute_as_float("offset")),
536  tag);
537  }
538  else
539  {
540  types::LinearConfig tmpCfg;
541  if (node.has_attribute("factor"))
542  {
543  float factor = node.attribute_as_float("factor");
544  tmpCfg.setFactorAbsoluteValue(std::abs(factor));
545  tmpCfg.setFactorIsNegative(factor < 0);
546  }
547  if (node.has_attribute("factorAbs"))
548  {
549  float factor = node.attribute_as_float("factorAbs");
550  if (factor < 0)
551  {
552  errors.push_back(createErrorMessageWithContext(
553  "factorAbs attribute of LinearConvertedValue has "
554  "a negative value",
555  configNode));
556  }
557  tmpCfg.setFactorAbsoluteValue(std::abs(factor));
558  }
559  if (node.has_attribute("factorSign"))
560  {
561  std::string val = node.attribute_value("factorSign");
562  if (val == "+" || val == "positive")
563  {
564  tmpCfg.setFactorIsNegative(false);
565  }
566  else if (val == "-" || val == "negative")
567  {
568  tmpCfg.setFactorIsNegative(true);
569  }
570  else
571  {
572  errors.push_back(createErrorMessageWithContext(
573  "factor attribute of LinearConvertedValue has invalid value. "
574  "Value: `" +
575  val +
576  "`, allowed values are `+`, `-`, `positive` and "
577  "`negative`.",
578  configNode));
579  }
580  }
581  if (node.has_attribute("offset"))
582  {
583  tmpCfg.setOffset(node.attribute_as_float("offset"));
584  }
585  config.setLinearConfig(name, std::move(tmpCfg), tag);
586  }
587  }
588  else if (nodeName == "ModularConvertedValue")
589  {
590  auto evaluate = [](const std::string& value) -> std::optional<float>
591  {
592  std::vector<std::string> parts = simox::alg::split(value, "^");
593 
594  if (parts.size() == 1)
595  {
596  return std::stof(value);
597  }
598  else if (parts.size() == 2)
599  {
600  return std::pow(std::stof(parts.at(0)), std::stof(parts.at(1)));
601  }
602 
603  return std::nullopt;
604  };
605 
606  if (node.has_attribute("zeroOffset") and
607  node.has_attribute("discontinuityOffset") and
608  node.has_attribute("maxValue") and node.has_attribute("isInverted"))
609  {
610  std::optional<float> discontinuityOffset =
611  evaluate(node.attribute_value("discontinuityOffset"));
612  bool any_error = false;
613 
614  if (not discontinuityOffset.has_value())
615  {
616  errors.push_back(createErrorMessageWithContext(
617  "Invalid value for discontinuityOffset in node " + nodeName,
618  configNode));
619  any_error = true;
620  }
621 
622  std::optional<float> maxValue = evaluate(node.attribute_value("maxValue"));
623 
624  if (not maxValue.has_value())
625  {
626  errors.push_back(createErrorMessageWithContext(
627  "Invalid value for maxValue in node " + nodeName, configNode));
628  any_error = true;
629  }
630 
631  if (any_error)
632  {
633  continue;
634  }
635 
636  config.setModularConvertedValueConfig(
637  name,
638  types::ModularConvertedValueConfig(
639  node.attribute_as_float("zeroOffset"),
640  discontinuityOffset.value(),
641  maxValue.value(),
642  node.attribute_as_bool("isInverted", "true", "false")),
643  tag);
644  }
645  else
646  {
647  errors.push_back(createErrorMessageWithContext(
648  "Not all required fields (zeroOffset, "
649  "discontinuityOffset, maxValue, isInverted) set for " +
650  nodeName,
651  configNode));
652  }
653  }
654  else if (nodeName == "String")
655  {
656  config.setString(name, node.value(), tag);
657  }
658  else
659  {
660  errors.push_back(createErrorMessageWithContext(
661  "Unknown node " + nodeName +
662  " in config node. Config node names are camel-cased.",
663  configNode));
664  }
665  }
666  catch (const ConfigInsertError& e)
667  {
668  errors.push_back(e.what());
669  }
670  }
671  }
672 
673  void
674  ConfigParser::parseController(ControllerConfig& config,
675  RapidXmlReaderNode& controllerNode,
676  ConfigTag tag)
677  {
678  if (!controllerNode.has_attribute("type"))
679  {
680  errors.push_back(createErrorMessageWithContext(
681  "Controller node does not have a type attribute", controllerNode));
682  return;
683  }
684 
685  std::string controllerType = controllerNode.attribute_value("type");
686  auto it = config.controllerConfigs.find(controllerType);
687  if (it != config.controllerConfigs.end())
688  {
689  parseConfigNode(*it->second, controllerNode, tag);
690  }
691  else
692  {
693  std::shared_ptr<Config> controllerConfig = std::make_shared<Config>();
694  parseConfigNode(*controllerConfig, controllerNode, tag);
695  config.addControllerConfig(controllerType, controllerConfig);
696  }
697  }
698 
699  void
700  ConfigParser::parseProfiles(RapidXmlReaderNode& profilesNode)
701  {
702  for (auto& node : profilesNode.nodes())
703  {
704  std::string nodeName = node.name();
705  if (nodeName == "Slave")
706  {
707  std::shared_ptr<SlaveProfile> slaveConf;
708  bool valid = parseSlaveProfile(slaveConf, node);
709  if (valid)
710  {
711  // Create type map if it does not alredy exist
712  if (slaveProfiles.find(slaveConf->getType()) == slaveProfiles.end())
713  {
714  slaveProfiles[slaveConf->getType()] = {};
715  }
716 
717  auto& profilesWithType = slaveProfiles[slaveConf->getType()];
718  if (profilesWithType.find(slaveConf->getName()) != profilesWithType.end())
719  {
720  errors.push_back("Slave Profile with name " + slaveConf->getName() +
721  " is defined multiple times for type " +
722  slaveConf->getType());
723  }
724  profilesWithType[slaveConf->getName()] = slaveConf;
725  }
726  }
727  else if (nodeName == "Device")
728  {
729  std::shared_ptr<DeviceConfigBase> deviceConf;
730  bool valid = parseDeviceProfile(deviceConf, node);
731  if (valid)
732  {
733  // Create type map if it does not alredy exist
734  if (deviceProfiles.find(deviceConf->getType()) == deviceProfiles.end())
735  {
736  deviceProfiles[deviceConf->getType()] = {};
737  }
738 
739  auto& profilesWithType = deviceProfiles[deviceConf->getType()];
740  std::string name = deviceConf->getName();
741  if (profilesWithType.find(name) != profilesWithType.end())
742  {
743  errors.push_back("Device Profile with name " + name +
744  " is defined multiple times for type " +
745  deviceConf->getType());
746  }
747  profilesWithType[name] = deviceConf;
748  }
749  }
750  else
751  {
752  errors.push_back(createErrorMessageWithContext(
753  "Illegal " + nodeName + " node in Profiles node", profilesNode));
754  }
755  }
756  }
757 
758  void
759  ConfigParser::parseSlaveIdentifier(SlaveIdentifierConfig& identifier,
760  RapidXmlReaderNode& identifierNode,
761  std::optional<std::string> name,
762  ConfigTag tag,
763  bool checkIdentifierValid)
764  {
765  if (name.has_value())
766  {
767  identifier.setName(name.value(), tag);
768  }
769 
770  auto vIds = identifierNode.nodes("VendorID");
771  if (vIds.size() > 1)
772  {
773  errors.push_back(createErrorMessageWithContext(
774  "Identifier of slave / profile has defined VendorID multiple times.",
775  identifierNode));
776  }
777  else if (vIds.size() == 1)
778  {
779  identifier.setVendorID(vIds.at(0).value_as_uint32(), tag);
780  }
781 
782  auto pCodes = identifierNode.nodes("ProductID");
783  if (pCodes.size() > 1)
784  {
785  errors.push_back(createErrorMessageWithContext(
786  "Identifier of slave / profile has defined ProductID multiple times.",
787  identifierNode));
788  }
789  else if (pCodes.size() == 1)
790  {
791  identifier.setProductCode(pCodes.at(0).value_as_uint32(), tag);
792  }
793 
794  auto serNums = identifierNode.nodes("Serial");
795  if (serNums.size() > 1)
796  {
797  errors.push_back(createErrorMessageWithContext(
798  "Identifier of slave / profile has defined Serial multiple times.",
799  identifierNode));
800  }
801  else if (serNums.size() == 1)
802  {
803  identifier.setSerialNumber(serNums.at(0).value_as_uint32(), tag);
804  }
805 
806  for (auto& node : identifierNode.nodes())
807  {
808  std::string nodeName = node.name();
809  if (nodeName != "VendorID" && nodeName != "ProductID" && nodeName != "Serial")
810  {
811  errors.push_back(createErrorMessageWithContext(
812  "Identifier node of slave / profile contains illegal " + nodeName + " node",
813  identifierNode));
814  }
815  }
816 
817  if (checkIdentifierValid)
818  {
819  if (!identifier.isValid(errors))
820  {
821  errors.push_back(createErrorMessageWithContext(
822  "Identifier of slave / profile should be complete but is not", identifierNode));
823  }
824 
825  testSlaveIdentifierUnique(identifier);
826  }
827  }
828 
829  template <typename T>
830  bool
831  ConfigParser::findProfile(std::shared_ptr<T>& ref,
832  std::string type,
833  std::string profileName,
834  std::string debugName,
835  std::map<std::string, std::map<std::string, std::shared_ptr<T>>>& map)
836  {
837  if (map.find(type) == map.end())
838  {
839  errors.push_back(debugName + " uses profile \"" + profileName + "\" and has type " +
840  type + " but there are no profiles defined for this type");
841  return false;
842  }
843  else
844  {
845  auto& profilesWithType = map[type];
846  if (profilesWithType.find(profileName) == profilesWithType.end())
847  {
848  errors.push_back(debugName + " uses profile \"" + profileName +
849  "\" that is not defined for its type (type: " + type + ")");
850  return false;
851  }
852  else
853  {
854  ref = profilesWithType[profileName];
855  return true;
856  }
857  }
858  }
859 
860  template <typename T>
861  std::list<T>
862  ConfigParser::parseList(std::string str, std::function<T(std::string)> func)
863  {
864  std::list<T> list;
865  for (auto& s : armarx::Split(str, "\n \t\r", true, true))
866  {
867  try
868  {
869  list.push_back(func(s));
870  }
871  catch (...)
872  {
873  std::stringstream ss;
874  ss << " A list has illegal value: Raw value: '" << s
875  << "', entire content string: '" << str << "'";
876  throw ConfigInsertError(ss.str());
877  }
878  }
879  return list;
880  }
881 
882  template <typename T>
884  ConfigParser::parseMatrix(RapidXmlReaderNode& node, std::function<T(std::string)> func)
885  {
886  if (!node.has_attribute("rows"))
887  {
888  throw ConfigInsertError("Matrix node missing \"rows\" attribute");
889  }
890  if (!node.has_attribute("columns"))
891  {
892  throw ConfigInsertError("Matrix node missing \"columns\" attribute");
893  }
894 
895  std::uint32_t rows = node.attribute_as_uint("rows");
896  std::uint32_t columns = node.attribute_as_uint("columns");
898  unsigned int i = 0;
899 
900  for (auto& s : armarx::Split(node.value(), "\t \n\r", true, true))
901  {
902  if (i >= rows * columns)
903  {
904  std::stringstream ss;
905  ss << " A matrix with " << rows << " rows and " << columns
906  << " columns has too many entries: Raw value: " << node.value();
907  throw ConfigInsertError(ss.str());
908  }
909 
910  try
911  {
912  mat(i / columns, i % columns) = func(s);
913  i++;
914  }
915  catch (...)
916  {
917  std::string msg = createErrorMessageWithContext(
918  " A matrix has illegal value: Raw value: '" + s + "'", node);
919  throw ConfigInsertError(msg);
920  }
921  }
922 
923  if (i < rows * columns)
924  {
925  std::stringstream ss;
926  ss << " A matrix with " << rows << " rows and " << columns
927  << " columns has not enough entries: Raw value: " << node.value();
928  throw ConfigInsertError(ss.str());
929  }
930 
931  return mat;
932  }
933 
934  bool
935  ConfigParser::strToBool(std::string s)
936  {
937  if (s == "true" || s == "True" || s == "1")
938  {
939  return true;
940  }
941  else if (s == "false" || s == "False" || s == "0")
942  {
943  return false;
944  }
945  else
946  {
947  errors.push_back("Bool has the following illegal value: " + s);
948  return false;
949  }
950  }
951 
952  void
953  ConfigParser::testSlaveIdentifierUnique(SlaveIdentifierConfig& identifier)
954  {
955  for (auto& id : identifiers)
956  {
957  if (id == identifier)
958  {
959  std::stringstream ss;
960  ss << "Slave Identifier: " << identifier << " is not unique in HardwareConfig";
961  errors.push_back(ss.str());
962  return;
963  }
964  }
965  identifiers.push_back(identifier);
966  }
967 } // namespace armarx::control::hardware_config
armarx::control::hardware_config::HardwareConfig::onParsingFinished
void onParsingFinished()
This method is called when the config is completely read form the HardwareConfig file.
Definition: HardwareConfig.cpp:21
ConfigParser.h
str
std::string str(const T &t)
Definition: UserAssistedSegmenterGuiWidgetController.cpp:43
list
list(APPEND SOURCES ${QT_RESOURCES}) set(COMPONENT_LIBS ArmarXGui ArmarXCoreObservers ArmarXCoreEigen3Variants PlotterController $
Definition: CMakeLists.txt:49
armarx::Split
std::vector< std::string > Split(const std::string &source, const std::string &splitBy, bool trimElements=false, bool removeEmptyElements=false)
Definition: StringHelperTemplates.h:36
armarx::RapidXmlReaderNode::attribute_value
std::string attribute_value(const char *attrName) const
Definition: RapidXmlReader.h:230
armarx::control::hardware_config::WithType
@ WithType
Definition: SlaveConfig.h:63
message
message(STATUS "Boost-Library-Dir: " "${Boost_LIBRARY_DIRS}") message(STATUS "Boost-LIBRARIES
Definition: CMakeLists.txt:8
armarx::detail::StreamPrinterTag::tag
@ tag
armarx::MultiNodeRapidXMLReader
Definition: MultiNodeRapidXMLReader.h:36
armarx::control::hardware_config
Definition: Config.cpp:3
armarx::RapidXmlReaderNode::has_attribute
bool has_attribute(const char *attrName) const
Definition: RapidXmlReader.h:243
armarx::control::hardware_config::ConfigParser::getWarnings
std::vector< std::string > & getWarnings()
Get warnings that occured during parsing These are additional messages that the parser generated and ...
Definition: ConfigParser.cpp:88
armarx::control::hardware_config::HardwareConfig::addDeviceConfig
void addDeviceConfig(const std::string name, std::shared_ptr< DeviceConfig > config)
Add a DeviceConfig.
Definition: HardwareConfig.cpp:30
cxxopts::value
std::shared_ptr< Value > value()
Definition: cxxopts.hpp:855
armarx::abs
std::vector< T > abs(const std::vector< T > &v)
Definition: VectorHelpers.h:281
armarx::control::hardware_config::ConfigParser::ConfigParser
ConfigParser(MultiNodeRapidXMLReader nodes)
Definition: ConfigParser.cpp:16
armarx::control::hardware_config::ConfigTag
ConfigTag
The ConfigTag is used when setting a config value.
Definition: Config.h:61
armarx::control::hardware_config::createErrorMessageWithContext
std::string createErrorMessageWithContext(std::string message, const Config &context)
Create an error message that prints part of the current Config object to make it easiier to find the ...
Definition: Config.cpp:325
armarx::RapidXmlReaderNode
Definition: RapidXmlReader.h:69
armarx::control::hardware_config::TagCustom
@ TagCustom
Definition: Config.h:65
armarx::MultiNodeRapidXMLReader::nodes
RapidXmlReaderNodeSeq nodes(const char *name=nullptr) const
Definition: MultiNodeRapidXMLReader.h:60
armarx::RapidXmlReaderNode::toString
std::string toString() const
Definition: RapidXmlReader.h:122
armarx::control::hardware_config::ParserError
The ParserError class represents a generic error that occurs during parsing.
Definition: Errors.h:35
armarx::control::hardware_config::ConfigParser::getHardwareConfig
HardwareConfig & getHardwareConfig()
Get the parsed Config object hierarchy.
Definition: ConfigParser.cpp:94
armarx::control::hardware_config::ConfigParser::parse
void parse()
Parse the input nodes given in constructor.
Definition: ConfigParser.cpp:26
armarx::RapidXmlReaderNode::nodes
std::vector< RapidXmlReaderNode > nodes(const char *name=nullptr) const
Definition: RapidXmlReader.h:181
armarx::control::hardware_config::HardwareConfig
Root of the config structure.
Definition: HardwareConfig.h:14
Eigen::Matrix
Definition: EigenForwardDeclarations.h:27
T
float T
Definition: UnscentedKalmanFilterTest.cpp:38
armarx::control::hardware_config::TagProfile
@ TagProfile
Definition: Config.h:64
armarx::control::hardware_config::WithoutType
@ WithoutType
Definition: SlaveConfig.h:64
armarx::ctrlutil::s
double s(double t, double s0, double v0, double a0, double j)
Definition: CtrlUtil.h:33
identifiers
const std::list< std::string > identifiers
Definition: linux_networkload.cpp:22
armarx::split
std::vector< std::string > split(const std::string &source, const std::string &splitBy, bool trimElements=false, bool removeEmptyElements=false)
Definition: StringHelpers.cpp:38
armarx::control::hardware_config::SlaveType
SlaveType
Definition: SlaveConfig.h:61