3 #include <SimoxUtility/algorithm/string/string_tools.h>
12 ss <<
message << std::endl <<
"Context:" << std::endl;
23 this->hardwareConfigNode.addNode(hardwareConfigNode);
34 for (
auto& node : hardwareConfigNode.
nodes())
36 std::string nodeName = node.name();
37 if (nodeName ==
"Profiles")
44 for (
auto& node : hardwareConfigNode.
nodes())
46 std::string nodeName = node.name();
47 if (nodeName ==
"Device")
49 std::shared_ptr<DeviceConfig> deviceConfig;
50 bool deviceValid = parseDevice(deviceConfig, node);
56 else if (nodeName ==
"Profiles")
61 else if (nodeName ==
"include")
68 errors.push_back(
"Unexpected node " + nodeName +
69 " at top level of HardwareConfig xml");
77 if (errors.size() > 0)
80 for (
auto& error : errors)
82 ss << error << std::endl;
88 std::vector<std::string>&
101 ConfigParser::parseDevice(std::shared_ptr<DeviceConfig>& deviceConfig,
107 "Device node is missing attribute \"type\"", deviceNode));
113 "Device node is missing attribute \"name\"", deviceNode));
121 std::shared_ptr<DeviceConfigBase> profile;
123 findProfile<DeviceConfigBase>(
124 profile, type, deviceNode.
attribute_value(
"profile"),
"Device", deviceProfiles))
126 deviceConfig = std::make_shared<DeviceConfig>(type, name, profile);
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");
135 auto slaveNodes = deviceNode.
nodes(
"Slave");
136 if (slaveNodes.size() < 1)
139 errors.push_back(
"Device with type " + deviceConfig->getType() +
" and name " +
140 deviceConfig->getName() +
" does not have any Slaves");
144 bool needsType = slaveNodes.size() != 1;
145 for (
auto& node : slaveNodes)
147 std::shared_ptr<SlaveConfig> slaveConfig;
148 bool slaveValid = parseSlave(
152 deviceConfig->addSlaveConfig(slaveConfig);
158 for (
auto& node : deviceNode.
nodes())
160 std::string nodeName = node.name();
161 if (nodeName ==
"Slave")
167 if (nodeName ==
"Subdevice")
169 std::shared_ptr<DeviceConfigBase> subDeviceConfig;
170 bool subDeviceValid = parseSubDevice(subDeviceConfig, node);
173 deviceConfig->addSubDeviceConfig(subDeviceConfig->getName(), subDeviceConfig);
176 else if (nodeName ==
"Controller")
180 else if (nodeName ==
"Config")
186 errors.push_back(
"Device with type " + deviceConfig->getType() +
" and name " +
187 deviceConfig->getName() +
" contains illegal " + nodeName +
195 ConfigParser::parseDeviceProfile(std::shared_ptr<DeviceConfigBase>& deviceProfileConfig,
196 RapidXmlReaderNode& deviceNode)
198 if (!deviceNode.has_attribute(
"type"))
201 "Device profile node is missing attribute \"type\"", deviceNode));
204 if (!deviceNode.has_attribute(
"profilename"))
207 "Device profile node is missing attribute \"profilename\"", deviceNode));
211 std::string name = deviceNode.attribute_value(
"profilename");
212 deviceProfileConfig =
213 std::make_shared<DeviceConfigBase>(deviceNode.attribute_value(
"type"), name);
216 for (
auto& node : deviceNode.nodes())
218 std::string nodeName = node.name();
219 if (nodeName ==
"Controller")
223 else if (nodeName ==
"Config")
229 errors.push_back(
"Device Profile with type " + deviceProfileConfig->getType() +
230 " and profilename " + deviceProfileConfig->getName() +
231 " contains illegal " + nodeName +
" node");
238 ConfigParser::parseSubDevice(std::shared_ptr<DeviceConfigBase>& subDeviceConfig,
239 RapidXmlReaderNode& subDeviceNode)
241 if (!subDeviceNode.has_attribute(
"name"))
244 "Subdevice node does not have \"name\" attribute", subDeviceNode));
247 if (!subDeviceNode.has_attribute(
"type"))
250 "Subdevice node does not have \"type\" attribute", subDeviceNode));
253 std::string name = subDeviceNode.attribute_value(
"name");
254 std::string type = subDeviceNode.attribute_value(
"type");
256 std::shared_ptr<DeviceConfigBase> profile;
257 if (subDeviceNode.has_attribute(
"profile") &&
258 findProfile<DeviceConfigBase>(profile,
260 subDeviceNode.attribute_value(
"profile"),
264 subDeviceConfig = std::make_shared<DeviceConfigBase>(type, name, profile);
268 subDeviceConfig = std::make_shared<DeviceConfigBase>(type, name);
272 for (
auto& node : subDeviceNode.nodes())
274 std::string nodeName = node.name();
275 if (nodeName ==
"Controller")
279 else if (nodeName ==
"Config")
285 errors.push_back(
"SubDevice with type " + subDeviceConfig->getType() +
286 " and name " + subDeviceConfig->getName() +
" contains illegal " +
294 ConfigParser::parseSlave(std::shared_ptr<SlaveConfig>& slaveConfig,
295 RapidXmlReaderNode& slaveNode,
298 std::optional<std::string> type;
299 if (!slaveNode.has_attribute(
"type"))
304 "Slave is missing a \"type\" attribute", slaveNode));
310 type = std::make_optional(slaveNode.attribute_value(
"type"));
313 std::optional<std::string> name;
314 if (slaveNode.has_attribute(
"name"))
316 name = std::make_optional(slaveNode.attribute_value(
"name"));
320 auto identifierNodes = slaveNode.nodes(
"Identifier");
321 if (identifierNodes.size() != 1)
324 "Slave does not have exactly one identifier node", slaveNode));
329 SlaveIdentifierConfig slaveIdentifier;
332 std::shared_ptr<SlaveProfile> profile;
333 if (slaveNode.has_attribute(
"profile") &&
334 findProfile<SlaveProfile>(profile,
336 slaveNode.attribute_value(
"profile"),
340 slaveIdentifier = profile->getIdentifier();
341 parseSlaveIdentifier(
343 slaveConfig = std::make_shared<SlaveConfig>(slaveIdentifier, type, name, profile);
347 parseSlaveIdentifier(
349 slaveConfig = std::make_shared<SlaveConfig>(slaveIdentifier, type, name);
350 if (type.has_value() || name.has_value())
353 "Slave with type " + type.value_or(
"<no type>") +
" and name " +
354 name.value_or(
"<no name>") +
" does not have a profile",
360 for (
auto& node : slaveNode.nodes())
362 std::string nodeName = node.name();
364 if (nodeName ==
"Identifier")
370 if (nodeName ==
"Config")
376 std::stringstream ss;
377 ss <<
"Slave with identifier (" << slaveIdentifier <<
") has illegal " << nodeName
387 ConfigParser::parseSlaveProfile(std::shared_ptr<SlaveProfile>& slaveConfig,
388 RapidXmlReaderNode& slaveNode)
390 if (!slaveNode.has_attribute(
"type"))
393 "Slave Profile is missing a \"type\" attribute", slaveNode));
396 std::string type = slaveNode.attribute_value(
"type");
398 if (!slaveNode.has_attribute(
"profilename"))
401 "Slave Profile is missing a \"profilename\" attribute", slaveNode));
404 std::string name = slaveNode.attribute_value(
"profilename");
407 auto identifierNodes = slaveNode.nodes(
"Identifier");
408 if (identifierNodes.size() > 1)
411 "Slave Profile does have too many identifiers", slaveNode));
415 SlaveIdentifierConfig slaveIdentifier;
416 if (identifierNodes.size() == 1)
420 parseSlaveIdentifier(
424 slaveConfig = std::make_shared<SlaveProfile>(slaveIdentifier, type, name);
427 for (
auto& node : slaveNode.nodes())
429 std::string nodeName = node.name();
431 if (nodeName ==
"Identifier")
437 if (nodeName ==
"Config")
444 "Slave Profile has illegal " + nodeName +
" node", slaveNode));
452 ConfigParser::parseConfigNode(Config& config, RapidXmlReaderNode& configNode,
ConfigTag tag)
454 for (
auto& node : configNode.nodes())
456 std::string nodeName = node.name();
458 if (!node.has_attribute(
"name"))
461 nodeName +
" node is missing a \"name\" attribute", configNode));
464 std::string name = node.attribute_value(
"name");
468 if (nodeName ==
"Bool")
470 std::string
value = node.value();
471 config.setBool(name, strToBool(
value), tag);
473 else if (nodeName ==
"Float")
475 config.setFloat(name, node.value_as_float(), tag);
477 else if (nodeName ==
"Int")
479 config.setInt(name, node.value_as_int32(), tag);
481 else if (nodeName ==
"UInt")
483 config.setUint(name, node.value_as_uint32(), tag);
485 else if (nodeName ==
"BoolList")
487 std::list<bool>
list = parseList<bool>(
488 node.value(), [
this](std::string
s) { return strToBool(s); });
489 config.setBoolList(name,
list, tag);
491 else if (nodeName ==
"FloatList")
493 std::list<float>
list =
494 parseList<float>(node.value(), [](std::string
s) { return std::stof(s); });
495 config.setFloatList(name,
list, tag);
497 else if (nodeName ==
"IntList")
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);
503 else if (nodeName ==
"UIntList")
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);
509 else if (nodeName ==
"FloatMatrix")
511 auto mat = parseMatrix<float>(node, [](std::string
s) {
return std::stof(
s); });
512 config.set<Eigen ::Matrix<float, Eigen::Dynamic, Eigen::Dynamic>>(
515 else if (nodeName ==
"IntMatrix")
518 parseMatrix<std::int32_t>(node, [](std::string
s) {
return std::stol(
s); });
522 else if (nodeName ==
"UIntMatrix")
524 auto mat = parseMatrix<std::uint32_t>(
525 node, [](std::string
s) {
return std::stoul(
s); });
529 else if (nodeName ==
"LinearConvertedValue")
531 if (node.has_attribute(
"factor") && node.has_attribute(
"offset"))
533 config.setLinearConfig(
535 types::LinearConfig(node.attribute_as_float(
"factor"),
536 node.attribute_as_float(
"offset")),
541 types::LinearConfig tmpCfg;
542 if (node.has_attribute(
"factor"))
544 float factor = node.attribute_as_float(
"factor");
545 tmpCfg.setFactorAbsoluteValue(
std::abs(factor));
546 tmpCfg.setFactorIsNegative(factor < 0);
548 if (node.has_attribute(
"factorAbs"))
550 float factor = node.attribute_as_float(
"factorAbs");
554 "factorAbs attribute of LinearConvertedValue has "
558 tmpCfg.setFactorAbsoluteValue(
std::abs(factor));
560 if (node.has_attribute(
"factorSign"))
562 std::string val = node.attribute_value(
"factorSign");
563 if (val ==
"+" || val ==
"positive")
565 tmpCfg.setFactorIsNegative(
false);
567 else if (val ==
"-" || val ==
"negative")
569 tmpCfg.setFactorIsNegative(
true);
574 "factor attribute of LinearConvertedValue has invalid value. "
577 "`, allowed values are `+`, `-`, `positive` and "
582 if (node.has_attribute(
"offset"))
584 tmpCfg.setOffset(node.attribute_as_float(
"offset"));
586 config.setLinearConfig(name, std::move(tmpCfg),
tag);
589 else if (nodeName ==
"ModularConvertedValue")
591 auto evaluate = [](
const std::string&
value) -> std::optional<float>
595 if (parts.size() == 1)
597 return std::stof(
value);
599 else if (parts.size() == 2)
601 return std::pow(std::stof(parts.at(0)), std::stof(parts.at(1)));
607 if (node.has_attribute(
"zeroOffset") and
608 node.has_attribute(
"discontinuityOffset") and
609 node.has_attribute(
"maxValue") and node.has_attribute(
"isInverted"))
611 std::optional<float> discontinuityOffset =
612 evaluate(node.attribute_value(
"discontinuityOffset"));
613 bool any_error =
false;
615 if (not discontinuityOffset.has_value())
618 "Invalid value for discontinuityOffset in node " + nodeName,
623 std::optional<float> maxValue = evaluate(node.attribute_value(
"maxValue"));
625 if (not maxValue.has_value())
628 "Invalid value for maxValue in node " + nodeName, configNode));
637 config.setModularConvertedValueConfig(
639 types::ModularConvertedValueConfig(
640 node.attribute_as_float(
"zeroOffset"),
641 discontinuityOffset.value(),
643 node.attribute_as_bool(
"isInverted",
"true",
"false")),
649 "Not all required fields (zeroOffset, "
650 "discontinuityOffset, maxValue, isInverted) set for " +
655 else if (nodeName ==
"String")
657 config.setString(name, node.value(), tag);
662 "Unknown node " + nodeName +
663 " in config node. Config node names are camel-cased.",
667 catch (
const ConfigInsertError& e)
669 errors.push_back(e.what());
675 ConfigParser::parseController(ControllerConfig& config,
676 RapidXmlReaderNode& controllerNode,
679 if (!controllerNode.has_attribute(
"type"))
682 "Controller node does not have a type attribute", controllerNode));
686 std::string controllerType = controllerNode.attribute_value(
"type");
687 auto it = config.controllerConfigs.find(controllerType);
688 if (it != config.controllerConfigs.end())
690 parseConfigNode(*it->second, controllerNode, tag);
694 std::shared_ptr<Config> controllerConfig = std::make_shared<Config>();
695 parseConfigNode(*controllerConfig, controllerNode, tag);
696 config.addControllerConfig(controllerType, controllerConfig);
701 ConfigParser::parseProfiles(RapidXmlReaderNode& profilesNode)
703 for (
auto& node : profilesNode.nodes())
705 std::string nodeName = node.name();
706 if (nodeName ==
"Slave")
708 std::shared_ptr<SlaveProfile> slaveConf;
709 bool valid = parseSlaveProfile(slaveConf, node);
713 if (slaveProfiles.find(slaveConf->getType()) == slaveProfiles.end())
715 slaveProfiles[slaveConf->getType()] = {};
718 auto& profilesWithType = slaveProfiles[slaveConf->getType()];
719 if (profilesWithType.find(slaveConf->getName()) != profilesWithType.end())
721 errors.push_back(
"Slave Profile with name " + slaveConf->getName() +
722 " is defined multiple times for type " +
723 slaveConf->getType());
725 profilesWithType[slaveConf->getName()] = slaveConf;
728 else if (nodeName ==
"Device")
730 std::shared_ptr<DeviceConfigBase> deviceConf;
731 bool valid = parseDeviceProfile(deviceConf, node);
735 if (deviceProfiles.find(deviceConf->getType()) == deviceProfiles.end())
737 deviceProfiles[deviceConf->getType()] = {};
740 auto& profilesWithType = deviceProfiles[deviceConf->getType()];
741 std::string name = deviceConf->getName();
742 if (profilesWithType.find(name) != profilesWithType.end())
744 errors.push_back(
"Device Profile with name " + name +
745 " is defined multiple times for type " +
746 deviceConf->getType());
748 profilesWithType[name] = deviceConf;
754 "Illegal " + nodeName +
" node in Profiles node", profilesNode));
760 ConfigParser::parseSlaveIdentifier(SlaveIdentifierConfig& identifier,
761 RapidXmlReaderNode& identifierNode,
762 std::optional<std::string> name,
764 bool checkIdentifierValid)
766 if (name.has_value())
768 identifier.setName(name.value(), tag);
771 auto vIds = identifierNode.nodes(
"VendorID");
775 "Identifier of slave / profile has defined VendorID multiple times.",
778 else if (vIds.size() == 1)
780 identifier.setVendorID(vIds.at(0).value_as_uint32(), tag);
783 auto pCodes = identifierNode.nodes(
"ProductID");
784 if (pCodes.size() > 1)
787 "Identifier of slave / profile has defined ProductID multiple times.",
790 else if (pCodes.size() == 1)
792 identifier.setProductCode(pCodes.at(0).value_as_uint32(), tag);
795 auto serNums = identifierNode.nodes(
"Serial");
796 if (serNums.size() > 1)
799 "Identifier of slave / profile has defined Serial multiple times.",
802 else if (serNums.size() == 1)
804 identifier.setSerialNumber(serNums.at(0).value_as_uint32(), tag);
807 for (
auto& node : identifierNode.nodes())
809 std::string nodeName = node.name();
810 if (nodeName !=
"VendorID" && nodeName !=
"ProductID" && nodeName !=
"Serial")
813 "Identifier node of slave / profile contains illegal " + nodeName +
" node",
818 if (checkIdentifierValid)
820 if (!identifier.isValid(errors))
823 "Identifier of slave / profile should be complete but is not", identifierNode));
826 testSlaveIdentifierUnique(identifier);
830 template <
typename T>
832 ConfigParser::findProfile(std::shared_ptr<T>& ref,
834 std::string profileName,
835 std::string debugName,
836 std::map<std::string, std::map<std::string, std::shared_ptr<T>>>& map)
838 if (map.find(type) == map.end())
840 errors.push_back(debugName +
" uses profile \"" + profileName +
"\" and has type " +
841 type +
" but there are no profiles defined for this type");
846 auto& profilesWithType = map[type];
847 if (profilesWithType.find(profileName) == profilesWithType.end())
849 errors.push_back(debugName +
" uses profile \"" + profileName +
850 "\" that is not defined for its type (type: " + type +
")");
855 ref = profilesWithType[profileName];
861 template <
typename T>
863 ConfigParser::parseList(std::string
str, std::function<
T(std::string)> func)
870 list.push_back(func(
s));
874 std::stringstream ss;
875 ss <<
" A list has illegal value: Raw value: '" <<
s
876 <<
"', entire content string: '" <<
str <<
"'";
877 throw ConfigInsertError(ss.str());
883 template <
typename T>
885 ConfigParser::parseMatrix(RapidXmlReaderNode& node, std::function<
T(std::string)> func)
887 if (!node.has_attribute(
"rows"))
889 throw ConfigInsertError(
"Matrix node missing \"rows\" attribute");
891 if (!node.has_attribute(
"columns"))
893 throw ConfigInsertError(
"Matrix node missing \"columns\" attribute");
896 std::uint32_t rows = node.attribute_as_uint(
"rows");
897 std::uint32_t columns = node.attribute_as_uint(
"columns");
901 for (
auto&
s :
armarx::Split(node.value(),
"\t \n\r",
true,
true))
903 if (i >= rows * columns)
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());
913 mat(i / columns, i % columns) = func(
s);
919 " A matrix has illegal value: Raw value: '" +
s +
"'", node);
920 throw ConfigInsertError(msg);
924 if (i < rows * columns)
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());
936 ConfigParser::strToBool(std::string
s)
938 if (
s ==
"true" ||
s ==
"True" ||
s ==
"1")
942 else if (
s ==
"false" ||
s ==
"False" ||
s ==
"0")
948 errors.push_back(
"Bool has the following illegal value: " +
s);
954 ConfigParser::testSlaveIdentifierUnique(SlaveIdentifierConfig& identifier)
956 for (
auto&
id : identifiers)
958 if (
id == identifier)
960 std::stringstream ss;
961 ss <<
"Slave Identifier: " << identifier <<
" is not unique in HardwareConfig";
962 errors.push_back(ss.str());