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
8 createErrorMessageWithContext(std::string message, RapidXmlReaderNode& context)
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); });
518 config.set<Eigen::Matrix<std::int32_t, Eigen::Dynamic, Eigen::Dynamic>>(
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); });
525 config.set<Eigen::Matrix<std::uint32_t, Eigen::Dynamic, Eigen::Dynamic>>(
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>
883 Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>
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");
897 Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> mat(rows, 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
std::string str(const T &t)
std::string toString() const
std::vector< RapidXmlReaderNode > nodes(const char *name=nullptr) const
bool has_attribute(const char *attrName) const
std::string attribute_value(const char *attrName) const
ConfigParser(MultiNodeRapidXMLReader nodes)
std::vector< std::string > & getWarnings()
Get warnings that occured during parsing These are additional messages that the parser generated and ...
void parse()
Parse the input nodes given in constructor.
HardwareConfig & getHardwareConfig()
Get the parsed Config object hierarchy.
The Config class is the base class of all specialized configurations that have a direct key -> value ...
Definition Config.h:94
Config with additional Config objects for controllers.
Definition Config.h:354
The ParserError class represents a generic error that occurs during parsing.
Definition Errors.h:36
Data structure holding the information necessary to create a SlaveIdentifier.
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
ConfigTag
The ConfigTag is used when setting a config value.
Definition Config.h:62
double s(double t, double s0, double v0, double a0, double j)
Definition CtrlUtil.h:33
std::vector< std::string > Split(const std::string &source, const std::string &splitBy, bool trimElements=false, bool removeEmptyElements=false)
std::shared_ptr< Value > value()
Definition cxxopts.hpp:855