10#include <libxml++/libxml++.h>
17 std::string LogicalNameMapParser::getValueFromXmlSubnode<std::string>(
const xmlpp::Node* node,
19 bool hasDefault, std::string defaultValue) {
20 auto list = node->find(subnodeName);
21 if(list.empty() && hasDefault)
return defaultValue;
22 if(list.size() != 1) {
24 "Expected exactly one subnode of the type '" + subnodeName +
"' below node '" + node->get_name() +
"'.");
26 auto childList = list[0]->get_children();
29 for(
auto& child : childList) {
31 parsingError(child,
"Got nullptr from parser library.");
35 const auto* cdataNode =
dynamic_cast<const xmlpp::CdataNode*
>(child);
37 value += cdataNode->get_content();
42 const auto* textNode =
dynamic_cast<const xmlpp::TextNode*
>(child);
45 value += textNode->get_content();
50 if(child->get_name() ==
"ref") {
51 auto childChildList = child->get_children();
52 const auto* refNameNode =
dynamic_cast<const xmlpp::TextNode*
>(childChildList.front());
53 if(refNameNode && childChildList.size() == 1) {
54 std::string regName = refNameNode->get_content();
56 parsingError(child,
"Reference to constant '" + regName +
"' could not be resolved.");
63 if(!reg.plugins.empty()) {
64 parsingError(childList.front(),
"'" + regName +
"' uses plugins which is not supported for <ref>");
67 auto& lnmVariable = _variables[reg.name];
69 std::stringstream buf;
70 buf << boost::fusion::at_key<decltype(arg)>(lnmVariable.valueTable.table).latestValue[0];
75 parsingError(child,
"Reference to '" + regName +
"' does not refer to a constant.");
78 parsingError(child,
"The <ref> node must contain only text.");
83 if(child->get_name() ==
"par") {
84 auto childChildList = child->get_children();
85 const auto* parNameNode =
dynamic_cast<const xmlpp::TextNode*
>(childChildList.front());
86 if(parNameNode && childChildList.size() == 1) {
87 std::string parName = parNameNode->get_content();
88 if(_parameters.find(parName) == _parameters.end()) {
89 parsingError(child,
"Parameter '" + parName +
"' could not be resolved.");
92 value += _parameters[parName];
95 parsingError(child,
"The <par> node must contain only text.");
100 "Node '" + subnodeName +
101 "' should contain only text, CDATA sections, references or parameters. Instead child '" +
102 child->get_name() +
"' was found.");
110 static T stringToValue(
const std::string& valAsString) {
111 if constexpr(std::is_integral_v<T>) {
116 int64_t longVal = std::stoll(valAsString,
nullptr, 0);
117 if(std::is_unsigned_v<T>) {
119 throw std::out_of_range(std::string(
"negative!"));
123 if(longVal < (int64_t)std::numeric_limits<T>::min()) {
124 throw std::out_of_range(std::string(
"too small!"));
128 if(!std::is_same_v<T, uint64_t> && longVal > (int64_t)std::numeric_limits<T>::max()) {
129 throw std::out_of_range(std::string(
"too large!"));
133 catch(std::exception& e) {
135 "LogicalNameMapParser: string '" + valAsString +
"' cannot be converted to integer (" + e.what() +
")";
142 std::stringstream stream;
143 stream << valAsString;
158 return stringToValue<T>(valAsString);
166 auto list = node->find(subnodeName);
169 "Expected at least one subnode of the type '" + subnodeName +
"' below node '" + node->get_name() +
"'.");
172 std::vector<T> valueVector;
174 for(
auto& child : list) {
175 const auto* childElement =
dynamic_cast<const xmlpp::Element*
>(child);
176 if(!childElement)
continue;
179 auto* indexAttr = childElement->get_attribute(
"index");
182 index = std::stoi(indexAttr->get_value());
184 if(index >= valueVector.size()) valueVector.resize(index + 1);
187 auto childList = childElement->get_children();
188 if(childList.size() != 1) {
190 "Node '" + subnodeName +
191 "' should contain only text or exactly one reference, "
192 "instead multiple childs "
197 const auto* textNode =
dynamic_cast<const xmlpp::TextNode*
>(childList.front());
199 std::string valAsString = textNode->get_content();
200 valueVector[index] = stringToValue<T>(valAsString);
205 if(childList.front()->get_name() ==
"ref") {
206 auto childChildList = childList.front()->get_children();
207 const auto* refNameNode =
dynamic_cast<const xmlpp::TextNode*
>(childChildList.front());
208 if(refNameNode && childChildList.size() == 1) {
209 std::string regName = refNameNode->get_content();
211 parsingError(childList.front(),
"Reference to constant '" + regName +
"' could not be resolved.");
216 if(!reg.plugins.empty()) {
217 parsingError(childList.front(),
"'" + regName +
"' uses plugins which is not supported for <ref>");
220 std::stringstream buf;
223 buf << boost::fusion::at_key<decltype(arg)>(lnmVariable.valueTable.table).latestValue[0];
227 valueVector[index] = stringToValue<T>(strVal);
230 parsingError(childList.front(),
"Reference to '" + regName +
"' does not refer to a constant.");
233 parsingError(childList.front(),
"The <ref> node must contain only text.");
237 if(childList.front()->get_name() ==
"par") {
238 auto childChildList = childList.front()->get_children();
239 const auto* parNameNode =
dynamic_cast<const xmlpp::TextNode*
>(childChildList.front());
240 if(parNameNode && childChildList.size() == 1) {
241 std::string parName = parNameNode->get_content();
243 parsingError(childList.front(),
"Parameter '" + parName +
"' could not be resolved.");
245 valueVector[index] = stringToValue<T>(
_parameters[parName]);
248 parsingError(childList.front(),
"The <par> node must contain only text.");
253 "Node '" + subnodeName +
"' should contain only text or exactly one reference, instead child '" +
254 childList.front()->get_name() +
"' was found.");
268 xmlpp::DomParser parser;
270 parser.parse_file(fileName);
272 catch(xmlpp::exception& e) {
277 auto*
const root = parser.get_document()->get_root_node();
278 if(root->get_name() !=
"logicalNameMap") {
279 parsingError(root,
"Expected 'logicalNameMap' tag instead of: " + root->get_name());
283 for(
const auto& child : root->get_children()) {
285 const auto* element =
dynamic_cast<const xmlpp::Element*
>(child);
286 if(!element)
continue;
298 if(element->get_name() ==
"module") {
300 auto* nameAttr = element->get_attribute(
"name");
302 parsingError(element,
"Missing name attribute of 'module' tag.");
304 std::string moduleName = nameAttr->get_value();
307 for(
const auto& child : element->get_children()) {
309 const auto* childElement =
dynamic_cast<const xmlpp::Element*
>(child);
310 if(!childElement)
continue;
313 parseElement(currentPath / moduleName, childElement, catalogue);
320 std::string type = element->get_name();
323 auto* nameAttr = element->get_attribute(
"name");
325 parsingError(element,
"Missing name attribute of '" + type +
"' tag.");
327 RegisterPath registerName = currentPath / std::string(nameAttr->get_value());
331 info.
name = registerName;
332 if(type ==
"redirectedRegister") {
336 info.
firstIndex = getValueFromXmlSubnode<unsigned int>(element,
"targetStartIndex", catalogue,
true, 0);
337 info.
length = getValueFromXmlSubnode<unsigned int>(element,
"numberOfElements", catalogue,
true, 0);
340 else if(type ==
"redirectedChannel") {
344 info.
channel = getValueFromXmlSubnode<unsigned int>(element,
"targetChannel", catalogue);
345 info.
firstIndex = getValueFromXmlSubnode<unsigned int>(element,
"targetStartIndex", catalogue,
true, 0);
346 info.
length = getValueFromXmlSubnode<unsigned int>(element,
"numberOfElements", catalogue,
true, 0);
349 else if(type ==
"redirectedBit") {
353 info.
bit = getValueFromXmlSubnode<unsigned int>(element,
"targetBit", catalogue);
358 else if(type ==
"constant") {
360 if(constantType ==
"integer") constantType =
"int32";
365 boost::fusion::at_key<decltype(arg)>(lnmVariable.valueTable.table).latestValue =
366 this->getValueVectorFromXmlSubnode<decltype(arg)>(element,
"value", catalogue);
368 lnmVariable.isConstant =
true;
371 info.
length = getValueFromXmlSubnode<unsigned int>(element,
"numberOfElements", catalogue,
true, 1);
377 else if(type ==
"variable") {
379 if(constantType ==
"integer") constantType =
"int32";
384 boost::fusion::at_key<decltype(arg)>(lnmVariable.valueTable.table).latestValue =
385 this->getValueVectorFromXmlSubnode<decltype(arg)>(element,
"value", catalogue);
387 lnmVariable.isConstant =
false;
390 info.
length = getValueFromXmlSubnode<unsigned int>(element,
"numberOfElements", catalogue,
true, 1);
398 parsingError(element,
"Wrong logical register type: " + type);
402 for(
const auto& child : element->get_children()) {
404 const auto* childElement =
dynamic_cast<const xmlpp::Element*
>(child);
405 if(!childElement)
continue;
406 if(childElement->get_name() !=
"plugin")
continue;
409 auto* pluginNameAttr = childElement->get_attribute(
"name");
410 if(!pluginNameAttr) {
411 parsingError(childElement,
"Missing name attribute of 'plugin' tag.");
413 std::string pluginName = pluginNameAttr->get_value();
416 std::map<std::string, std::string> parameters;
417 for(
const auto& paramchild : childElement->get_children()) {
419 const auto* paramElement =
dynamic_cast<const xmlpp::Element*
>(paramchild);
420 if(!paramElement)
continue;
421 if(paramElement->get_name() !=
"parameter") {
422 parsingError(paramElement,
"Unexpected element '" + paramElement->get_name() +
"' inside plugin tag.");
426 auto* parameterNameAttr = paramElement->get_attribute(
"name");
427 if(!pluginNameAttr) {
428 parsingError(paramElement,
"Missing name attribute of 'parameter' tag.");
430 std::string parameterName = parameterNameAttr->get_value();
433 parameters[parameterName] =
Interface for backends to the register catalogue.
void addRegister(const BackendRegisterInfo ®isterInfo)
Add register information to the catalogue.
virtual BackendRegisterInfo getBackendRegister(const RegisterPath ®isterPathName) const
Note: Override this function if backend has "hidden" registers which are not added to the map and hen...
bool hasRegister(const RegisterPath ®isterPathName) const override
Check if register with the given path name exists.
Class describing the actual payload data format of a register in an abstract manner.
A class to describe which of the supported data types is used.
RegisterInfo structure for the LogicalNameMappingBackend.
DataType valueType
Data type of CONSTANT or VARIABLE type.
unsigned int firstIndex
The first index in the range.
DataDescriptor _dataDescriptor
std::string registerName
The target register name.
AccessModeFlags supportedFlags
Supported AccessMode flags.
bool readable
Flag if the register is readable.
unsigned int nChannels
The number of channels of the logical register.
bool writeable
Flag if the register is writeable.
unsigned int length
The length of the range (i.e.
std::string deviceName
The target device alias.
TargetType targetType
Type of the target.
RegisterPath name
Name of the register.
unsigned int bit
The bit of the target register (if TargetType::BIT)
std::vector< boost::shared_ptr< LNMBackend::AccessorPluginBase > > plugins
List of accessor plugins enabled for this register.
unsigned int channel
The channel of the target 2D register (if TargetType::CHANNEL)
std::map< std::string, LNMVariable > & _variables
Reference to the variables map inside the LNM backend.
BackendRegisterCatalogue< LNMBackendRegisterInfo > parseFile(const std::string &fileName)
parse the given XML file
std::vector< ValueType > getValueVectorFromXmlSubnode(const xmlpp::Node *node, const std::string &subnodeName, BackendRegisterCatalogue< LNMBackendRegisterInfo > const &catalogue)
std::string _fileName
file name of the logical map
ValueType getValueFromXmlSubnode(const xmlpp::Node *node, const std::string &subnodeName, BackendRegisterCatalogue< LNMBackendRegisterInfo > const &catalogue, bool hasDefault=false, ValueType defaultValue=ValueType())
Build a Value object for a given subnode.
void parseElement(const RegisterPath ¤tPath, const xmlpp::Element *element, BackendRegisterCatalogue< LNMBackendRegisterInfo > &catalogue)
called inside parseFile() to parse an XML element and its sub-elements recursively
std::map< std::string, std::string > _parameters
actual register info map (register name to target information)
void parsingError(const xmlpp::Node *node, const std::string &message)
throw a parsing error with more information
Class to store a register path name.
Exception thrown when a logic error has occured.
boost::shared_ptr< AccessorPluginBase > makePlugin(LNMBackendRegisterInfo info, size_t pluginIndex, const std::string &name, const std::map< std::string, std::string > ¶meters)
Factory function for accessor plugins.
std::string LogicalNameMapParser::getValueFromXmlSubnode< std::string >(const xmlpp::Node *node, const std::string &subnodeName, BackendRegisterCatalogue< LNMBackendRegisterInfo > const &catalogue, bool hasDefault, std::string defaultValue)
void callForType(const std::type_info &type, LAMBDATYPE lambda)
Helper function for running code which uses some compile-time type that is specified at runtime as a ...
@ wait_for_new_data
Make any read blocking until new data has arrived since the last read.
std::string to_string(const std::string &v)