9 #include <libxml++/libxml++.h>
16 std::string LogicalNameMapParser::getValueFromXmlSubnode<std::string>(
const xmlpp::Node* node,
18 bool hasDefault, std::string defaultValue) {
19 auto list = node->find(subnodeName);
20 if(list.empty() && hasDefault)
return defaultValue;
21 if(list.size() != 1) {
23 "Expected exactly one subnode of the type '" + subnodeName +
"' below node '" + node->get_name() +
"'.");
25 auto childList = list[0]->get_children();
28 for(
auto& child : childList) {
30 parsingError(child,
"Got nullptr from parser library.");
34 const auto* cdataNode =
dynamic_cast<const xmlpp::CdataNode*
>(child);
36 value += cdataNode->get_content();
41 const auto* textNode =
dynamic_cast<const xmlpp::TextNode*
>(child);
44 value += textNode->get_content();
49 if(child->get_name() ==
"ref") {
50 auto childChildList = child->get_children();
51 const auto* refNameNode =
dynamic_cast<const xmlpp::TextNode*
>(childChildList.front());
52 if(refNameNode && childChildList.size() == 1) {
53 std::string regName = refNameNode->get_content();
55 parsingError(child,
"Reference to constant '" + regName +
"' could not be resolved.");
61 if(reg.targetType == LNMBackendRegisterInfo::TargetType::CONSTANT) {
62 if(!reg.plugins.empty()) {
63 parsingError(childList.front(),
"'" + regName +
"' uses plugins which is not supported for <ref>");
66 auto& lnmVariable = _variables[reg.name];
68 std::stringstream buf;
69 buf << boost::fusion::at_key<decltype(arg)>(lnmVariable.valueTable.table).latestValue[0];
74 parsingError(child,
"Reference to '" + regName +
"' does not refer to a constant.");
77 parsingError(child,
"The <ref> node must contain only text.");
82 if(child->get_name() ==
"par") {
83 auto childChildList = child->get_children();
84 const auto* parNameNode =
dynamic_cast<const xmlpp::TextNode*
>(childChildList.front());
85 if(parNameNode && childChildList.size() == 1) {
86 std::string parName = parNameNode->get_content();
87 if(_parameters.find(parName) == _parameters.end()) {
88 parsingError(child,
"Parameter '" + parName +
"' could not be resolved.");
91 value += _parameters[parName];
94 parsingError(child,
"The <par> node must contain only text.");
99 "Node '" + subnodeName +
100 "' should contain only text, CDATA sections, references or parameters. Instead child '" +
101 child->get_name() +
"' was found.");
109 static T stringToValue(
const std::string& valAsString) {
110 if constexpr(std::is_integral_v<T>) {
115 int64_t longVal = std::stoll(valAsString,
nullptr, 0);
116 if(std::is_unsigned_v<T>) {
118 throw std::out_of_range(std::string(
"negative!"));
122 if(longVal < (int64_t)std::numeric_limits<T>::min()) {
123 throw std::out_of_range(std::string(
"too small!"));
127 if(!std::is_same_v<T, uint64_t> && longVal > (int64_t)std::numeric_limits<T>::max()) {
128 throw std::out_of_range(std::string(
"too large!"));
132 catch(std::exception& e) {
134 "LogicalNameMapParser: string '" + valAsString +
"' cannot be converted to integer (" + e.what() +
")";
141 std::stringstream stream;
142 stream << valAsString;
155 getValueFromXmlSubnode<std::string>(node, subnodeName, catalogue, hasDefault,
std::to_string(defaultValue));
157 return stringToValue<T>(valAsString);
165 auto list = node->find(subnodeName);
168 "Expected at least one subnode of the type '" + subnodeName +
"' below node '" + node->get_name() +
"'.");
171 std::vector<T> valueVector;
173 for(
auto& child : list) {
174 const auto* childElement =
dynamic_cast<const xmlpp::Element*
>(child);
175 if(!childElement)
continue;
178 auto* indexAttr = childElement->get_attribute(
"index");
181 index = std::stoi(indexAttr->get_value());
183 if(index >= valueVector.size()) valueVector.resize(index + 1);
186 auto childList = childElement->get_children();
187 if(childList.size() != 1) {
189 "Node '" + subnodeName +
190 "' should contain only text or exactly one reference, "
191 "instead multiple childs "
196 const auto* textNode =
dynamic_cast<const xmlpp::TextNode*
>(childList.front());
198 std::string valAsString = textNode->get_content();
199 valueVector[index] = stringToValue<T>(valAsString);
204 if(childList.front()->get_name() ==
"ref") {
205 auto childChildList = childList.front()->get_children();
206 const auto* refNameNode =
dynamic_cast<const xmlpp::TextNode*
>(childChildList.front());
207 if(refNameNode && childChildList.size() == 1) {
208 std::string regName = refNameNode->get_content();
210 parsingError(childList.front(),
"Reference to constant '" + regName +
"' could not be resolved.");
214 if(reg.targetType == LNMBackendRegisterInfo::TargetType::CONSTANT) {
215 if(!reg.plugins.empty()) {
216 parsingError(childList.front(),
"'" + regName +
"' uses plugins which is not supported for <ref>");
219 std::stringstream buf;
222 buf << boost::fusion::at_key<decltype(arg)>(lnmVariable.valueTable.table).latestValue[0];
226 valueVector[index] = stringToValue<T>(strVal);
229 parsingError(childList.front(),
"Reference to '" + regName +
"' does not refer to a constant.");
232 parsingError(childList.front(),
"The <ref> node must contain only text.");
236 if(childList.front()->get_name() ==
"par") {
237 auto childChildList = childList.front()->get_children();
238 const auto* parNameNode =
dynamic_cast<const xmlpp::TextNode*
>(childChildList.front());
239 if(parNameNode && childChildList.size() == 1) {
240 std::string parName = parNameNode->get_content();
242 parsingError(childList.front(),
"Parameter '" + parName +
"' could not be resolved.");
244 valueVector[index] = stringToValue<T>(
_parameters[parName]);
247 parsingError(childList.front(),
"The <par> node must contain only text.");
252 "Node '" + subnodeName +
"' should contain only text or exactly one reference, instead child '" +
253 childList.front()->get_name() +
"' was found.");
267 xmlpp::DomParser parser;
269 parser.parse_file(fileName);
271 catch(xmlpp::exception& e) {
276 auto*
const root = parser.get_document()->get_root_node();
277 if(root->get_name() !=
"logicalNameMap") {
278 parsingError(root,
"Expected 'logicalNameMap' tag instead of: " + root->get_name());
282 for(
const auto& child : root->get_children()) {
284 const auto* element =
dynamic_cast<const xmlpp::Element*
>(child);
285 if(!element)
continue;
297 if(element->get_name() ==
"module") {
299 auto* nameAttr = element->get_attribute(
"name");
301 parsingError(element,
"Missing name attribute of 'module' tag.");
303 std::string moduleName = nameAttr->get_value();
306 for(
const auto& child : element->get_children()) {
308 const auto* childElement =
dynamic_cast<const xmlpp::Element*
>(child);
309 if(!childElement)
continue;
312 parseElement(currentPath / moduleName, childElement, catalogue);
319 std::string type = element->get_name();
322 auto* nameAttr = element->get_attribute(
"name");
324 parsingError(element,
"Missing name attribute of '" + type +
"' tag.");
326 RegisterPath registerName = currentPath / std::string(nameAttr->get_value());
330 info.
name = registerName;
331 if(type ==
"redirectedRegister") {
332 info.
targetType = LNMBackendRegisterInfo::TargetType::REGISTER;
333 info.
deviceName = getValueFromXmlSubnode<std::string>(element,
"targetDevice", catalogue);
334 info.
registerName = getValueFromXmlSubnode<std::string>(element,
"targetRegister", catalogue);
335 info.
firstIndex = getValueFromXmlSubnode<unsigned int>(element,
"targetStartIndex", catalogue,
true, 0);
336 info.
length = getValueFromXmlSubnode<unsigned int>(element,
"numberOfElements", catalogue,
true, 0);
339 else if(type ==
"redirectedChannel") {
340 info.
targetType = LNMBackendRegisterInfo::TargetType::CHANNEL;
341 info.
deviceName = getValueFromXmlSubnode<std::string>(element,
"targetDevice", catalogue);
342 info.
registerName = getValueFromXmlSubnode<std::string>(element,
"targetRegister", catalogue);
343 info.
channel = getValueFromXmlSubnode<unsigned int>(element,
"targetChannel", catalogue);
344 info.
firstIndex = getValueFromXmlSubnode<unsigned int>(element,
"targetStartIndex", catalogue,
true, 0);
345 info.
length = getValueFromXmlSubnode<unsigned int>(element,
"numberOfElements", catalogue,
true, 0);
348 else if(type ==
"redirectedBit") {
349 info.
targetType = LNMBackendRegisterInfo::TargetType::BIT;
350 info.
deviceName = getValueFromXmlSubnode<std::string>(element,
"targetDevice", catalogue);
351 info.
registerName = getValueFromXmlSubnode<std::string>(element,
"targetRegister", catalogue);
352 info.
bit = getValueFromXmlSubnode<unsigned int>(element,
"targetBit", catalogue);
357 else if(type ==
"constant") {
358 std::string constantType = getValueFromXmlSubnode<std::string>(element,
"type", catalogue);
359 if(constantType ==
"integer") constantType =
"int32";
360 info.
targetType = LNMBackendRegisterInfo::TargetType::CONSTANT;
364 boost::fusion::at_key<decltype(arg)>(lnmVariable.valueTable.table).latestValue =
365 this->getValueVectorFromXmlSubnode<decltype(arg)>(element,
"value", catalogue);
367 lnmVariable.isConstant =
true;
370 info.
length = getValueFromXmlSubnode<unsigned int>(element,
"numberOfElements", catalogue,
true, 1);
376 else if(type ==
"variable") {
377 std::string constantType = getValueFromXmlSubnode<std::string>(element,
"type", catalogue);
378 if(constantType ==
"integer") constantType =
"int32";
379 info.
targetType = LNMBackendRegisterInfo::TargetType::VARIABLE;
383 boost::fusion::at_key<decltype(arg)>(lnmVariable.valueTable.table).latestValue =
384 this->getValueVectorFromXmlSubnode<decltype(arg)>(element,
"value", catalogue);
386 lnmVariable.isConstant =
false;
389 info.
length = getValueFromXmlSubnode<unsigned int>(element,
"numberOfElements", catalogue,
true, 1);
397 parsingError(element,
"Wrong logical register type: " + type);
401 for(
const auto& child : element->get_children()) {
403 const auto* childElement =
dynamic_cast<const xmlpp::Element*
>(child);
404 if(!childElement)
continue;
405 if(childElement->get_name() !=
"plugin")
continue;
408 auto* pluginNameAttr = childElement->get_attribute(
"name");
409 if(!pluginNameAttr) {
410 parsingError(childElement,
"Missing name attribute of 'plugin' tag.");
412 std::string pluginName = pluginNameAttr->get_value();
415 std::map<std::string, std::string> parameters;
416 for(
const auto& paramchild : childElement->get_children()) {
418 const auto* paramElement =
dynamic_cast<const xmlpp::Element*
>(paramchild);
419 if(!paramElement)
continue;
420 if(paramElement->get_name() !=
"parameter") {
421 parsingError(paramElement,
"Unexpected element '" + paramElement->get_name() +
"' inside plugin tag.");
425 auto* parameterNameAttr = paramElement->get_attribute(
"name");
426 if(!pluginNameAttr) {
427 parsingError(paramElement,
"Missing name attribute of 'parameter' tag.");
429 std::string parameterName = parameterNameAttr->get_value();
432 parameters[parameterName] =
433 getValueFromXmlSubnode<std::string>(childElement,
"parameter[@name='" + parameterName +
"']", catalogue);