18 #include <libxml2/libxml/xmlmemory.h> 19 #include <libxml2/libxml/parser.h> 29 using namespace OpcUa;
33 if (referenceName ==
"organizes")
36 if (referenceName ==
"references_to")
39 if (referenceName ==
"has_child")
42 if (referenceName ==
"has_event_source")
45 if (referenceName ==
"has_modelling_rule")
48 if (referenceName ==
"has_encoding")
51 if (referenceName ==
"has_description")
54 if (referenceName ==
"has_type_definition")
57 if (referenceName ==
"generates_event")
60 if (referenceName ==
"aggregates")
63 if (referenceName ==
"has_subtype")
66 if (referenceName ==
"has_property")
69 if (referenceName ==
"has_component")
72 if (referenceName ==
"has_notifier")
75 if (referenceName ==
"has_ordered_component")
78 if (referenceName ==
"has_model_parent")
81 if (referenceName ==
"from_state")
84 if (referenceName ==
"to_state")
87 if (referenceName ==
"has_clause")
90 if (referenceName ==
"has_effect")
93 if (referenceName ==
"has_historical_configuration")
96 if (referenceName ==
"has_historical_event_configuration")
99 if (referenceName ==
"has_substate_machine")
102 if (referenceName ==
"has_event_history")
105 if (referenceName ==
"always_generates_event")
108 if (referenceName ==
"has_true_substate")
111 if (referenceName ==
"has_false_substate")
114 if (referenceName ==
"has_condition")
117 if (referenceName ==
"non_hierarchical_references")
120 if (referenceName ==
"hierarchical_references")
123 if (referenceName ==
"has_cause")
126 if (referenceName ==
"has_sub_state_machine")
129 if (referenceName ==
"has_true_sub_state")
132 if (referenceName ==
"has_false_sub_state")
135 throw std::logic_error(
std::string(
"Unknown reference name '") + referenceName +
std::string(referenceName));
175 std::map<AttributeId, Variant> Attributes;
187 void operator()(xmlDocPtr doc)
195 void operator()(
void * ptr)
201 int xmlStrcmp(
const xmlChar * xmlStr,
const char * str)
203 return ::xmlStrcmp(xmlStr, (
const xmlChar *)str);
206 bool IsXmlNode(
const xmlNode & node)
208 return node.type == XML_ELEMENT_NODE;
211 bool IsXmlNode(
const xmlNode & node,
const char *
name,
bool debug =
false)
213 if (node.type != XML_ELEMENT_NODE)
218 if (xmlStrcmp(node.name, name))
233 return (
const char *)node.name;
236 std::string GetProperty(xmlNode & node,
const char * propName)
238 std::unique_ptr<xmlChar, LibXmlFree> attrValue(xmlGetProp(&node, (
const xmlChar *)propName), LibXmlFree());
239 const xmlChar * propValue = attrValue.get();
243 return (
const char *)propValue;
251 const std::string nodeValue = GetProperty(node,
"value");
253 if (!nodeValue.empty())
258 std::unique_ptr<xmlChar, LibXmlFree> content(xmlNodeGetContent(&node));
265 return (
const char *)content.get();
270 if (nodeValue ==
"bool")
275 if (nodeValue ==
"sbyte")
280 if (nodeValue ==
"byte")
285 if (nodeValue ==
"int16")
290 if (nodeValue ==
"uint16")
295 if (nodeValue ==
"int32")
300 if (nodeValue ==
"uint32")
305 if (nodeValue ==
"string")
310 if (nodeValue ==
"enum")
315 if (nodeValue ==
"int")
320 if (nodeValue ==
"byte_string")
325 if (nodeValue ==
"guid")
330 if (nodeValue ==
"date_time")
335 std::stringstream stream;
336 stream <<
"Unknown data type '" << nodeValue <<
"'.";
337 throw std::logic_error(stream.str());
340 inline ObjectId GetObjectIdOfType(xmlNode & node)
342 return GetObjectIdOfType(GetNodeValue(node));
400 std::stringstream stream;
401 stream <<
"Cannot convert ObjectId '" << (unsigned)
id <<
"' to VariantType.";
402 throw std::logic_error(stream.str());
459 std::stringstream stream;
460 stream <<
"Cannot convert VariantType '" << (unsigned)type <<
"' to ObjectId.";
461 throw std::logic_error(stream.str());
467 const ObjectId typeId = GetObjectIdOfType(GetProperty(node,
"type"));
468 return ConvertToVariantType(typeId);
475 if (nodeValue ==
"object")
480 if (nodeValue ==
"variable")
485 if (nodeValue ==
"method")
490 if (nodeValue ==
"object_type")
495 if (nodeValue ==
"variable_type")
500 if (nodeValue ==
"reference_type")
505 if (nodeValue ==
"data_type")
510 if (nodeValue ==
"data_type")
515 std::stringstream stream;
516 stream <<
"Unknown node class '" << nodeValue <<
"'. Line " << node.line <<
".";
517 throw std::logic_error(stream.str());
520 bool IsNumericNodeType(xmlNode & node)
522 const std::string propValue = GetProperty(node,
"type");
523 return propValue ==
"numeric";
526 uint32_t GetNamespaceIndex(xmlNode & node)
528 const std::string propValue = GetProperty(node,
"ns");
529 return atoi(propValue.c_str());
532 NodeId GetNodeId(xmlNode & node)
536 if (nodeValue.empty())
538 std::stringstream stream;
539 stream <<
"Empty node id. Line " << node.line <<
".";
540 throw std::logic_error(stream.str());
543 uint32_t nsIndex = GetNamespaceIndex(node);
545 if (IsNumericNodeType(node))
555 if (!str.empty() && (str ==
"false" || str ==
"0"))
567 if (nodeValue.empty())
569 std::stringstream stream;
570 stream <<
"Empty browse name. Line " << node.line <<
".";
571 throw std::logic_error(stream.str());
580 return QualifiedName(GetNamespaceIndex(node), GetText(node));
587 if (nodeValue.empty())
589 std::stringstream stream;
590 stream <<
"Empty browse name. Line " << node.line <<
".";
591 throw std::logic_error(stream.str());
597 uint32_t GetUInt32(xmlNode & node)
601 if (nodeValue.empty())
603 std::stringstream stream;
604 stream <<
"Empty opcua attribute value. Line " << node.line <<
".";
605 throw std::logic_error(stream.str());
608 return atoi(nodeValue.c_str());
611 int32_t GetInt32(xmlNode & node)
615 if (nodeValue.empty())
617 std::stringstream stream;
618 stream <<
"Empty opcua attribute value. Line " << node.line <<
".";
619 throw std::logic_error(stream.str());
622 return atoi(nodeValue.c_str());
626 bool GetBool(xmlNode & node)
629 return GetBool(nodeValue);
639 return Variant((int8_t)strtol(nodeValue.c_str(),
nullptr, 0));
642 return Variant((uint8_t)strtol(nodeValue.c_str(),
nullptr, 0));
645 return Variant((int16_t)strtol(nodeValue.c_str(),
nullptr, 0));
648 return Variant((uint16_t)strtol(nodeValue.c_str(),
nullptr, 0));
651 return Variant((int32_t)strtol(nodeValue.c_str(),
nullptr, 0));
654 return Variant((uint32_t)strtol(nodeValue.c_str(),
nullptr, 0));
657 return Variant((int64_t)strtoll(nodeValue.c_str(),
nullptr, 0));
660 return Variant((int64_t)strtoll(nodeValue.c_str(),
nullptr, 0));
663 return Variant(strtof(nodeValue.c_str(),
nullptr));
666 return Variant(strtod(nodeValue.c_str(),
nullptr));
682 if (IsXmlNode(node,
"id"))
685 else if (IsXmlNode(node,
"class"))
688 else if (IsXmlNode(node,
"browse_name"))
691 else if (IsXmlNode(node,
"display_name"))
694 else if (IsXmlNode(node,
"description"))
697 else if (IsXmlNode(node,
"write_mask"))
700 else if (IsXmlNode(node,
"user_write_mask"))
703 else if (IsXmlNode(node,
"is_abstract"))
706 else if (IsXmlNode(node,
"symmetric"))
709 else if (IsXmlNode(node,
"inverse_name"))
712 else if (IsXmlNode(node,
"contains_no_loops"))
715 else if (IsXmlNode(node,
"event_notifier"))
718 else if (IsXmlNode(node,
"value"))
721 else if (IsXmlNode(node,
"value_rank"))
724 else if (IsXmlNode(node,
"data_type"))
727 else if (IsXmlNode(node,
"array_dimensions"))
730 else if (IsXmlNode(node,
"access_level"))
733 else if (IsXmlNode(node,
"user_access_level"))
736 else if (IsXmlNode(node,
"minimum_sampling_interval"))
739 else if (IsXmlNode(node,
"historizing"))
742 else if (IsXmlNode(node,
"executable"))
745 else if (IsXmlNode(node,
"user_executable"))
756 return Variant(GetNodeId(node));
759 return Variant((int32_t)GetNodeClass(node));
762 return Variant(GetLocalizedText(node));
765 return Variant(GetQualifiedName(node));
772 return Variant(std::vector<uint8_t> {0});
775 return Variant(GetInt32(node));
783 return Variant(GetUInt32(node));
797 return Variant(GetObjectIdOfType(node));
804 return GetVariantValue(type, node);
810 AttributesCollector(INode & node,
bool debug)
816 virtual void Process(xmlNode & node)
818 for (xmlNodePtr subNode = node.children; subNode; subNode = subNode->next)
820 if (!IsXmlNode(*subNode))
825 const AttributeId attribute = GetAttributeId(*subNode);
829 OpcUaNode.Id = GetNodeId(*subNode);
833 const Variant value = GetAttributeValue(attribute, *subNode);
834 AddAttribute(attribute, value);
845 bool IsAttributes(
const xmlNode & node)
const 847 return IsXmlNode(node,
"attributes");
850 template <
typename T>
851 void AddAttribute(
AttributeId attr,
const T & value)
853 OpcUaNode.Attributes.insert(std::make_pair(attr,
Variant(value)));
863 auto attrPos = OpcUaNode.Attributes.find(attr);
865 if (attrPos == OpcUaNode.Attributes.end())
870 return ConvertToObjectId(attrPos->second.Type());
881 ReferencesCollector(INode & node,
bool debug)
887 virtual void Process(xmlNode & node)
889 for (xmlNodePtr refNode = node.children; refNode; refNode = refNode->next)
891 if (!IsXmlNode(*refNode))
898 AddReferenceToNode(*refNode);
901 catch (
const std::exception & exc)
903 std::cerr << exc.what() << std::endl;
909 void AddReferenceToNode(xmlNode & refNode)
912 const NodeId targetNode = GetNodeId(refNode);
914 reference.Id = GetReferenceId(refName);
916 for (xmlNodePtr subNode = refNode.children; subNode; subNode = subNode->next)
918 if (!IsXmlNode(*subNode))
923 const std::string & nodeName = GetNodeName(*subNode);
925 if (nodeName ==
"id")
927 reference.TargetNode = GetNodeId(*subNode);
930 else if (nodeName ==
"class")
932 reference.TargetClass = GetNodeClass(*subNode);
935 else if (nodeName ==
"browse_name")
937 reference.TargetBrowseName = GetQualifiedName(*subNode);
940 else if (nodeName ==
"display_name")
942 reference.TargetDisplayName = GetLocalizedText(*subNode);
945 else if (nodeName ==
"is_forward")
947 reference.IsForward = GetBool(GetNodeValue(*subNode));
950 else if (nodeName ==
"type_definition")
952 reference.TargetType = GetNodeId(*subNode);
956 EnsureValid(reference, refNode.line);
957 OpcUaNode.References.push_back(reference);
961 void EnsureValid(
const Reference & ref,
int lineNum)
const 963 std::stringstream stream;
967 stream <<
"Unknown reference type. line" << lineNum <<
".";
968 throw std::logic_error(stream.str());
971 if (ref.TargetNode ==
NodeId())
973 stream <<
"Empty target node Id. line" << lineNum <<
".";
974 throw std::logic_error(stream.str());
986 NodesCollector(std::map<NodeId, INode> & nodes,
bool debug)
992 virtual void Process(xmlNode & node)
994 if (!IsXmlNode(node))
1001 if (IsXmlNode(node,
"node"))
1003 opcuaNode.IsExternal =
false;
1006 else if (IsXmlNode(node,
"external"))
1008 opcuaNode.IsExternal =
true;
1015 std::cerr <<
"Unknown node '" << node.name <<
"' at line " << node.line <<
"." << std::endl;
1021 FillNode(node, opcuaNode);
1022 EnsureNodeIsValid(opcuaNode, node);
1023 Nodes.insert(std::make_pair(opcuaNode.Id, opcuaNode));
1027 void EnsureNodeIsValid(
const INode & opcuaNode,
const xmlNode & node)
const 1029 if (opcuaNode.Id ==
NodeId())
1031 std::stringstream stream;
1032 stream <<
"INode at line '" << node.line <<
"' has no Id.";
1033 throw std::logic_error(stream.str());
1037 void FillNode(
const xmlNode & node, INode & opcuaNode)
const 1039 AttributesCollector attributeCollector(opcuaNode, Debug);
1040 ReferencesCollector referencCollector(opcuaNode, Debug);
1042 for (xmlNodePtr subNode = node.children; subNode; subNode = subNode->next)
1044 if (IsXmlNode(*subNode,
"attributes"))
1046 attributeCollector.Process(*subNode);
1050 else if (IsXmlNode(*subNode,
"references"))
1052 referencCollector.Process(*subNode);
1057 std::cerr <<
"Unknown node '" << subNode->name <<
"' at line " << subNode->line <<
"." << std::endl;
1063 std::map<NodeId, INode> & Nodes;
1068 class ConfigurationProcessor
1071 ConfigurationProcessor(
bool debug)
1077 std::map<NodeId, INode> Process(xmlDoc & doc)
1079 xmlNodePtr rootNode = xmlDocGetRootElement(&doc);
1080 EnsureRootNodeValid(*rootNode);
1082 std::map<NodeId, INode> nodes;
1083 NodesCollector nodesBuilder(nodes, Debug);
1085 for (xmlNodePtr cur = rootNode->children; cur; cur = cur->next)
1087 nodesBuilder.Process(*cur);
1094 void EnsureRootNodeValid(xmlNode & rootNode)
1096 if (rootNode.type != XML_ELEMENT_NODE)
1098 throw std::logic_error(
"Root element is not a node.'");
1101 if (xmlStrcmp(rootNode.name,
"address_space"))
1103 throw std::logic_error(
std::string(
"Invalid root element '") + (
const char *)rootNode.name +
std::string(
"'."));
1106 std::unique_ptr<xmlChar, LibXmlFree> versionBuf(xmlGetProp(&rootNode, (
const xmlChar *)
"version"), LibXmlFree());
1107 const xmlChar *
version = versionBuf.get();
1111 throw std::logic_error(
"Address space element has no 'version' attribute.");
1114 if (xmlStrcmp(version,
"1"))
1116 throw std::logic_error(
std::string(
"Unknown version '") + (
const char *)version +
std::string(
"'of address space."));
1124 std::map<NodeId, INode> ParseConfig(
const char * configPath,
bool debug)
1126 std::unique_ptr<xmlDoc, XmlDocDeleter> doc(xmlParseFile(configPath), XmlDocDeleter());
1133 ConfigurationProcessor xmlConfiguration(debug);
1134 return xmlConfiguration.Process(*doc);
1138 class NodesRegistrator
1142 : Registry(registry)
1147 void RegisterNodes(
const std::map<NodeId, INode> & nodes)
1149 for (
const auto & node : nodes)
1151 if (!node.second.IsExternal)
1153 RegisterNode(node.second);
1156 AddReferences(node.second);
1161 void RegisterNode(
const INode & node)
1164 for (
const std::pair<AttributeId, Variant> & attr : node.Attributes)
1170 void AddReferences(
const INode & node)
1172 for (
const Reference & ref : node.References)
1198 : Registry(registry)
1205 std::map<NodeId, INode> nodes = ParseConfig(fileName,
Debug);
1207 reg.RegisterNodes(nodes);
LocalizedText DisplayName
registry_t< std::mutex > registry
void Load(const char *fileName)
NodeClass TargetNodeClass
OPC UA Address space part. GNU LGPL.
NodeId TargetNodeTypeDefinition
NodeId StringNodeId(std::string value, uint16_t namespaceIndex=0)
NodeId NumericNodeId(uint32_t value, uint16_t namespaceIndex=0)
OpcUa::NodeManagementServices & Registry