xml_address_space_loader.cpp
Go to the documentation of this file.
1 
11 // TODO Add EventNotifier Attribute to all requred nodes.
12 
14 #include "xml_processor.h"
15 
17 
18 #include <libxml2/libxml/xmlmemory.h>
19 #include <libxml2/libxml/parser.h>
20 
21 #include <iostream>
22 #include <map>
23 #include <stdexcept>
24 #include <sstream>
25 #include <string.h>
26 
27 namespace
28 {
29 using namespace OpcUa;
30 
31 ReferenceId GetReferenceId(const std::string & referenceName)
32 {
33  if (referenceName == "organizes")
34  { return ReferenceId::Organizes; }
35 
36  if (referenceName == "references_to")
37  { return ReferenceId::References; }
38 
39  if (referenceName == "has_child")
40  { return ReferenceId::HasChild; }
41 
42  if (referenceName == "has_event_source")
43  { return ReferenceId::HasEventSource; }
44 
45  if (referenceName == "has_modelling_rule")
47 
48  if (referenceName == "has_encoding")
49  { return ReferenceId::HasEncoding; }
50 
51  if (referenceName == "has_description")
52  { return ReferenceId::HasDescription; }
53 
54  if (referenceName == "has_type_definition")
56 
57  if (referenceName == "generates_event")
58  { return ReferenceId::GeneratesEvent; }
59 
60  if (referenceName == "aggregates")
61  { return ReferenceId::Aggregates; }
62 
63  if (referenceName == "has_subtype")
64  { return ReferenceId::HasSubtype; }
65 
66  if (referenceName == "has_property")
67  { return ReferenceId::HasProperty; }
68 
69  if (referenceName == "has_component")
70  { return ReferenceId::HasComponent; }
71 
72  if (referenceName == "has_notifier")
73  { return ReferenceId::HasNotifier; }
74 
75  if (referenceName == "has_ordered_component")
77 
78  if (referenceName == "has_model_parent")
79  { return ReferenceId::HasModelParent; }
80 
81  if (referenceName == "from_state")
82  { return ReferenceId::FromState; }
83 
84  if (referenceName == "to_state")
85  { return ReferenceId::ToState; }
86 
87  if (referenceName == "has_clause")
88  { return ReferenceId::HasCause; }
89 
90  if (referenceName == "has_effect")
91  { return ReferenceId::HasEffect; }
92 
93  if (referenceName == "has_historical_configuration")
95 
96  if (referenceName == "has_historical_event_configuration")
98 
99  if (referenceName == "has_substate_machine")
101 
102  if (referenceName == "has_event_history")
103  { return ReferenceId::HasEventHistory; }
104 
105  if (referenceName == "always_generates_event")
107 
108  if (referenceName == "has_true_substate")
109  { return ReferenceId::HasTrueSubState; }
110 
111  if (referenceName == "has_false_substate")
113 
114  if (referenceName == "has_condition")
115  { return ReferenceId::HasCondition; }
116 
117  if (referenceName == "non_hierarchical_references")
119 
120  if (referenceName == "hierarchical_references")
122 
123  if (referenceName == "has_cause")
124  { return ReferenceId::HasCause; }
125 
126  if (referenceName == "has_sub_state_machine")
128 
129  if (referenceName == "has_true_sub_state")
130  { return ReferenceId::HasTrueSubState; }
131 
132  if (referenceName == "has_false_sub_state")
134 
135  throw std::logic_error(std::string("Unknown reference name '") + referenceName + std::string(referenceName));
136 }
137 
138 struct Reference
139 {
140  ReferenceId Id;
141  NodeId TargetNode;
142  bool IsForward;
143  NodeClass TargetClass;
144  QualifiedName TargetBrowseName;
145  LocalizedText TargetDisplayName;
146  NodeId TargetType;
147 
148  Reference()
149  : Id(ReferenceId::Unknown)
150  , IsForward(true)
151  , TargetClass(NodeClass::Unspecified)
152  {
153  }
154 };
155 
156 struct Attribute
157 {
158  AttributeId Id;
159  Variant Value;
160  Attribute()
161  : Id(AttributeId::Unknown)
162  {
163  }
164 
165  Attribute(AttributeId id, Variant value)
166  : Id(id)
167  , Value(value)
168  {
169  }
170 };
171 
172 struct INode
173 {
174  NodeId Id;
175  std::map<AttributeId, Variant> Attributes;
176  std::vector<Reference> References;
177  bool IsExternal;
178 
179  INode()
180  : IsExternal(false)
181  {
182  }
183 };
184 
185 struct XmlDocDeleter
186 {
187  void operator()(xmlDocPtr doc)
188  {
189  xmlFreeDoc(doc);
190  }
191 };
192 
193 struct LibXmlFree
194 {
195  void operator()(void * ptr)
196  {
197  xmlFree(ptr);
198  }
199 };
200 
201 int xmlStrcmp(const xmlChar * xmlStr, const char * str)
202 {
203  return ::xmlStrcmp(xmlStr, (const xmlChar *)str);
204 }
205 
206 bool IsXmlNode(const xmlNode & node)
207 {
208  return node.type == XML_ELEMENT_NODE;
209 }
210 
211 bool IsXmlNode(const xmlNode & node, const char * name, bool debug = false)
212 {
213  if (node.type != XML_ELEMENT_NODE)
214  {
215  return false;
216  }
217 
218  if (xmlStrcmp(node.name, name))
219  {
220  return false;
221  }
222 
223  return true;
224 }
225 
226 std::string GetNodeName(xmlNode & node)
227 {
228  if (!node.name)
229  {
230  return std::string();
231  }
232 
233  return (const char *)node.name;
234 }
235 
236 std::string GetProperty(xmlNode & node, const char * propName)
237 {
238  std::unique_ptr<xmlChar, LibXmlFree> attrValue(xmlGetProp(&node, (const xmlChar *)propName), LibXmlFree());
239  const xmlChar * propValue = attrValue.get();
240 
241  if (propValue)
242  {
243  return (const char *)propValue;
244  }
245 
246  return std::string();
247 }
248 
249 std::string GetNodeValue(xmlNode & node)
250 {
251  const std::string nodeValue = GetProperty(node, "value");
252 
253  if (!nodeValue.empty())
254  {
255  return nodeValue;
256  }
257 
258  std::unique_ptr<xmlChar, LibXmlFree> content(xmlNodeGetContent(&node));
259 
260  if (!content)
261  {
262  return std::string();
263  }
264 
265  return (const char *)content.get();
266 }
267 
268 ObjectId GetObjectIdOfType(const std::string & nodeValue)
269 {
270  if (nodeValue == "bool")
271  {
272  return ObjectId::Boolean;
273  }
274 
275  if (nodeValue == "sbyte")
276  {
277  return ObjectId::SByte;
278  }
279 
280  if (nodeValue == "byte")
281  {
282  return ObjectId::Byte;
283  }
284 
285  if (nodeValue == "int16")
286  {
287  return ObjectId::Int16;
288  }
289 
290  if (nodeValue == "uint16")
291  {
292  return ObjectId::UInt16;
293  }
294 
295  if (nodeValue == "int32")
296  {
297  return ObjectId::Int32;
298  }
299 
300  if (nodeValue == "uint32")
301  {
302  return ObjectId::UInt32;
303  }
304 
305  if (nodeValue == "string")
306  {
307  return ObjectId::String;
308  }
309 
310  if (nodeValue == "enum")
311  {
312  return ObjectId::Enumeration;
313  }
314 
315  if (nodeValue == "int")
316  {
317  return ObjectId::Integer;
318  }
319 
320  if (nodeValue == "byte_string")
321  {
322  return ObjectId::ByteString;
323  }
324 
325  if (nodeValue == "guid")
326  {
327  return ObjectId::Guid;
328  }
329 
330  if (nodeValue == "date_time")
331  {
332  return ObjectId::DateTime;
333  }
334 
335  std::stringstream stream;
336  stream << "Unknown data type '" << nodeValue << "'.";
337  throw std::logic_error(stream.str());
338 }
339 
340 inline ObjectId GetObjectIdOfType(xmlNode & node)
341 {
342  return GetObjectIdOfType(GetNodeValue(node));
343 }
344 
345 inline VariantType ConvertToVariantType(ObjectId id)
346 {
347  switch (id)
348  {
349  case ObjectId::Null:
350  return VariantType::NUL;
351 
352  case ObjectId::Boolean:
353  return VariantType::BOOLEAN;
354 
355  case ObjectId::SByte:
356  return VariantType::SBYTE;
357 
358  case ObjectId::Byte:
359  return VariantType::BYTE;
360 
361  case ObjectId::Int16:
362  return VariantType::INT16;
363 
364  case ObjectId::UInt16:
365  return VariantType::UINT16;
366 
367  case ObjectId::Integer:
369  case ObjectId::Int32:
370  return VariantType::INT32;
371 
372  case ObjectId::UInt32:
373  return VariantType::UINT32;
374 
375  case ObjectId::Int64:
376  return VariantType::INT64;
377 
378  case ObjectId::UInt64:
379  return VariantType::UINT64;
380 
381  case ObjectId::Float:
382  return VariantType::FLOAT;
383 
384  case ObjectId::Double:
385  return VariantType::DOUBLE;
386 
387  case ObjectId::String:
388  return VariantType::STRING;
389 
392 
393  case ObjectId::DateTime:
394  return VariantType::DATE_TIME;
395 
396  case ObjectId::Guid:
397  return VariantType::GUId;
398 
399  default:
400  std::stringstream stream;
401  stream << "Cannot convert ObjectId '" << (unsigned)id << "' to VariantType.";
402  throw std::logic_error(stream.str());
403  }
404 }
405 
406 inline ObjectId ConvertToObjectId(VariantType type)
407 {
408  switch (type)
409  {
410  case VariantType::NUL:
411  return ObjectId::Null;
412 
414  return ObjectId::Boolean;
415 
416  case VariantType::SBYTE:
417  return ObjectId::SByte;
418 
419  case VariantType::BYTE:
420  return ObjectId::Byte;
421 
422  case VariantType::INT16:
423  return ObjectId::Int16;
424 
425  case VariantType::UINT16:
426  return ObjectId::UInt16;
427 
428  case VariantType::INT32:
429  return ObjectId::Int32;
430 
431  case VariantType::UINT32:
432  return ObjectId::UInt32;
433 
434  case VariantType::INT64:
435  return ObjectId::Int64;
436 
437  case VariantType::UINT64:
438  return ObjectId::UInt64;
439 
440  case VariantType::FLOAT:
441  return ObjectId::Float;
442 
443  case VariantType::DOUBLE:
444  return ObjectId::Double;
445 
446  case VariantType::STRING:
447  return ObjectId::String;
448 
450  return ObjectId::ByteString;
451 
453  return ObjectId::DateTime;
454 
455  case VariantType::GUId:
456  return ObjectId::Guid;
457 
458  default:
459  std::stringstream stream;
460  stream << "Cannot convert VariantType '" << (unsigned)type << "' to ObjectId.";
461  throw std::logic_error(stream.str());
462  }
463 }
464 
465 inline VariantType GetVariantType(xmlNode & node)
466 {
467  const ObjectId typeId = GetObjectIdOfType(GetProperty(node, "type"));
468  return ConvertToVariantType(typeId);
469 }
470 
471 NodeClass GetNodeClass(xmlNode & node)
472 {
473  const std::string nodeValue = GetNodeValue(node);
474 
475  if (nodeValue == "object")
476  {
477  return NodeClass::Object;
478  }
479 
480  if (nodeValue == "variable")
481  {
482  return NodeClass::Variable;
483  }
484 
485  if (nodeValue == "method")
486  {
487  return NodeClass::Method;
488  }
489 
490  if (nodeValue == "object_type")
491  {
492  return NodeClass::ObjectType;
493  }
494 
495  if (nodeValue == "variable_type")
496  {
498  }
499 
500  if (nodeValue == "reference_type")
501  {
503  }
504 
505  if (nodeValue == "data_type")
506  {
507  return NodeClass::DataType;
508  }
509 
510  if (nodeValue == "data_type")
511  {
512  return NodeClass::View;
513  }
514 
515  std::stringstream stream;
516  stream << "Unknown node class '" << nodeValue << "'. Line " << node.line << ".";
517  throw std::logic_error(stream.str());
518 }
519 
520 bool IsNumericNodeType(xmlNode & node)
521 {
522  const std::string propValue = GetProperty(node, "type");
523  return propValue == "numeric";
524 }
525 
526 uint32_t GetNamespaceIndex(xmlNode & node)
527 {
528  const std::string propValue = GetProperty(node, "ns");
529  return atoi(propValue.c_str());
530 }
531 
532 NodeId GetNodeId(xmlNode & node)
533 {
534  std::string nodeValue = GetNodeValue(node);
535 
536  if (nodeValue.empty())
537  {
538  std::stringstream stream;
539  stream << "Empty node id. Line " << node.line << ".";
540  throw std::logic_error(stream.str());
541  }
542 
543  uint32_t nsIndex = GetNamespaceIndex(node);
544 
545  if (IsNumericNodeType(node))
546  {
547  return NumericNodeId(atoi(nodeValue.c_str()), nsIndex);
548  }
549 
550  return StringNodeId(nodeValue, nsIndex);
551 }
552 
553 bool GetBool(std::string str)
554 {
555  if (!str.empty() && (str == "false" || str == "0"))
556  {
557  return false;
558  }
559 
560  return !str.empty();
561 }
562 
563 std::string GetText(xmlNode & node)
564 {
565  const std::string nodeValue = GetNodeValue(node);
566 
567  if (nodeValue.empty())
568  {
569  std::stringstream stream;
570  stream << "Empty browse name. Line " << node.line << ".";
571  throw std::logic_error(stream.str());
572  }
573 
574  return nodeValue;
575 }
576 
577 
578 QualifiedName GetQualifiedName(xmlNode & node)
579 {
580  return QualifiedName(GetNamespaceIndex(node), GetText(node));
581 }
582 
583 LocalizedText GetLocalizedText(xmlNode & node)
584 {
585  const std::string nodeValue = GetNodeValue(node);
586 
587  if (nodeValue.empty())
588  {
589  std::stringstream stream;
590  stream << "Empty browse name. Line " << node.line << ".";
591  throw std::logic_error(stream.str());
592  }
593 
594  return LocalizedText(nodeValue);
595 }
596 
597 uint32_t GetUInt32(xmlNode & node)
598 {
599  const std::string nodeValue = GetNodeValue(node);
600 
601  if (nodeValue.empty())
602  {
603  std::stringstream stream;
604  stream << "Empty opcua attribute value. Line " << node.line << ".";
605  throw std::logic_error(stream.str());
606  }
607 
608  return atoi(nodeValue.c_str());
609 }
610 
611 int32_t GetInt32(xmlNode & node)
612 {
613  const std::string nodeValue = GetNodeValue(node);
614 
615  if (nodeValue.empty())
616  {
617  std::stringstream stream;
618  stream << "Empty opcua attribute value. Line " << node.line << ".";
619  throw std::logic_error(stream.str());
620  }
621 
622  return atoi(nodeValue.c_str());
623 }
624 
625 
626 bool GetBool(xmlNode & node)
627 {
628  const std::string nodeValue = GetNodeValue(node);
629  return GetBool(nodeValue);
630 }
631 
632 Variant GetVariantValue(OpcUa::VariantType type, xmlNode & node)
633 {
634  const std::string nodeValue = GetNodeValue(node);
635 
636  switch (type)
637  {
638  case VariantType::SBYTE:
639  return Variant((int8_t)strtol(nodeValue.c_str(), nullptr, 0));
640 
641  case VariantType::BYTE:
642  return Variant((uint8_t)strtol(nodeValue.c_str(), nullptr, 0));
643 
644  case VariantType::INT16:
645  return Variant((int16_t)strtol(nodeValue.c_str(), nullptr, 0));
646 
647  case VariantType::UINT16:
648  return Variant((uint16_t)strtol(nodeValue.c_str(), nullptr, 0));
649 
650  case VariantType::INT32:
651  return Variant((int32_t)strtol(nodeValue.c_str(), nullptr, 0));
652 
653  case VariantType::UINT32:
654  return Variant((uint32_t)strtol(nodeValue.c_str(), nullptr, 0));
655 
656  case VariantType::INT64:
657  return Variant((int64_t)strtoll(nodeValue.c_str(), nullptr, 0));
658 
659  case VariantType::UINT64:
660  return Variant((int64_t)strtoll(nodeValue.c_str(), nullptr, 0));
661 
662  case VariantType::FLOAT:
663  return Variant(strtof(nodeValue.c_str(), nullptr));
664 
665  case VariantType::DOUBLE:
666  return Variant(strtod(nodeValue.c_str(), nullptr));
667 
668  // TODO check for other types.
669  case VariantType::NUL:
670  return Variant();
671 
672  case VariantType::STRING:
673  default:
674  break;
675  }
676 
677  return Variant(nodeValue);
678 }
679 
680 OpcUa::AttributeId GetAttributeId(xmlNode & node)
681 {
682  if (IsXmlNode(node, "id"))
683  { return AttributeId::NodeId; }
684 
685  else if (IsXmlNode(node, "class"))
686  { return AttributeId::NodeClass; }
687 
688  else if (IsXmlNode(node, "browse_name"))
689  { return AttributeId::BrowseName; }
690 
691  else if (IsXmlNode(node, "display_name"))
692  { return AttributeId::DisplayName; }
693 
694  else if (IsXmlNode(node, "description"))
695  { return AttributeId::Description; }
696 
697  else if (IsXmlNode(node, "write_mask"))
698  { return AttributeId::WriteMask; }
699 
700  else if (IsXmlNode(node, "user_write_mask"))
701  { return AttributeId::UserWriteMask; }
702 
703  else if (IsXmlNode(node, "is_abstract"))
704  { return AttributeId::IsAbstract; }
705 
706  else if (IsXmlNode(node, "symmetric"))
707  { return AttributeId::Symmetric; }
708 
709  else if (IsXmlNode(node, "inverse_name"))
710  { return AttributeId::InverseName; }
711 
712  else if (IsXmlNode(node, "contains_no_loops"))
713  { return AttributeId::ContainsNoLoops; }
714 
715  else if (IsXmlNode(node, "event_notifier"))
716  { return AttributeId::EventNotifier; }
717 
718  else if (IsXmlNode(node, "value"))
719  { return AttributeId::Value; }
720 
721  else if (IsXmlNode(node, "value_rank"))
722  { return AttributeId::ValueRank; }
723 
724  else if (IsXmlNode(node, "data_type"))
725  { return AttributeId::DataType; }
726 
727  else if (IsXmlNode(node, "array_dimensions"))
728  { return AttributeId::ArrayDimensions; }
729 
730  else if (IsXmlNode(node, "access_level"))
731  { return AttributeId::AccessLevel; }
732 
733  else if (IsXmlNode(node, "user_access_level"))
734  { return AttributeId::UserAccessLevel; }
735 
736  else if (IsXmlNode(node, "minimum_sampling_interval"))
738 
739  else if (IsXmlNode(node, "historizing"))
740  { return AttributeId::Historizing; }
741 
742  else if (IsXmlNode(node, "executable"))
743  { return AttributeId::Executable; }
744 
745  else if (IsXmlNode(node, "user_executable"))
746  { return AttributeId::UserExecutable; }
747 
748  return AttributeId::Unknown;
749 }
750 
751 Variant GetAttributeValue(OpcUa::AttributeId id, xmlNode & node)
752 {
753  switch (id)
754  {
755  case AttributeId::NodeId:
756  return Variant(GetNodeId(node));
757 
759  return Variant((int32_t)GetNodeClass(node));
760 
762  return Variant(GetLocalizedText(node));
763 
765  return Variant(GetQualifiedName(node));
766 
769  return Variant(GetText(node));
770 
772  return Variant(std::vector<uint8_t> {0});
773 
775  return Variant(GetInt32(node));
776 
783  return Variant(GetUInt32(node));
784 
791  return Variant(GetBool(node));
792 
793  case AttributeId::Value:
794  break;
795 
797  return Variant(GetObjectIdOfType(node));
798 
799  default:
800  return Variant(GetText(node));
801  }
802 
803  const VariantType type = GetVariantType(node);
804  return GetVariantValue(type, node);
805 }
806 
807 class AttributesCollector : private Internal::XmlProcessor
808 {
809 public:
810  AttributesCollector(INode & node, bool debug)
811  : OpcUaNode(node)
812  , Debug(debug)
813  {
814  }
815 
816  virtual void Process(xmlNode & node)
817  {
818  for (xmlNodePtr subNode = node.children; subNode; subNode = subNode->next)
819  {
820  if (!IsXmlNode(*subNode))
821  {
822  continue;
823  }
824 
825  const AttributeId attribute = GetAttributeId(*subNode);
826 
827  if (attribute == AttributeId::NodeId)
828  {
829  OpcUaNode.Id = GetNodeId(*subNode);
830  continue;
831  }
832 
833  const Variant value = GetAttributeValue(attribute, *subNode);
834  AddAttribute(attribute, value);
835  }
836 
837  // If tag 'data_type' is absent in the xml then need to add data type which will be based on type of value.
838  if (!HasAttribute(AttributeId::DataType) && HasAttribute(AttributeId::Value))
839  {
840  AddAttribute(AttributeId::DataType, GetDataType(AttributeId::Value));
841  }
842  }
843 
844 private:
845  bool IsAttributes(const xmlNode & node) const
846  {
847  return IsXmlNode(node, "attributes");
848  }
849 
850  template <typename T>
851  void AddAttribute(AttributeId attr, const T & value)
852  {
853  OpcUaNode.Attributes.insert(std::make_pair(attr, Variant(value)));
854  }
855 
856  bool HasAttribute(AttributeId attr) const
857  {
858  return OpcUaNode.Attributes.find(AttributeId::DataType) != OpcUaNode.Attributes.end();
859  }
860 
861  ObjectId GetDataType(AttributeId attr) const
862  {
863  auto attrPos = OpcUaNode.Attributes.find(attr);
864 
865  if (attrPos == OpcUaNode.Attributes.end())
866  {
867  return ObjectId::Null;
868  }
869 
870  return ConvertToObjectId(attrPos->second.Type());
871  }
872 
873 private:
874  INode & OpcUaNode;
875  const bool Debug;
876 };
877 
878 class ReferencesCollector : private Internal::XmlProcessor
879 {
880 public:
881  ReferencesCollector(INode & node, bool debug)
882  : OpcUaNode(node)
883  , Debug(debug)
884  {
885  }
886 
887  virtual void Process(xmlNode & node)
888  {
889  for (xmlNodePtr refNode = node.children; refNode; refNode = refNode->next)
890  {
891  if (!IsXmlNode(*refNode))
892  {
893  continue;
894  }
895 
896  try
897  {
898  AddReferenceToNode(*refNode);
899  }
900 
901  catch (const std::exception & exc)
902  {
903  std::cerr << exc.what() << std::endl;
904  }
905  }
906  }
907 
908 private:
909  void AddReferenceToNode(xmlNode & refNode)
910  {
911  const std::string refName = GetNodeName(refNode);
912  const NodeId targetNode = GetNodeId(refNode);
913  Reference reference;
914  reference.Id = GetReferenceId(refName);
915 
916  for (xmlNodePtr subNode = refNode.children; subNode; subNode = subNode->next)
917  {
918  if (!IsXmlNode(*subNode))
919  {
920  continue;
921  }
922 
923  const std::string & nodeName = GetNodeName(*subNode);
924 
925  if (nodeName == "id")
926  {
927  reference.TargetNode = GetNodeId(*subNode);
928  }
929 
930  else if (nodeName == "class")
931  {
932  reference.TargetClass = GetNodeClass(*subNode);
933  }
934 
935  else if (nodeName == "browse_name")
936  {
937  reference.TargetBrowseName = GetQualifiedName(*subNode);
938  }
939 
940  else if (nodeName == "display_name")
941  {
942  reference.TargetDisplayName = GetLocalizedText(*subNode);
943  }
944 
945  else if (nodeName == "is_forward")
946  {
947  reference.IsForward = GetBool(GetNodeValue(*subNode));
948  }
949 
950  else if (nodeName == "type_definition")
951  {
952  reference.TargetType = GetNodeId(*subNode);
953  }
954  }
955 
956  EnsureValid(reference, refNode.line);
957  OpcUaNode.References.push_back(reference);
958  }
959 
960 private:
961  void EnsureValid(const Reference & ref, int lineNum) const
962  {
963  std::stringstream stream;
964 
965  if (ref.Id == ReferenceId::Unknown)
966  {
967  stream << "Unknown reference type. line" << lineNum << ".";
968  throw std::logic_error(stream.str());
969  }
970 
971  if (ref.TargetNode == NodeId())
972  {
973  stream << "Empty target node Id. line" << lineNum << ".";
974  throw std::logic_error(stream.str());
975  }
976  }
977 
978 private:
979  INode & OpcUaNode;
980  const bool Debug;
981 };
982 
983 class NodesCollector : private Internal::XmlProcessor
984 {
985 public:
986  NodesCollector(std::map<NodeId, INode> & nodes, bool debug)
987  : Nodes(nodes)
988  , Debug(debug)
989  {
990  }
991 
992  virtual void Process(xmlNode & node)
993  {
994  if (!IsXmlNode(node))
995  {
996  return;
997  }
998 
999  INode opcuaNode;
1000 
1001  if (IsXmlNode(node, "node"))
1002  {
1003  opcuaNode.IsExternal = false;
1004  }
1005 
1006  else if (IsXmlNode(node, "external"))
1007  {
1008  opcuaNode.IsExternal = true;
1009  }
1010 
1011  else
1012  {
1013  if (Debug)
1014  {
1015  std::cerr << "Unknown node '" << node.name << "' at line " << node.line << "." << std::endl;
1016  }
1017 
1018  return;
1019  }
1020 
1021  FillNode(node, opcuaNode);
1022  EnsureNodeIsValid(opcuaNode, node);
1023  Nodes.insert(std::make_pair(opcuaNode.Id, opcuaNode));
1024  }
1025 
1026 private:
1027  void EnsureNodeIsValid(const INode & opcuaNode, const xmlNode & node) const
1028  {
1029  if (opcuaNode.Id == NodeId())
1030  {
1031  std::stringstream stream;
1032  stream << "INode at line '" << node.line << "' has no Id.";
1033  throw std::logic_error(stream.str());
1034  }
1035  }
1036 
1037  void FillNode(const xmlNode & node, INode & opcuaNode) const
1038  {
1039  AttributesCollector attributeCollector(opcuaNode, Debug);
1040  ReferencesCollector referencCollector(opcuaNode, Debug);
1041 
1042  for (xmlNodePtr subNode = node.children; subNode; subNode = subNode->next)
1043  {
1044  if (IsXmlNode(*subNode, "attributes"))
1045  {
1046  attributeCollector.Process(*subNode);
1047  continue;
1048  }
1049 
1050  else if (IsXmlNode(*subNode, "references"))
1051  {
1052  referencCollector.Process(*subNode);
1053  }
1054 
1055  else if (Debug)
1056  {
1057  std::cerr << "Unknown node '" << subNode->name << "' at line " << subNode->line << "." << std::endl;
1058  }
1059  }
1060  }
1061 
1062 private:
1063  std::map<NodeId, INode> & Nodes;
1064  const bool Debug;
1065 };
1066 
1067 
1068 class ConfigurationProcessor
1069 {
1070 public:
1071  ConfigurationProcessor(bool debug)
1072  : Debug(debug)
1073  {
1074 
1075  }
1076 
1077  std::map<NodeId, INode> Process(xmlDoc & doc)
1078  {
1079  xmlNodePtr rootNode = xmlDocGetRootElement(&doc);
1080  EnsureRootNodeValid(*rootNode);
1081 
1082  std::map<NodeId, INode> nodes;
1083  NodesCollector nodesBuilder(nodes, Debug);
1084 
1085  for (xmlNodePtr cur = rootNode->children; cur; cur = cur->next)
1086  {
1087  nodesBuilder.Process(*cur);
1088  }
1089 
1090  return nodes;
1091  }
1092 
1093 private:
1094  void EnsureRootNodeValid(xmlNode & rootNode)
1095  {
1096  if (rootNode.type != XML_ELEMENT_NODE)
1097  {
1098  throw std::logic_error("Root element is not a node.'");
1099  }
1100 
1101  if (xmlStrcmp(rootNode.name, "address_space"))
1102  {
1103  throw std::logic_error(std::string("Invalid root element '") + (const char *)rootNode.name + std::string("'."));
1104  }
1105 
1106  std::unique_ptr<xmlChar, LibXmlFree> versionBuf(xmlGetProp(&rootNode, (const xmlChar *)"version"), LibXmlFree());
1107  const xmlChar * version = versionBuf.get();
1108 
1109  if (!version)
1110  {
1111  throw std::logic_error("Address space element has no 'version' attribute.");
1112  }
1113 
1114  if (xmlStrcmp(version, "1"))
1115  {
1116  throw std::logic_error(std::string("Unknown version '") + (const char *)version + std::string("'of address space."));
1117  }
1118  }
1119 
1120 private:
1121  const bool Debug;
1122 };
1123 
1124 std::map<NodeId, INode> ParseConfig(const char * configPath, bool debug)
1125 {
1126  std::unique_ptr<xmlDoc, XmlDocDeleter> doc(xmlParseFile(configPath), XmlDocDeleter());
1127 
1128  if (!doc)
1129  {
1130  throw std::logic_error(std::string("Cannot load file '") + std::string(configPath) + std::string("'"));
1131  }
1132 
1133  ConfigurationProcessor xmlConfiguration(debug);
1134  return xmlConfiguration.Process(*doc);
1135 }
1136 
1137 
1138 class NodesRegistrator
1139 {
1140 public:
1141  NodesRegistrator(OpcUa::NodeManagementServices & registry, bool debug)
1142  : Registry(registry)
1143  , Debug(debug)
1144  {
1145  }
1146 
1147  void RegisterNodes(const std::map<NodeId, INode> & nodes)
1148  {
1149  for (const auto & node : nodes)
1150  {
1151  if (!node.second.IsExternal)
1152  {
1153  RegisterNode(node.second);
1154  }
1155 
1156  AddReferences(node.second);
1157  }
1158  }
1159 
1160 private:
1161  void RegisterNode(const INode & node)
1162  {
1163  //Registry.AddAttribute(node.Id, AttributeId::NodeId, Variant(node.Id));
1164  for (const std::pair<AttributeId, Variant> & attr : node.Attributes)
1165  {
1166  //Registry.AddAttribute(node.Id, attr.first, attr.second);
1167  }
1168  }
1169 
1170  void AddReferences(const INode & node)
1171  {
1172  for (const Reference & ref : node.References)
1173  {
1174  ReferenceDescription desc;
1175  desc.BrowseName = ref.TargetBrowseName;
1176  desc.DisplayName = ref.TargetDisplayName;
1177  desc.IsForward = ref.IsForward;
1178  desc.ReferenceTypeId = ref.Id;
1179  desc.TargetNodeClass = ref.TargetClass;
1180  desc.TargetNodeId = ref.TargetNode;
1181  desc.TargetNodeTypeDefinition = ref.TargetType;
1182  //Registry.AddReference(node.Id, desc);
1183  }
1184  }
1185 
1186 private:
1187  OpcUa::NodeManagementServices & Registry;
1188  const bool Debug;
1189 };
1190 } // namespace
1191 
1192 namespace OpcUa
1193 {
1194 namespace Internal
1195 {
1196 
1197 XmlAddressSpaceLoader::XmlAddressSpaceLoader(OpcUa::NodeManagementServices & registry, bool debug)
1198  : Registry(registry)
1199  , Debug(debug)
1200 {
1201 }
1202 
1203 void XmlAddressSpaceLoader::Load(const char * fileName)
1204 {
1205  std::map<NodeId, INode> nodes = ParseConfig(fileName, Debug);
1206  NodesRegistrator reg(Registry, Debug);
1207  reg.RegisterNodes(nodes);
1208 }
1209 
1210 } // namespace Internal
1211 } // namespace OpcUa
registry_t< std::mutex > registry
Definition: registry.h:211
name
Definition: setup.py:38
VariantType
Definition: variant.h:27
Debug
ObjectId
Definition: object_ids.h:12
OPC UA Address space part. GNU LGPL.
version
Definition: setup.py:42
NodeClass
Definition: enums.h:39
NodeId StringNodeId(std::string value, uint16_t namespaceIndex=0)
Definition: nodeid.h:227
NodeId NumericNodeId(uint32_t value, uint16_t namespaceIndex=0)
Definition: nodeid.h:218
OpcUa::NodeManagementServices & Registry


ros_opcua_impl_freeopcua
Author(s): Denis Štogl
autogenerated on Tue Jan 19 2021 03:12:08