00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #include "xml_address_space_loader.h"
00014 #include "xml_processor.h"
00015
00016 #include <opc/ua/server/address_space.h>
00017
00018 #include <libxml2/libxml/xmlmemory.h>
00019 #include <libxml2/libxml/parser.h>
00020
00021 #include <iostream>
00022 #include <map>
00023 #include <stdexcept>
00024 #include <sstream>
00025 #include <string.h>
00026
00027 namespace
00028 {
00029 using namespace OpcUa;
00030
00031 ReferenceId GetReferenceId(const std::string& referenceName)
00032 {
00033 if (referenceName == "organizes")
00034 return ReferenceId::Organizes;
00035 if (referenceName == "references_to")
00036 return ReferenceId::References;
00037 if (referenceName == "has_child")
00038 return ReferenceId::HasChild;
00039 if (referenceName == "has_event_source")
00040 return ReferenceId::HasEventSource;
00041 if (referenceName == "has_modelling_rule")
00042 return ReferenceId::HasModellingRule;
00043 if (referenceName == "has_encoding")
00044 return ReferenceId::HasEncoding;
00045 if (referenceName == "has_description")
00046 return ReferenceId::HasDescription;
00047 if (referenceName == "has_type_definition")
00048 return ReferenceId::HasTypeDefinition;
00049 if (referenceName == "generates_event")
00050 return ReferenceId::GeneratesEvent;
00051 if (referenceName == "aggregates")
00052 return ReferenceId::Aggregates;
00053 if (referenceName == "has_subtype")
00054 return ReferenceId::HasSubtype;
00055 if (referenceName == "has_property")
00056 return ReferenceId::HasProperty;
00057 if (referenceName == "has_component")
00058 return ReferenceId::HasComponent;
00059 if (referenceName == "has_notifier")
00060 return ReferenceId::HasNotifier;
00061 if (referenceName == "has_ordered_component")
00062 return ReferenceId::HasOrderedComponent;
00063 if (referenceName == "has_model_parent")
00064 return ReferenceId::HasModelParent;
00065 if (referenceName == "from_state")
00066 return ReferenceId::FromState;
00067 if (referenceName == "to_state")
00068 return ReferenceId::ToState;
00069 if (referenceName == "has_clause")
00070 return ReferenceId::HasCause;
00071 if (referenceName == "has_effect")
00072 return ReferenceId::HasEffect;
00073 if (referenceName == "has_historical_configuration")
00074 return ReferenceId::HasHistoricalConfiguration;
00075 if (referenceName == "has_historical_event_configuration")
00076 return ReferenceId::HasHistoricalEventConfiguration;
00077 if (referenceName == "has_substate_machine")
00078 return ReferenceId::HasSubStateMachine;
00079 if (referenceName == "has_event_history")
00080 return ReferenceId::HasEventHistory;
00081 if (referenceName == "always_generates_event")
00082 return ReferenceId::AlwaysGeneratesEvent;
00083 if (referenceName == "has_true_substate")
00084 return ReferenceId::HasTrueSubState;
00085 if (referenceName == "has_false_substate")
00086 return ReferenceId::HasFalseSubState;
00087 if (referenceName == "has_condition")
00088 return ReferenceId::HasCondition;
00089 if (referenceName == "non_hierarchical_references")
00090 return ReferenceId::NonHierarchicalReferences;
00091 if (referenceName == "hierarchical_references")
00092 return ReferenceId::HierarchicalReferences;
00093 if (referenceName == "has_cause")
00094 return ReferenceId::HasCause;
00095 if (referenceName == "has_sub_state_machine")
00096 return ReferenceId::HasSubStateMachine;
00097 if (referenceName == "has_true_sub_state")
00098 return ReferenceId::HasTrueSubState;
00099 if (referenceName == "has_false_sub_state")
00100 return ReferenceId::HasFalseSubState;
00101
00102 throw std::logic_error(std::string("Unknown reference name '") + referenceName + std::string(referenceName));
00103 }
00104
00105 struct Reference
00106 {
00107 ReferenceId Id;
00108 NodeId TargetNode;
00109 bool IsForward;
00110 NodeClass TargetClass;
00111 QualifiedName TargetBrowseName;
00112 LocalizedText TargetDisplayName;
00113 NodeId TargetType;
00114
00115 Reference()
00116 : Id(ReferenceId::Unknown)
00117 , IsForward(true)
00118 , TargetClass(NodeClass::Unspecified)
00119 {
00120 }
00121 };
00122
00123 struct Attribute
00124 {
00125 AttributeId Id;
00126 Variant Value;
00127 Attribute()
00128 : Id(AttributeId::Unknown)
00129 {
00130 }
00131
00132 Attribute(AttributeId id, Variant value)
00133 : Id(id)
00134 , Value(value)
00135 {
00136 }
00137 };
00138
00139 struct INode
00140 {
00141 NodeId Id;
00142 std::map<AttributeId, Variant> Attributes;
00143 std::vector<Reference> References;
00144 bool IsExternal;
00145
00146 INode()
00147 : IsExternal(false)
00148 {
00149 }
00150 };
00151
00152 struct XmlDocDeleter
00153 {
00154 void operator() (xmlDocPtr doc)
00155 {
00156 xmlFreeDoc(doc);
00157 }
00158 };
00159
00160 struct LibXmlFree
00161 {
00162 void operator() (void* ptr)
00163 {
00164 xmlFree(ptr);
00165 }
00166 };
00167
00168 int xmlStrcmp(const xmlChar* xmlStr, const char* str)
00169 {
00170 return ::xmlStrcmp(xmlStr, (const xmlChar*)str);
00171 }
00172
00173 bool IsXmlNode(const xmlNode& node)
00174 {
00175 return node.type == XML_ELEMENT_NODE;
00176 }
00177
00178 bool IsXmlNode(const xmlNode& node, const char* name, bool debug = false)
00179 {
00180 if (node.type != XML_ELEMENT_NODE)
00181 {
00182 return false;
00183 }
00184
00185 if (xmlStrcmp(node.name, name))
00186 {
00187 return false;
00188 }
00189 return true;
00190 }
00191
00192 std::string GetNodeName(xmlNode& node)
00193 {
00194 if (!node.name)
00195 {
00196 return std::string();
00197 }
00198 return (const char*)node.name;
00199 }
00200
00201 std::string GetProperty(xmlNode& node, const char* propName)
00202 {
00203 std::unique_ptr<xmlChar, LibXmlFree> attrValue(xmlGetProp(&node, (const xmlChar*)propName), LibXmlFree());
00204 const xmlChar* propValue = attrValue.get();
00205 if (propValue)
00206 {
00207 return (const char*)propValue;
00208 }
00209 return std::string();
00210 }
00211
00212 std::string GetNodeValue(xmlNode& node)
00213 {
00214 const std::string nodeValue = GetProperty(node, "value");
00215 if (!nodeValue.empty())
00216 {
00217 return nodeValue;
00218 }
00219
00220 std::unique_ptr<xmlChar, LibXmlFree> content(xmlNodeGetContent(&node));
00221 if (!content)
00222 {
00223 return std::string();
00224 }
00225 return (const char*)content.get();
00226 }
00227
00228 ObjectId GetObjectIdOfType(const std::string& nodeValue)
00229 {
00230 if (nodeValue == "bool")
00231 {
00232 return ObjectId::Boolean;
00233 }
00234 if (nodeValue == "sbyte")
00235 {
00236 return ObjectId::SByte;
00237 }
00238 if (nodeValue == "byte")
00239 {
00240 return ObjectId::Byte;
00241 }
00242 if (nodeValue == "int16")
00243 {
00244 return ObjectId::Int16;
00245 }
00246 if (nodeValue == "uint16")
00247 {
00248 return ObjectId::UInt16;
00249 }
00250 if (nodeValue == "int32")
00251 {
00252 return ObjectId::Int32;
00253 }
00254 if (nodeValue == "uint32")
00255 {
00256 return ObjectId::UInt32;
00257 }
00258 if (nodeValue == "string")
00259 {
00260 return ObjectId::String;
00261 }
00262 if (nodeValue == "enum")
00263 {
00264 return ObjectId::Enumeration;
00265 }
00266 if (nodeValue == "int")
00267 {
00268 return ObjectId::Integer;
00269 }
00270 if (nodeValue == "byte_string")
00271 {
00272 return ObjectId::ByteString;
00273 }
00274 if (nodeValue == "guid")
00275 {
00276 return ObjectId::Guid;
00277 }
00278 if (nodeValue == "date_time")
00279 {
00280 return ObjectId::DateTime;
00281 }
00282
00283 std::stringstream stream;
00284 stream << "Unknown data type '" << nodeValue << "'.";
00285 throw std::logic_error(stream.str());
00286 }
00287
00288 inline ObjectId GetObjectIdOfType(xmlNode& node)
00289 {
00290 return GetObjectIdOfType(GetNodeValue(node));
00291 }
00292
00293 inline VariantType ConvertToVariantType(ObjectId id)
00294 {
00295 switch (id)
00296 {
00297 case ObjectId::Null: return VariantType::NUL;
00298 case ObjectId::Boolean: return VariantType::BOOLEAN;
00299 case ObjectId::SByte: return VariantType::SBYTE;
00300 case ObjectId::Byte: return VariantType::BYTE;
00301 case ObjectId::Int16: return VariantType::INT16;
00302 case ObjectId::UInt16: return VariantType::UINT16;
00303
00304 case ObjectId::Integer:
00305 case ObjectId::Enumeration:
00306 case ObjectId::Int32: return VariantType::INT32;
00307
00308 case ObjectId::UInt32: return VariantType::UINT32;
00309 case ObjectId::Int64: return VariantType::INT64;
00310 case ObjectId::UInt64: return VariantType::UINT64;
00311 case ObjectId::Float: return VariantType::FLOAT;
00312 case ObjectId::Double: return VariantType::DOUBLE;
00313 case ObjectId::String: return VariantType::STRING;
00314 case ObjectId::ByteString: return VariantType::BYTE_STRING;
00315 case ObjectId::DateTime: return VariantType::DATE_TIME;
00316 case ObjectId::Guid: return VariantType::GUId;
00317
00318 default:
00319 std::stringstream stream;
00320 stream << "Cannot convert ObjectId '" << (unsigned)id << "' to VariantType.";
00321 throw std::logic_error(stream.str());
00322 }
00323 }
00324
00325 inline ObjectId ConvertToObjectId(VariantType type)
00326 {
00327 switch (type)
00328 {
00329 case VariantType::NUL: return ObjectId::Null;
00330 case VariantType::BOOLEAN: return ObjectId::Boolean;
00331 case VariantType::SBYTE: return ObjectId::SByte;
00332 case VariantType::BYTE: return ObjectId::Byte;
00333 case VariantType::INT16: return ObjectId::Int16;
00334 case VariantType::UINT16: return ObjectId::UInt16;
00335 case VariantType::INT32: return ObjectId::Int32;
00336 case VariantType::UINT32: return ObjectId::UInt32;
00337 case VariantType::INT64: return ObjectId::Int64;
00338 case VariantType::UINT64: return ObjectId::UInt64;
00339 case VariantType::FLOAT: return ObjectId::Float;
00340 case VariantType::DOUBLE: return ObjectId::Double;
00341 case VariantType::STRING: return ObjectId::String;
00342 case VariantType::BYTE_STRING: return ObjectId::ByteString;
00343 case VariantType::DATE_TIME: return ObjectId::DateTime;
00344 case VariantType::GUId: return ObjectId::Guid;
00345
00346 default:
00347 std::stringstream stream;
00348 stream << "Cannot convert VariantType '"<< (unsigned)type << "' to ObjectId.";
00349 throw std::logic_error(stream.str());
00350 }
00351 }
00352
00353 inline VariantType GetVariantType(xmlNode& node)
00354 {
00355 const ObjectId typeId = GetObjectIdOfType(GetProperty(node, "type"));
00356 return ConvertToVariantType(typeId);
00357 }
00358
00359 NodeClass GetNodeClass(xmlNode& node)
00360 {
00361 const std::string nodeValue = GetNodeValue(node);
00362 if (nodeValue == "object")
00363 {
00364 return NodeClass::Object;
00365 }
00366 if (nodeValue == "variable")
00367 {
00368 return NodeClass::Variable;
00369 }
00370 if (nodeValue == "method")
00371 {
00372 return NodeClass::Method;
00373 }
00374 if (nodeValue == "object_type")
00375 {
00376 return NodeClass::ObjectType;
00377 }
00378 if (nodeValue == "variable_type")
00379 {
00380 return NodeClass::VariableType;
00381 }
00382 if (nodeValue == "reference_type")
00383 {
00384 return NodeClass::ReferenceType;
00385 }
00386 if (nodeValue == "data_type")
00387 {
00388 return NodeClass::DataType;
00389 }
00390 if (nodeValue == "data_type")
00391 {
00392 return NodeClass::View;
00393 }
00394 std::stringstream stream;
00395 stream << "Unknown node class '" << nodeValue << "'. Line " << node.line << ".";
00396 throw std::logic_error(stream.str());
00397 }
00398
00399 bool IsNumericNodeType(xmlNode& node)
00400 {
00401 const std::string propValue = GetProperty(node, "type");
00402 return propValue == "numeric";
00403 }
00404
00405 uint32_t GetNamespaceIndex(xmlNode& node)
00406 {
00407 const std::string propValue = GetProperty(node, "ns");
00408 return atoi(propValue.c_str());
00409 }
00410
00411 NodeId GetNodeId(xmlNode& node)
00412 {
00413 std::string nodeValue = GetNodeValue(node);
00414 if (nodeValue.empty())
00415 {
00416 std::stringstream stream;
00417 stream << "Empty node id. Line " << node.line << ".";
00418 throw std::logic_error(stream.str());
00419 }
00420 uint32_t nsIndex = GetNamespaceIndex(node);
00421 if (IsNumericNodeType(node))
00422 {
00423 return NumericNodeId(atoi(nodeValue.c_str()), nsIndex);
00424 }
00425 return StringNodeId(nodeValue, nsIndex);
00426 }
00427
00428 bool GetBool(std::string str)
00429 {
00430 if (!str.empty() && (str == "false" || str == "0"))
00431 {
00432 return false;
00433 }
00434 return !str.empty();
00435 }
00436
00437 std::string GetText(xmlNode& node)
00438 {
00439 const std::string nodeValue = GetNodeValue(node);
00440 if (nodeValue.empty())
00441 {
00442 std::stringstream stream;
00443 stream << "Empty browse name. Line " << node.line << ".";
00444 throw std::logic_error(stream.str());
00445 }
00446 return nodeValue;
00447 }
00448
00449
00450 QualifiedName GetQualifiedName(xmlNode& node)
00451 {
00452 return QualifiedName(GetNamespaceIndex(node), GetText(node));
00453 }
00454
00455 LocalizedText GetLocalizedText(xmlNode& node)
00456 {
00457 const std::string nodeValue = GetNodeValue(node);
00458 if (nodeValue.empty())
00459 {
00460 std::stringstream stream;
00461 stream << "Empty browse name. Line " << node.line << ".";
00462 throw std::logic_error(stream.str());
00463 }
00464 return LocalizedText(nodeValue);
00465 }
00466
00467 uint32_t GetUInt32(xmlNode& node)
00468 {
00469 const std::string nodeValue = GetNodeValue(node);
00470 if (nodeValue.empty())
00471 {
00472 std::stringstream stream;
00473 stream << "Empty opcua attribute value. Line " << node.line << ".";
00474 throw std::logic_error(stream.str());
00475 }
00476 return atoi(nodeValue.c_str());
00477 }
00478
00479 int32_t GetInt32(xmlNode& node)
00480 {
00481 const std::string nodeValue = GetNodeValue(node);
00482 if (nodeValue.empty())
00483 {
00484 std::stringstream stream;
00485 stream << "Empty opcua attribute value. Line " << node.line << ".";
00486 throw std::logic_error(stream.str());
00487 }
00488 return atoi(nodeValue.c_str());
00489 }
00490
00491
00492 bool GetBool(xmlNode& node)
00493 {
00494 const std::string nodeValue = GetNodeValue(node);
00495 return GetBool(nodeValue);
00496 }
00497
00498 Variant GetVariantValue(OpcUa::VariantType type, xmlNode& node)
00499 {
00500 const std::string nodeValue = GetNodeValue(node);
00501 switch (type)
00502 {
00503 case VariantType::SBYTE:
00504 return Variant((int8_t)strtol(nodeValue.c_str(), nullptr, 0));
00505 case VariantType::BYTE:
00506 return Variant((uint8_t)strtol(nodeValue.c_str(), nullptr, 0));
00507 case VariantType::INT16:
00508 return Variant((int16_t)strtol(nodeValue.c_str(), nullptr, 0));
00509 case VariantType::UINT16:
00510 return Variant((uint16_t)strtol(nodeValue.c_str(), nullptr, 0));
00511 case VariantType::INT32:
00512 return Variant((int32_t)strtol(nodeValue.c_str(), nullptr, 0));
00513 case VariantType::UINT32:
00514 return Variant((uint32_t)strtol(nodeValue.c_str(), nullptr, 0));
00515 case VariantType::INT64:
00516 return Variant((int64_t)strtoll(nodeValue.c_str(), nullptr, 0));
00517 case VariantType::UINT64:
00518 return Variant((int64_t)strtoll(nodeValue.c_str(), nullptr, 0));
00519 case VariantType::FLOAT:
00520 return Variant(strtof(nodeValue.c_str(), nullptr));
00521 case VariantType::DOUBLE:
00522 return Variant(strtod(nodeValue.c_str(), nullptr));
00523
00524 case VariantType::NUL:
00525 return Variant();
00526
00527 case VariantType::STRING:
00528 default:
00529 break;
00530 }
00531 return Variant(nodeValue);
00532 }
00533
00534 OpcUa::AttributeId GetAttributeId(xmlNode& node)
00535 {
00536 if (IsXmlNode(node, "id"))
00537 return AttributeId::NodeId;
00538 else if (IsXmlNode(node, "class"))
00539 return AttributeId::NodeClass;
00540 else if (IsXmlNode(node, "browse_name"))
00541 return AttributeId::BrowseName;
00542 else if (IsXmlNode(node, "display_name"))
00543 return AttributeId::DisplayName;
00544 else if (IsXmlNode(node, "description"))
00545 return AttributeId::Description;
00546 else if (IsXmlNode(node, "write_mask"))
00547 return AttributeId::WriteMask;
00548 else if (IsXmlNode(node, "user_write_mask"))
00549 return AttributeId::UserWriteMask;
00550 else if (IsXmlNode(node, "is_abstract"))
00551 return AttributeId::IsAbstract;
00552 else if (IsXmlNode(node, "symmetric"))
00553 return AttributeId::Symmetric;
00554 else if (IsXmlNode(node, "inverse_name"))
00555 return AttributeId::InverseName;
00556 else if (IsXmlNode(node, "contains_no_loops"))
00557 return AttributeId::ContainsNoLoops;
00558 else if (IsXmlNode(node, "event_notifier"))
00559 return AttributeId::EventNotifier;
00560 else if (IsXmlNode(node, "value"))
00561 return AttributeId::Value;
00562 else if (IsXmlNode(node, "value_rank"))
00563 return AttributeId::ValueRank;
00564 else if (IsXmlNode(node, "data_type"))
00565 return AttributeId::DataType;
00566 else if (IsXmlNode(node, "array_dimensions"))
00567 return AttributeId::ArrayDimensions;
00568 else if (IsXmlNode(node, "access_level"))
00569 return AttributeId::AccessLevel;
00570 else if (IsXmlNode(node, "user_access_level"))
00571 return AttributeId::UserAccessLevel;
00572 else if (IsXmlNode(node, "minimum_sampling_interval"))
00573 return AttributeId::MinimumSamplingInterval;
00574 else if (IsXmlNode(node, "historizing"))
00575 return AttributeId::Historizing;
00576 else if (IsXmlNode(node, "executable"))
00577 return AttributeId::Executable;
00578 else if (IsXmlNode(node, "user_executable"))
00579 return AttributeId::UserExecutable;
00580
00581 return AttributeId::Unknown;
00582 }
00583
00584 Variant GetAttributeValue(OpcUa::AttributeId id, xmlNode& node)
00585 {
00586 switch (id)
00587 {
00588 case AttributeId::NodeId:
00589 return Variant(GetNodeId(node));
00590
00591 case AttributeId::NodeClass:
00592 return Variant((int32_t)GetNodeClass(node));
00593
00594 case AttributeId::DisplayName:
00595 return Variant(GetLocalizedText(node));
00596
00597 case AttributeId::BrowseName:
00598 return Variant(GetQualifiedName(node));
00599
00600 case AttributeId::Description:
00601 case AttributeId::InverseName:
00602 return Variant(GetText(node));
00603
00604 case AttributeId::EventNotifier:
00605 return Variant(std::vector<uint8_t>{0});
00606
00607 case AttributeId::ValueRank:
00608 return Variant(GetInt32(node));
00609
00610 case AttributeId::WriteMask:
00611 case AttributeId::UserWriteMask:
00612 case AttributeId::ArrayDimensions:
00613 case AttributeId::AccessLevel:
00614 case AttributeId::UserAccessLevel:
00615 case AttributeId::MinimumSamplingInterval:
00616 return Variant(GetUInt32(node));
00617
00618 case AttributeId::IsAbstract:
00619 case AttributeId::Symmetric:
00620 case AttributeId::ContainsNoLoops:
00621 case AttributeId::Historizing:
00622 case AttributeId::Executable:
00623 case AttributeId::UserExecutable:
00624 return Variant(GetBool(node));
00625
00626 case AttributeId::Value:
00627 break;
00628
00629 case AttributeId::DataType:
00630 return Variant(GetObjectIdOfType(node));
00631
00632 default:
00633 return Variant(GetText(node));
00634 }
00635 const VariantType type = GetVariantType(node);
00636 return GetVariantValue(type, node);
00637 }
00638
00639 class AttributesCollector : private Internal::XmlProcessor
00640 {
00641 public:
00642 AttributesCollector(INode& node, bool debug)
00643 : OpcUaNode(node)
00644 , Debug(debug)
00645 {
00646 }
00647
00648 virtual void Process(xmlNode& node)
00649 {
00650 for (xmlNodePtr subNode = node.children; subNode; subNode = subNode->next)
00651 {
00652 if (!IsXmlNode(*subNode))
00653 {
00654 continue;
00655 }
00656 const AttributeId attribute = GetAttributeId(*subNode);
00657 if (attribute == AttributeId::NodeId)
00658 {
00659 OpcUaNode.Id = GetNodeId(*subNode);
00660 continue;
00661 }
00662
00663 const Variant value = GetAttributeValue(attribute, *subNode);
00664 AddAttribute(attribute, value);
00665 }
00666
00667
00668 if (!HasAttribute(AttributeId::DataType) && HasAttribute(AttributeId::Value))
00669 {
00670 AddAttribute(AttributeId::DataType, GetDataType(AttributeId::Value));
00671 }
00672 }
00673
00674 private:
00675 bool IsAttributes(const xmlNode& node) const
00676 {
00677 return IsXmlNode(node, "attributes");
00678 }
00679
00680 template <typename T>
00681 void AddAttribute(AttributeId attr, const T& value)
00682 {
00683 OpcUaNode.Attributes.insert(std::make_pair(attr, Variant(value)));
00684 }
00685
00686 bool HasAttribute(AttributeId attr) const
00687 {
00688 return OpcUaNode.Attributes.find(AttributeId::DataType) != OpcUaNode.Attributes.end();
00689 }
00690
00691 ObjectId GetDataType(AttributeId attr) const
00692 {
00693 auto attrPos = OpcUaNode.Attributes.find(attr);
00694 if (attrPos == OpcUaNode.Attributes.end())
00695 {
00696 return ObjectId::Null;
00697 }
00698 return ConvertToObjectId(attrPos->second.Type());
00699 }
00700
00701 private:
00702 INode& OpcUaNode;
00703 const bool Debug;
00704 };
00705
00706 class ReferencesCollector : private Internal::XmlProcessor
00707 {
00708 public:
00709 ReferencesCollector(INode& node, bool debug)
00710 : OpcUaNode(node)
00711 , Debug(debug)
00712 {
00713 }
00714
00715 virtual void Process(xmlNode& node)
00716 {
00717 for (xmlNodePtr refNode = node.children; refNode; refNode = refNode->next)
00718 {
00719 if (!IsXmlNode(*refNode))
00720 {
00721 continue;
00722 }
00723
00724 try
00725 {
00726 AddReferenceToNode(*refNode);
00727 }
00728 catch (const std::exception& exc)
00729 {
00730 std::cerr << exc.what() << std::endl;
00731 }
00732 }
00733 }
00734
00735 private:
00736 void AddReferenceToNode(xmlNode& refNode)
00737 {
00738 const std::string refName = GetNodeName(refNode);
00739 const NodeId targetNode = GetNodeId(refNode);
00740 Reference reference;
00741 reference.Id = GetReferenceId(refName);
00742
00743 for (xmlNodePtr subNode = refNode.children; subNode; subNode = subNode->next)
00744 {
00745 if (!IsXmlNode(*subNode))
00746 {
00747 continue;
00748 }
00749
00750 const std::string& nodeName = GetNodeName(*subNode);
00751 if (nodeName == "id")
00752 {
00753 reference.TargetNode = GetNodeId(*subNode);
00754 }
00755 else if (nodeName == "class")
00756 {
00757 reference.TargetClass = GetNodeClass(*subNode);
00758 }
00759 else if (nodeName == "browse_name")
00760 {
00761 reference.TargetBrowseName = GetQualifiedName(*subNode);
00762 }
00763 else if (nodeName == "display_name")
00764 {
00765 reference.TargetDisplayName = GetLocalizedText(*subNode);
00766 }
00767 else if (nodeName == "is_forward")
00768 {
00769 reference.IsForward = GetBool(GetNodeValue(*subNode));
00770 }
00771 else if (nodeName == "type_definition")
00772 {
00773 reference.TargetType = GetNodeId(*subNode);
00774 }
00775 }
00776
00777 EnsureValid(reference, refNode.line);
00778 OpcUaNode.References.push_back(reference);
00779 }
00780
00781 private:
00782 void EnsureValid(const Reference& ref, int lineNum) const
00783 {
00784 std::stringstream stream;
00785 if (ref.Id == ReferenceId::Unknown)
00786 {
00787 stream << "Unknown reference type. line" << lineNum << ".";
00788 throw std::logic_error(stream.str());
00789 }
00790 if (ref.TargetNode == NodeId())
00791 {
00792 stream << "Empty target node Id. line" << lineNum << ".";
00793 throw std::logic_error(stream.str());
00794 }
00795 }
00796
00797 private:
00798 INode& OpcUaNode;
00799 const bool Debug;
00800 };
00801
00802 class NodesCollector : private Internal::XmlProcessor
00803 {
00804 public:
00805 NodesCollector(std::map<NodeId, INode>& nodes, bool debug)
00806 : Nodes(nodes)
00807 , Debug(debug)
00808 {
00809 }
00810
00811 virtual void Process(xmlNode& node)
00812 {
00813 if (!IsXmlNode(node))
00814 {
00815 return;
00816 }
00817
00818 INode opcuaNode;
00819 if (IsXmlNode(node, "node"))
00820 {
00821 opcuaNode.IsExternal = false;
00822 }
00823 else if (IsXmlNode(node, "external"))
00824 {
00825 opcuaNode.IsExternal = true;
00826 }
00827 else
00828 {
00829 if (Debug)
00830 {
00831 std::cerr << "Unknown node '" << node.name << "' at line " << node.line << "." << std::endl;
00832 }
00833 return;
00834 }
00835
00836 FillNode(node, opcuaNode);
00837 EnsureNodeIsValid(opcuaNode, node);
00838 Nodes.insert(std::make_pair(opcuaNode.Id, opcuaNode));
00839 }
00840
00841 private:
00842 void EnsureNodeIsValid(const INode& opcuaNode, const xmlNode& node) const
00843 {
00844 if (opcuaNode.Id == NodeId())
00845 {
00846 std::stringstream stream;
00847 stream << "INode at line '" << node.line << "' has no Id.";
00848 throw std::logic_error(stream.str());
00849 }
00850 }
00851
00852 void FillNode(const xmlNode& node, INode& opcuaNode) const
00853 {
00854 AttributesCollector attributeCollector(opcuaNode, Debug);
00855 ReferencesCollector referencCollector(opcuaNode, Debug);
00856
00857 for (xmlNodePtr subNode = node.children; subNode; subNode = subNode->next)
00858 {
00859 if (IsXmlNode(*subNode, "attributes"))
00860 {
00861 attributeCollector.Process(*subNode);
00862 continue;
00863 }
00864 else if (IsXmlNode(*subNode, "references"))
00865 {
00866 referencCollector.Process(*subNode);
00867 }
00868 else if (Debug)
00869 {
00870 std::cerr << "Unknown node '" << subNode->name << "' at line " << subNode->line << "." << std::endl;
00871 }
00872 }
00873 }
00874
00875 private:
00876 std::map<NodeId, INode>& Nodes;
00877 const bool Debug;
00878 };
00879
00880
00881 class ConfigurationProcessor
00882 {
00883 public:
00884 ConfigurationProcessor(bool debug)
00885 : Debug(debug)
00886 {
00887
00888 }
00889
00890 std::map<NodeId, INode> Process(xmlDoc& doc)
00891 {
00892 xmlNodePtr rootNode = xmlDocGetRootElement(&doc);
00893 EnsureRootNodeValid(*rootNode);
00894
00895 std::map<NodeId, INode> nodes;
00896 NodesCollector nodesBuilder(nodes, Debug);
00897 for (xmlNodePtr cur = rootNode->children; cur; cur = cur->next)
00898 {
00899 nodesBuilder.Process(*cur);
00900 }
00901
00902 return nodes;
00903 }
00904
00905 private:
00906 void EnsureRootNodeValid(xmlNode& rootNode)
00907 {
00908 if (rootNode.type != XML_ELEMENT_NODE)
00909 {
00910 throw std::logic_error("Root element is not a node.'");
00911 }
00912 if (xmlStrcmp(rootNode.name, "address_space"))
00913 {
00914 throw std::logic_error(std::string("Invalid root element '") + (const char*)rootNode.name + std::string("'."));
00915 }
00916 std::unique_ptr<xmlChar, LibXmlFree> versionBuf(xmlGetProp(&rootNode, (const xmlChar*)"version"), LibXmlFree());
00917 const xmlChar* version = versionBuf.get();
00918 if (!version)
00919 {
00920 throw std::logic_error("Address space element has no 'version' attribute.");
00921 }
00922 if (xmlStrcmp(version, "1"))
00923 {
00924 throw std::logic_error(std::string("Unknown version '") + (const char*)version + std::string("'of address space."));
00925 }
00926 }
00927
00928 private:
00929 const bool Debug;
00930 };
00931
00932 std::map<NodeId, INode> ParseConfig(const char* configPath, bool debug)
00933 {
00934 std::unique_ptr<xmlDoc, XmlDocDeleter> doc(xmlParseFile(configPath), XmlDocDeleter());
00935 if (!doc)
00936 {
00937 throw std::logic_error(std::string("Cannot load file '") + std::string(configPath) + std::string("'"));
00938 }
00939 ConfigurationProcessor xmlConfiguration(debug);
00940 return xmlConfiguration.Process(*doc);
00941 }
00942
00943
00944 class NodesRegistrator
00945 {
00946 public:
00947 NodesRegistrator(OpcUa::NodeManagementServices& registry, bool debug)
00948 : Registry(registry)
00949 , Debug(debug)
00950 {
00951 }
00952
00953 void RegisterNodes(const std::map<NodeId, INode>& nodes)
00954 {
00955 for (const auto& node : nodes)
00956 {
00957 if (!node.second.IsExternal)
00958 {
00959 RegisterNode(node.second);
00960 }
00961 AddReferences(node.second);
00962 }
00963 }
00964
00965 private:
00966 void RegisterNode(const INode& node)
00967 {
00968
00969 for (const std::pair<AttributeId, Variant>& attr : node.Attributes)
00970 {
00971
00972 }
00973 }
00974
00975 void AddReferences(const INode& node)
00976 {
00977 for (const Reference& ref : node.References)
00978 {
00979 ReferenceDescription desc;
00980 desc.BrowseName = ref.TargetBrowseName;
00981 desc.DisplayName = ref.TargetDisplayName;
00982 desc.IsForward = ref.IsForward;
00983 desc.ReferenceTypeId = ref.Id;
00984 desc.TargetNodeClass = ref.TargetClass;
00985 desc.TargetNodeId = ref.TargetNode;
00986 desc.TargetNodeTypeDefinition = ref.TargetType;
00987
00988 }
00989 }
00990
00991 private:
00992 OpcUa::NodeManagementServices& Registry;
00993 const bool Debug;
00994 };
00995 }
00996
00997 namespace OpcUa
00998 {
00999 namespace Internal
01000 {
01001
01002 XmlAddressSpaceLoader::XmlAddressSpaceLoader(OpcUa::NodeManagementServices& registry, bool debug)
01003 : Registry(registry)
01004 , Debug(debug)
01005 {
01006 }
01007
01008 void XmlAddressSpaceLoader::Load(const char* fileName)
01009 {
01010 std::map<NodeId, INode> nodes = ParseConfig(fileName, Debug);
01011 NodesRegistrator reg(Registry, Debug);
01012 reg.RegisterNodes(nodes);
01013 }
01014
01015 }
01016 }