3 #include <SimoxUtility/algorithm/string/string_tools.h>
11 ss <<
message << std::endl <<
"Context:" << std::endl;
22 this->hardwareConfigNode.addNode(hardwareConfigNode);
33 for (
auto& node : hardwareConfigNode.
nodes())
35 std::string nodeName = node.name();
36 if (nodeName ==
"Profiles")
43 for (
auto& node : hardwareConfigNode.
nodes())
45 std::string nodeName = node.name();
46 if (nodeName ==
"Device")
48 std::shared_ptr<DeviceConfig> deviceConfig;
49 bool deviceValid = parseDevice(deviceConfig, node);
55 else if (nodeName ==
"Profiles")
60 else if (nodeName ==
"include")
67 errors.push_back(
"Unexpected node " + nodeName +
68 " at top level of HardwareConfig xml");
76 if (errors.size() > 0)
79 for (
auto& error : errors)
81 ss << error << std::endl;
87 std::vector<std::string>&
100 ConfigParser::parseDevice(std::shared_ptr<DeviceConfig>& deviceConfig,
106 "Device node is missing attribute \"type\"", deviceNode));
112 "Device node is missing attribute \"name\"", deviceNode));
120 std::shared_ptr<DeviceConfigBase> profile;
122 findProfile<DeviceConfigBase>(
123 profile, type, deviceNode.
attribute_value(
"profile"),
"Device", deviceProfiles))
125 deviceConfig = std::make_shared<DeviceConfig>(type, name, profile);
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");
134 auto slaveNodes = deviceNode.
nodes(
"Slave");
135 if (slaveNodes.size() < 1)
138 errors.push_back(
"Device with type " + deviceConfig->getType() +
" and name " +
139 deviceConfig->getName() +
" does not have any Slaves");
143 bool needsType = slaveNodes.size() != 1;
144 for (
auto& node : slaveNodes)
146 std::shared_ptr<SlaveConfig> slaveConfig;
147 bool slaveValid = parseSlave(
151 deviceConfig->addSlaveConfig(slaveConfig);
157 for (
auto& node : deviceNode.
nodes())
159 std::string nodeName = node.name();
160 if (nodeName ==
"Slave")
166 if (nodeName ==
"Subdevice")
168 std::shared_ptr<DeviceConfigBase> subDeviceConfig;
169 bool subDeviceValid = parseSubDevice(subDeviceConfig, node);
172 deviceConfig->addSubDeviceConfig(subDeviceConfig->getName(), subDeviceConfig);
175 else if (nodeName ==
"Controller")
179 else if (nodeName ==
"Config")
185 errors.push_back(
"Device with type " + deviceConfig->getType() +
" and name " +
186 deviceConfig->getName() +
" contains illegal " + nodeName +
194 ConfigParser::parseDeviceProfile(std::shared_ptr<DeviceConfigBase>& deviceProfileConfig,
195 RapidXmlReaderNode& deviceNode)
197 if (!deviceNode.has_attribute(
"type"))
200 "Device profile node is missing attribute \"type\"", deviceNode));
203 if (!deviceNode.has_attribute(
"profilename"))
206 "Device profile node is missing attribute \"profilename\"", deviceNode));
210 std::string name = deviceNode.attribute_value(
"profilename");
211 deviceProfileConfig =
212 std::make_shared<DeviceConfigBase>(deviceNode.attribute_value(
"type"), name);
215 for (
auto& node : deviceNode.nodes())
217 std::string nodeName = node.name();
218 if (nodeName ==
"Controller")
222 else if (nodeName ==
"Config")
228 errors.push_back(
"Device Profile with type " + deviceProfileConfig->getType() +
229 " and profilename " + deviceProfileConfig->getName() +
230 " contains illegal " + nodeName +
" node");
237 ConfigParser::parseSubDevice(std::shared_ptr<DeviceConfigBase>& subDeviceConfig,
238 RapidXmlReaderNode& subDeviceNode)
240 if (!subDeviceNode.has_attribute(
"name"))
243 "Subdevice node does not have \"name\" attribute", subDeviceNode));
246 if (!subDeviceNode.has_attribute(
"type"))
249 "Subdevice node does not have \"type\" attribute", subDeviceNode));
252 std::string name = subDeviceNode.attribute_value(
"name");
253 std::string type = subDeviceNode.attribute_value(
"type");
255 std::shared_ptr<DeviceConfigBase> profile;
256 if (subDeviceNode.has_attribute(
"profile") &&
257 findProfile<DeviceConfigBase>(profile,
259 subDeviceNode.attribute_value(
"profile"),
263 subDeviceConfig = std::make_shared<DeviceConfigBase>(type, name, profile);
267 subDeviceConfig = std::make_shared<DeviceConfigBase>(type, name);
271 for (
auto& node : subDeviceNode.nodes())
273 std::string nodeName = node.name();
274 if (nodeName ==
"Controller")
278 else if (nodeName ==
"Config")
284 errors.push_back(
"SubDevice with type " + subDeviceConfig->getType() +
285 " and name " + subDeviceConfig->getName() +
" contains illegal " +
293 ConfigParser::parseSlave(std::shared_ptr<SlaveConfig>& slaveConfig,
294 RapidXmlReaderNode& slaveNode,
297 std::optional<std::string> type;
298 if (!slaveNode.has_attribute(
"type"))
303 "Slave is missing a \"type\" attribute", slaveNode));
309 type = std::make_optional(slaveNode.attribute_value(
"type"));
312 std::optional<std::string> name;
313 if (slaveNode.has_attribute(
"name"))
315 name = std::make_optional(slaveNode.attribute_value(
"name"));
319 auto identifierNodes = slaveNode.nodes(
"Identifier");
320 if (identifierNodes.size() != 1)
323 "Slave does not have exactly one identifier node", slaveNode));
328 SlaveIdentifierConfig slaveIdentifier;
331 std::shared_ptr<SlaveProfile> profile;
332 if (slaveNode.has_attribute(
"profile") &&
333 findProfile<SlaveProfile>(profile,
335 slaveNode.attribute_value(
"profile"),
339 slaveIdentifier = profile->getIdentifier();
340 parseSlaveIdentifier(
342 slaveConfig = std::make_shared<SlaveConfig>(slaveIdentifier, type, name, profile);
346 parseSlaveIdentifier(
348 slaveConfig = std::make_shared<SlaveConfig>(slaveIdentifier, type, name);
349 if (type.has_value() || name.has_value())
352 "Slave with type " + type.value_or(
"<no type>") +
" and name " +
353 name.value_or(
"<no name>") +
" does not have a profile",
359 for (
auto& node : slaveNode.nodes())
361 std::string nodeName = node.name();
363 if (nodeName ==
"Identifier")
369 if (nodeName ==
"Config")
375 std::stringstream ss;
376 ss <<
"Slave with identifier (" << slaveIdentifier <<
") has illegal " << nodeName
386 ConfigParser::parseSlaveProfile(std::shared_ptr<SlaveProfile>& slaveConfig,
387 RapidXmlReaderNode& slaveNode)
389 if (!slaveNode.has_attribute(
"type"))
392 "Slave Profile is missing a \"type\" attribute", slaveNode));
395 std::string type = slaveNode.attribute_value(
"type");
397 if (!slaveNode.has_attribute(
"profilename"))
400 "Slave Profile is missing a \"profilename\" attribute", slaveNode));
403 std::string name = slaveNode.attribute_value(
"profilename");
406 auto identifierNodes = slaveNode.nodes(
"Identifier");
407 if (identifierNodes.size() > 1)
410 "Slave Profile does have too many identifiers", slaveNode));
414 SlaveIdentifierConfig slaveIdentifier;
415 if (identifierNodes.size() == 1)
419 parseSlaveIdentifier(
423 slaveConfig = std::make_shared<SlaveProfile>(slaveIdentifier, type, name);
426 for (
auto& node : slaveNode.nodes())
428 std::string nodeName = node.name();
430 if (nodeName ==
"Identifier")
436 if (nodeName ==
"Config")
443 "Slave Profile has illegal " + nodeName +
" node", slaveNode));
451 ConfigParser::parseConfigNode(Config& config, RapidXmlReaderNode& configNode,
ConfigTag tag)
453 for (
auto& node : configNode.nodes())
455 std::string nodeName = node.name();
457 if (!node.has_attribute(
"name"))
460 nodeName +
" node is missing a \"name\" attribute", configNode));
463 std::string name = node.attribute_value(
"name");
467 if (nodeName ==
"Bool")
469 std::string
value = node.value();
470 config.setBool(name, strToBool(
value), tag);
472 else if (nodeName ==
"Float")
474 config.setFloat(name, node.value_as_float(), tag);
476 else if (nodeName ==
"Int")
478 config.setInt(name, node.value_as_int32(), tag);
480 else if (nodeName ==
"UInt")
482 config.setUint(name, node.value_as_uint32(), tag);
484 else if (nodeName ==
"BoolList")
486 std::list<bool>
list = parseList<bool>(
487 node.value(), [
this](std::string
s) { return strToBool(s); });
488 config.setBoolList(name,
list, tag);
490 else if (nodeName ==
"FloatList")
492 std::list<float>
list =
493 parseList<float>(node.value(), [](std::string
s) { return std::stof(s); });
494 config.setFloatList(name,
list, tag);
496 else if (nodeName ==
"IntList")
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);
502 else if (nodeName ==
"UIntList")
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);
508 else if (nodeName ==
"FloatMatrix")
510 auto mat = parseMatrix<float>(node, [](std::string
s) {
return std::stof(
s); });
511 config.set<Eigen ::Matrix<float, Eigen::Dynamic, Eigen::Dynamic>>(
514 else if (nodeName ==
"IntMatrix")
517 parseMatrix<std::int32_t>(node, [](std::string
s) {
return std::stol(
s); });
521 else if (nodeName ==
"UIntMatrix")
523 auto mat = parseMatrix<std::uint32_t>(
524 node, [](std::string
s) {
return std::stoul(
s); });
528 else if (nodeName ==
"LinearConvertedValue")
530 if (node.has_attribute(
"factor") && node.has_attribute(
"offset"))
532 config.setLinearConfig(
534 types::LinearConfig(node.attribute_as_float(
"factor"),
535 node.attribute_as_float(
"offset")),
540 types::LinearConfig tmpCfg;
541 if (node.has_attribute(
"factor"))
543 float factor = node.attribute_as_float(
"factor");
544 tmpCfg.setFactorAbsoluteValue(
std::abs(factor));
545 tmpCfg.setFactorIsNegative(factor < 0);
547 if (node.has_attribute(
"factorAbs"))
549 float factor = node.attribute_as_float(
"factorAbs");
553 "factorAbs attribute of LinearConvertedValue has "
557 tmpCfg.setFactorAbsoluteValue(
std::abs(factor));
559 if (node.has_attribute(
"factorSign"))
561 std::string val = node.attribute_value(
"factorSign");
562 if (val ==
"+" || val ==
"positive")
564 tmpCfg.setFactorIsNegative(
false);
566 else if (val ==
"-" || val ==
"negative")
568 tmpCfg.setFactorIsNegative(
true);
573 "factor attribute of LinearConvertedValue has invalid value. "
576 "`, allowed values are `+`, `-`, `positive` and "
581 if (node.has_attribute(
"offset"))
583 tmpCfg.setOffset(node.attribute_as_float(
"offset"));
585 config.setLinearConfig(name, std::move(tmpCfg),
tag);
588 else if (nodeName ==
"ModularConvertedValue")
590 auto evaluate = [](
const std::string&
value) -> std::optional<float>
594 if (parts.size() == 1)
596 return std::stof(
value);
598 else if (parts.size() == 2)
600 return std::pow(std::stof(parts.at(0)), std::stof(parts.at(1)));
606 if (node.has_attribute(
"zeroOffset") and
607 node.has_attribute(
"discontinuityOffset") and
608 node.has_attribute(
"maxValue") and node.has_attribute(
"isInverted"))
610 std::optional<float> discontinuityOffset =
611 evaluate(node.attribute_value(
"discontinuityOffset"));
612 bool any_error =
false;
614 if (not discontinuityOffset.has_value())
617 "Invalid value for discontinuityOffset in node " + nodeName,
622 std::optional<float> maxValue = evaluate(node.attribute_value(
"maxValue"));
624 if (not maxValue.has_value())
627 "Invalid value for maxValue in node " + nodeName, configNode));
636 config.setModularConvertedValueConfig(
638 types::ModularConvertedValueConfig(
639 node.attribute_as_float(
"zeroOffset"),
640 discontinuityOffset.value(),
642 node.attribute_as_bool(
"isInverted",
"true",
"false")),
648 "Not all required fields (zeroOffset, "
649 "discontinuityOffset, maxValue, isInverted) set for " +
654 else if (nodeName ==
"String")
656 config.setString(name, node.value(), tag);
661 "Unknown node " + nodeName +
662 " in config node. Config node names are camel-cased.",
666 catch (
const ConfigInsertError& e)
668 errors.push_back(e.what());
674 ConfigParser::parseController(ControllerConfig& config,
675 RapidXmlReaderNode& controllerNode,
678 if (!controllerNode.has_attribute(
"type"))
681 "Controller node does not have a type attribute", controllerNode));
685 std::string controllerType = controllerNode.attribute_value(
"type");
686 auto it = config.controllerConfigs.find(controllerType);
687 if (it != config.controllerConfigs.end())
689 parseConfigNode(*it->second, controllerNode, tag);
693 std::shared_ptr<Config> controllerConfig = std::make_shared<Config>();
694 parseConfigNode(*controllerConfig, controllerNode, tag);
695 config.addControllerConfig(controllerType, controllerConfig);
700 ConfigParser::parseProfiles(RapidXmlReaderNode& profilesNode)
702 for (
auto& node : profilesNode.nodes())
704 std::string nodeName = node.name();
705 if (nodeName ==
"Slave")
707 std::shared_ptr<SlaveProfile> slaveConf;
708 bool valid = parseSlaveProfile(slaveConf, node);
712 if (slaveProfiles.find(slaveConf->getType()) == slaveProfiles.end())
714 slaveProfiles[slaveConf->getType()] = {};
717 auto& profilesWithType = slaveProfiles[slaveConf->getType()];
718 if (profilesWithType.find(slaveConf->getName()) != profilesWithType.end())
720 errors.push_back(
"Slave Profile with name " + slaveConf->getName() +
721 " is defined multiple times for type " +
722 slaveConf->getType());
724 profilesWithType[slaveConf->getName()] = slaveConf;
727 else if (nodeName ==
"Device")
729 std::shared_ptr<DeviceConfigBase> deviceConf;
730 bool valid = parseDeviceProfile(deviceConf, node);
734 if (deviceProfiles.find(deviceConf->getType()) == deviceProfiles.end())
736 deviceProfiles[deviceConf->getType()] = {};
739 auto& profilesWithType = deviceProfiles[deviceConf->getType()];
740 std::string name = deviceConf->getName();
741 if (profilesWithType.find(name) != profilesWithType.end())
743 errors.push_back(
"Device Profile with name " + name +
744 " is defined multiple times for type " +
745 deviceConf->getType());
747 profilesWithType[name] = deviceConf;
753 "Illegal " + nodeName +
" node in Profiles node", profilesNode));
759 ConfigParser::parseSlaveIdentifier(SlaveIdentifierConfig& identifier,
760 RapidXmlReaderNode& identifierNode,
761 std::optional<std::string> name,
763 bool checkIdentifierValid)
765 if (name.has_value())
767 identifier.setName(name.value(), tag);
770 auto vIds = identifierNode.nodes(
"VendorID");
774 "Identifier of slave / profile has defined VendorID multiple times.",
777 else if (vIds.size() == 1)
779 identifier.setVendorID(vIds.at(0).value_as_uint32(), tag);
782 auto pCodes = identifierNode.nodes(
"ProductID");
783 if (pCodes.size() > 1)
786 "Identifier of slave / profile has defined ProductID multiple times.",
789 else if (pCodes.size() == 1)
791 identifier.setProductCode(pCodes.at(0).value_as_uint32(), tag);
794 auto serNums = identifierNode.nodes(
"Serial");
795 if (serNums.size() > 1)
798 "Identifier of slave / profile has defined Serial multiple times.",
801 else if (serNums.size() == 1)
803 identifier.setSerialNumber(serNums.at(0).value_as_uint32(), tag);
806 for (
auto& node : identifierNode.nodes())
808 std::string nodeName = node.name();
809 if (nodeName !=
"VendorID" && nodeName !=
"ProductID" && nodeName !=
"Serial")
812 "Identifier node of slave / profile contains illegal " + nodeName +
" node",
817 if (checkIdentifierValid)
819 if (!identifier.isValid(errors))
822 "Identifier of slave / profile should be complete but is not", identifierNode));
825 testSlaveIdentifierUnique(identifier);
829 template <
typename T>
831 ConfigParser::findProfile(std::shared_ptr<T>& ref,
833 std::string profileName,
834 std::string debugName,
835 std::map<std::string, std::map<std::string, std::shared_ptr<T>>>& map)
837 if (map.find(type) == map.end())
839 errors.push_back(debugName +
" uses profile \"" + profileName +
"\" and has type " +
840 type +
" but there are no profiles defined for this type");
845 auto& profilesWithType = map[type];
846 if (profilesWithType.find(profileName) == profilesWithType.end())
848 errors.push_back(debugName +
" uses profile \"" + profileName +
849 "\" that is not defined for its type (type: " + type +
")");
854 ref = profilesWithType[profileName];
860 template <
typename T>
862 ConfigParser::parseList(std::string
str, std::function<
T(std::string)> func)
869 list.push_back(func(
s));
873 std::stringstream ss;
874 ss <<
" A list has illegal value: Raw value: '" <<
s
875 <<
"', entire content string: '" <<
str <<
"'";
876 throw ConfigInsertError(ss.str());
882 template <
typename T>
884 ConfigParser::parseMatrix(RapidXmlReaderNode& node, std::function<
T(std::string)> func)
886 if (!node.has_attribute(
"rows"))
888 throw ConfigInsertError(
"Matrix node missing \"rows\" attribute");
890 if (!node.has_attribute(
"columns"))
892 throw ConfigInsertError(
"Matrix node missing \"columns\" attribute");
895 std::uint32_t rows = node.attribute_as_uint(
"rows");
896 std::uint32_t columns = node.attribute_as_uint(
"columns");
900 for (
auto&
s :
armarx::Split(node.value(),
"\t \n\r",
true,
true))
902 if (i >= rows * columns)
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());
912 mat(i / columns, i % columns) = func(
s);
918 " A matrix has illegal value: Raw value: '" +
s +
"'", node);
919 throw ConfigInsertError(msg);
923 if (i < rows * columns)
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());
935 ConfigParser::strToBool(std::string
s)
937 if (
s ==
"true" ||
s ==
"True" ||
s ==
"1")
941 else if (
s ==
"false" ||
s ==
"False" ||
s ==
"0")
947 errors.push_back(
"Bool has the following illegal value: " +
s);
953 ConfigParser::testSlaveIdentifierUnique(SlaveIdentifierConfig& identifier)
955 for (
auto&
id : identifiers)
957 if (
id == identifier)
959 std::stringstream ss;
960 ss <<
"Slave Identifier: " << identifier <<
" is not unique in HardwareConfig";
961 errors.push_back(ss.str());