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