/* Copyright (c) 1996-2020 The OPC Foundation. All rights reserved. The source code in this file is covered under a dual-license scenario: - RCL: for OPC Foundation members in good-standing - GPL V2: everybody else RCL license terms accompanied with this source code. See http://opcfoundation.org/License/RCL/1.00/ GNU General Public License as published by the Free Software Foundation; version 2 of the License are accompanied with this source code. See http://opcfoundation.org/License/GPLv2 This source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Xml; using System.Xml.Serialization; namespace Opc.Ua.Export { /// /// A set of nodes in an address space. /// public partial class UANodeSet { #region Constructors /// /// Creates an empty nodeset. /// public UANodeSet() { } /// /// Loads a nodeset from a stream. /// /// The input stream. /// The set of nodes public static UANodeSet Read(Stream istrm) { StreamReader reader = new StreamReader(istrm); try { XmlSerializer serializer = new XmlSerializer(typeof(UANodeSet)); return serializer.Deserialize(reader) as UANodeSet; } finally { reader.Dispose(); } } /// /// Write a nodeset to a stream. /// /// The input stream. public void Write(Stream istrm) { StreamWriter writer = new StreamWriter(istrm, Encoding.UTF8); try { XmlSerializer serializer = new XmlSerializer(typeof(UANodeSet)); serializer.Serialize(writer, this); } finally { writer.Flush(); writer.Dispose(); } } #endregion #region Public Methods /// /// Adds an alias to the node set. /// public void AddAlias(ISystemContext context, string alias, Opc.Ua.NodeId nodeId) { int count = 1; if (this.Aliases != null) { for (int ii = 0; ii < this.Aliases.Length; ii++) { if (this.Aliases[ii].Alias == alias) { this.Aliases[ii].Value = Export(nodeId, context.NamespaceUris); return; } } count += this.Aliases.Length; } NodeIdAlias[] aliases = new NodeIdAlias[count]; if (this.Aliases != null) { Array.Copy(this.Aliases, aliases, this.Aliases.Length); } aliases[count-1] = new NodeIdAlias() { Alias = alias, Value = Export(nodeId, context.NamespaceUris) }; this.Aliases = aliases; } /// /// Imports a node from the set. /// public void Import(ISystemContext context, NodeStateCollection nodes) { for (int ii = 0; ii < this.Items.Length; ii++) { UANode node = this.Items[ii]; NodeState importedNode = Import(context, node); nodes.Add(importedNode); } } /// /// Adds a node to the set. /// public void Export(ISystemContext context, NodeState node, bool outputRedundantNames =true) { if (node == null) throw new ArgumentNullException(nameof(node)); if (Opc.Ua.NodeId.IsNull(node.NodeId)) { throw new ArgumentException("A non-null NodeId must be specified."); } UANode exportedNode = null; switch (node.NodeClass) { case NodeClass.Object: { BaseObjectState o = (BaseObjectState)node; UAObject value = new UAObject(); value.EventNotifier = o.EventNotifier; if (o.Parent != null) { value.ParentNodeId = ExportAlias(o.Parent.NodeId, context.NamespaceUris); } exportedNode = value; break; } case NodeClass.Variable: { BaseVariableState o = (BaseVariableState)node; UAVariable value = new UAVariable(); value.DataType = ExportAlias(o.DataType, context.NamespaceUris); value.ValueRank = o.ValueRank; value.ArrayDimensions = Export(o.ArrayDimensions); value.AccessLevel = o.AccessLevelEx; value.MinimumSamplingInterval = o.MinimumSamplingInterval; value.Historizing = o.Historizing; if (o.Parent != null) { value.ParentNodeId = ExportAlias(o.Parent.NodeId, context.NamespaceUris); } if (o.Value != null) { XmlEncoder encoder = CreateEncoder(context); Variant variant = new Variant(o.Value); encoder.WriteVariantContents(variant.Value, variant.TypeInfo); XmlDocument document = new XmlDocument(); document.InnerXml = encoder.Close(); value.Value = document.DocumentElement; } exportedNode = value; break; } case NodeClass.Method: { MethodState o = (MethodState)node; UAMethod value = new UAMethod(); value.Executable = o.Executable; if (o.TypeDefinitionId != null && !o.TypeDefinitionId.IsNullNodeId && o.TypeDefinitionId != o.NodeId) { value.MethodDeclarationId = Export(o.TypeDefinitionId, context.NamespaceUris); } if (o.Parent != null) { value.ParentNodeId = ExportAlias(o.Parent.NodeId, context.NamespaceUris); } exportedNode = value; break; } case NodeClass.View: { ViewState o = (ViewState)node; UAView value = new UAView(); value.ContainsNoLoops = o.ContainsNoLoops; exportedNode = value; break; } case NodeClass.ObjectType: { BaseObjectTypeState o = (BaseObjectTypeState)node; UAObjectType value = new UAObjectType(); value.IsAbstract = o.IsAbstract; exportedNode = value; break; } case NodeClass.VariableType: { BaseVariableTypeState o = (BaseVariableTypeState)node; UAVariableType value = new UAVariableType(); value.IsAbstract = o.IsAbstract; value.DataType = ExportAlias(o.DataType, context.NamespaceUris); value.ValueRank = o.ValueRank; value.ArrayDimensions = Export(o.ArrayDimensions); if (o.Value != null) { XmlEncoder encoder = CreateEncoder(context); Variant variant = new Variant(o.Value); encoder.WriteVariantContents(variant.Value, variant.TypeInfo); XmlDocument document = new XmlDocument(); document.InnerXml = encoder.Close(); value.Value = document.DocumentElement; } exportedNode = value; break; } case NodeClass.DataType: { DataTypeState o = (DataTypeState)node; UADataType value = new UADataType(); value.IsAbstract = o.IsAbstract; value.Definition = Export(o, o.DataTypeDefinition, context.NamespaceUris, outputRedundantNames); value.Purpose = o.Purpose; exportedNode = value; break; } case NodeClass.ReferenceType: { ReferenceTypeState o = (ReferenceTypeState)node; UAReferenceType value = new UAReferenceType(); value.IsAbstract = o.IsAbstract; if (!Opc.Ua.LocalizedText.IsNullOrEmpty(o.InverseName)) { value.InverseName = Export(new Opc.Ua.LocalizedText[] { o.InverseName }); } value.Symmetric = o.Symmetric; exportedNode = value; break; } } exportedNode.NodeId = Export(node.NodeId, context.NamespaceUris); exportedNode.BrowseName = Export(node.BrowseName, context.NamespaceUris); if (outputRedundantNames || node.DisplayName.Text != node.BrowseName.Name) { exportedNode.DisplayName = Export(new Opc.Ua.LocalizedText[] { node.DisplayName }); } else { exportedNode.DisplayName = null; } if (node.Description != null && !String.IsNullOrEmpty(node.Description.Text)) { exportedNode.Description = Export(new Opc.Ua.LocalizedText[] { node.Description }); } else { exportedNode.Description = new LocalizedText[0]; } exportedNode.Documentation = node.NodeSetDocumentation; exportedNode.Category = (node.Categories != null && node.Categories.Count > 0) ? new List(node.Categories).ToArray() : null; exportedNode.ReleaseStatus = node.ReleaseStatus; exportedNode.WriteMask = (uint)node.WriteMask; exportedNode.UserWriteMask = (uint)node.UserWriteMask; exportedNode.Extensions = node.Extensions; if (!String.IsNullOrEmpty(node.SymbolicName) && node.SymbolicName != node.BrowseName.Name) { exportedNode.SymbolicName = node.SymbolicName; } // export references. INodeBrowser browser = node.CreateBrowser(context, null, null, true, BrowseDirection.Both, null, null, true); List exportedReferences = new List(); IReference reference = browser.Next(); while (reference != null) { if (node.NodeClass == NodeClass.Method) { if (!reference.IsInverse && reference.ReferenceTypeId == ReferenceTypeIds.HasTypeDefinition) { reference = browser.Next(); continue; } } Reference exportedReference = new Reference(); exportedReference.ReferenceType = ExportAlias(reference.ReferenceTypeId, context.NamespaceUris); exportedReference.IsForward = !reference.IsInverse; exportedReference.Value = Export(reference.TargetId, context.NamespaceUris, context.ServerUris); exportedReferences.Add(exportedReference); reference = browser.Next(); } exportedNode.References = exportedReferences.ToArray(); // add node to list. UANode[] nodes = null; int count = 1; if (this.Items == null) { nodes = new UANode[count]; } else { count += this.Items.Length; nodes = new UANode[count]; Array.Copy(this.Items, nodes, this.Items.Length); } nodes[count-1] = exportedNode; this.Items = nodes; // recusively process children. List children = new List(); node.GetChildren(context, children); for (int ii = 0; ii < children.Count; ii++) { Export(context, children[ii], outputRedundantNames); } } #endregion #region Private Members /// /// Creates an encoder to save Variant values. /// private XmlEncoder CreateEncoder(ISystemContext context) { ServiceMessageContext messageContext = new ServiceMessageContext(); messageContext.NamespaceUris = context.NamespaceUris; messageContext.ServerUris = context.ServerUris; messageContext.Factory = context.EncodeableFactory; XmlEncoder encoder = new XmlEncoder(messageContext); NamespaceTable namespaceUris = new NamespaceTable(); if (NamespaceUris != null) { for (int ii = 0; ii < NamespaceUris.Length; ii++) { namespaceUris.Append(NamespaceUris[ii]); } } StringTable serverUris = new StringTable(); if (ServerUris != null) { serverUris.Append(context.ServerUris.GetString(0)); for (int ii = 0; ii < ServerUris.Length; ii++) { serverUris.Append(ServerUris[ii]); } } encoder.SetMappingTables(namespaceUris, serverUris); return encoder; } /// /// Creates an decoder to restore Variant values. /// private XmlDecoder CreateDecoder(ISystemContext context, XmlElement source) { ServiceMessageContext messageContext = new ServiceMessageContext(); messageContext.NamespaceUris = context.NamespaceUris; messageContext.ServerUris = context.ServerUris; messageContext.Factory = context.EncodeableFactory; XmlDecoder decoder = new XmlDecoder(source, messageContext); NamespaceTable namespaceUris = new NamespaceTable(); if (NamespaceUris != null) { for (int ii = 0; ii < NamespaceUris.Length; ii++) { namespaceUris.Append(NamespaceUris[ii]); } } StringTable serverUris = new StringTable(); if (ServerUris != null) { serverUris.Append(context.ServerUris.GetString(0)); for (int ii = 0; ii < ServerUris.Length; ii++) { serverUris.Append(ServerUris[ii]); } } decoder.SetMappingTables(namespaceUris, serverUris); return decoder; } /// /// Imports a node from the set. /// private NodeState Import(ISystemContext context, UANode node) { NodeState importedNode = null; NodeClass nodeClass = NodeClass.Unspecified; if (node is UAObject) nodeClass = NodeClass.Object; else if (node is UAVariable) nodeClass = NodeClass.Variable; else if (node is UAMethod) nodeClass = NodeClass.Method; else if (node is UAObjectType) nodeClass = NodeClass.ObjectType; else if (node is UAVariableType) nodeClass = NodeClass.VariableType; else if (node is UADataType) nodeClass = NodeClass.DataType; else if (node is UAReferenceType) nodeClass = NodeClass.ReferenceType; else if (node is UAView) nodeClass = NodeClass.View; switch (nodeClass) { case NodeClass.Object: { UAObject o = (UAObject)node; BaseObjectState value = new BaseObjectState(null); value.EventNotifier = o.EventNotifier; importedNode = value; break; } case NodeClass.Variable: { UAVariable o = (UAVariable)node; NodeId typeDefinitionId = null; if (node.References != null) { for (int ii = 0; ii < node.References.Length; ii++) { Opc.Ua.NodeId referenceTypeId = ImportNodeId(node.References[ii].ReferenceType, context.NamespaceUris, true); bool isInverse = !node.References[ii].IsForward; Opc.Ua.ExpandedNodeId targetId = ImportExpandedNodeId(node.References[ii].Value, context.NamespaceUris, context.ServerUris); if (referenceTypeId == ReferenceTypeIds.HasTypeDefinition && !isInverse) { typeDefinitionId = Opc.Ua.ExpandedNodeId.ToNodeId(targetId, context.NamespaceUris); break; } } } BaseVariableState value = null; if (typeDefinitionId == Opc.Ua.VariableTypeIds.PropertyType) { value = new PropertyState(null); } else { value = new BaseDataVariableState(null); } value.DataType = ImportNodeId(o.DataType, context.NamespaceUris, true); value.ValueRank = o.ValueRank; value.ArrayDimensions = ImportArrayDimensions(o.ArrayDimensions); value.AccessLevelEx = o.AccessLevel; value.UserAccessLevel = (byte)(o.AccessLevel & 0xFF); value.MinimumSamplingInterval = o.MinimumSamplingInterval; value.Historizing = o.Historizing; if (o.Value != null) { XmlDecoder decoder = CreateDecoder(context, o.Value); TypeInfo typeInfo = null; value.Value = decoder.ReadVariantContents(out typeInfo); decoder.Close(); } importedNode = value; break; } case NodeClass.Method: { UAMethod o = (UAMethod)node; MethodState value = new MethodState(null); value.Executable = o.Executable; value.UserExecutable = o.Executable; value.TypeDefinitionId = ImportNodeId(o.MethodDeclarationId, context.NamespaceUris, true); importedNode = value; break; } case NodeClass.View: { UAView o = (UAView)node; ViewState value = new ViewState(); value.ContainsNoLoops = o.ContainsNoLoops; importedNode = value; break; } case NodeClass.ObjectType: { UAObjectType o = (UAObjectType)node; BaseObjectTypeState value = new BaseObjectTypeState(); value.IsAbstract = o.IsAbstract; importedNode = value; break; } case NodeClass.VariableType: { UAVariableType o = (UAVariableType)node; BaseVariableTypeState value = new BaseDataVariableTypeState(); value.IsAbstract = o.IsAbstract; value.DataType = ImportNodeId(o.DataType, context.NamespaceUris, true); value.ValueRank = o.ValueRank; value.ArrayDimensions = ImportArrayDimensions(o.ArrayDimensions); if (o.Value != null) { XmlDecoder decoder = CreateDecoder(context, o.Value); TypeInfo typeInfo = null; value.Value = decoder.ReadVariantContents(out typeInfo); decoder.Close(); } importedNode = value; break; } case NodeClass.DataType: { UADataType o = (UADataType)node; DataTypeState value = new DataTypeState(); value.IsAbstract = o.IsAbstract; Opc.Ua.DataTypeDefinition dataTypeDefinition = Import(o, o.Definition, context.NamespaceUris); value.DataTypeDefinition = new ExtensionObject(dataTypeDefinition); value.Purpose = o.Purpose; value.DataTypeModifier = DataTypeModifier.None; if (o.Definition != null) { if (o.Definition.IsOptionSet) { value.DataTypeModifier = DataTypeModifier.OptionSet; } else if (o.Definition.IsUnion) { value.DataTypeModifier = DataTypeModifier.Union; } } importedNode = value; break; } case NodeClass.ReferenceType: { UAReferenceType o = (UAReferenceType)node; ReferenceTypeState value = new ReferenceTypeState(); value.IsAbstract = o.IsAbstract; value.InverseName = Import(o.InverseName); value.Symmetric = o.Symmetric; importedNode = value; break; } } importedNode.NodeId = ImportNodeId(node.NodeId, context.NamespaceUris, false); importedNode.BrowseName = ImportQualifiedName(node.BrowseName, context.NamespaceUris); importedNode.DisplayName = Import(node.DisplayName); if (importedNode.DisplayName == null) { importedNode.DisplayName = new Ua.LocalizedText(importedNode.BrowseName.Name); } importedNode.Description = Import(node.Description); importedNode.NodeSetDocumentation = node.Documentation; importedNode.Categories = (node.Category != null && node.Category.Length > 0) ? node.Category : null; importedNode.ReleaseStatus = node.ReleaseStatus; importedNode.WriteMask = (AttributeWriteMask)node.WriteMask; importedNode.UserWriteMask = (AttributeWriteMask)node.UserWriteMask; importedNode.Extensions = node.Extensions; if (!String.IsNullOrEmpty(node.SymbolicName)) { importedNode.SymbolicName = node.SymbolicName; } if (node.References != null) { BaseInstanceState instance = importedNode as BaseInstanceState; BaseTypeState type = importedNode as BaseTypeState; for (int ii = 0; ii < node.References.Length; ii++) { Opc.Ua.NodeId referenceTypeId = ImportNodeId(node.References[ii].ReferenceType, context.NamespaceUris, true); bool isInverse = !node.References[ii].IsForward; Opc.Ua.ExpandedNodeId targetId = ImportExpandedNodeId(node.References[ii].Value, context.NamespaceUris, context.ServerUris); if (instance != null) { if (referenceTypeId == ReferenceTypeIds.HasModellingRule && !isInverse) { instance.ModellingRuleId = Opc.Ua.ExpandedNodeId.ToNodeId(targetId, context.NamespaceUris); continue; } if (referenceTypeId == ReferenceTypeIds.HasTypeDefinition && !isInverse) { instance.TypeDefinitionId = Opc.Ua.ExpandedNodeId.ToNodeId(targetId, context.NamespaceUris); continue; } } if (type != null) { if (referenceTypeId == ReferenceTypeIds.HasSubtype && isInverse) { type.SuperTypeId = Opc.Ua.ExpandedNodeId.ToNodeId(targetId, context.NamespaceUris); continue; } } importedNode.AddReference(referenceTypeId, isInverse, targetId); } } string parentNodeId = (node as UAInstance)?.ParentNodeId; if (!String.IsNullOrEmpty(parentNodeId)) { // set parent NodeId in Handle property. importedNode.Handle = ImportNodeId(parentNodeId, context.NamespaceUris, true); } return importedNode; } /// /// Exports a NodeId as an alias. /// private string ExportAlias(Opc.Ua.NodeId source, NamespaceTable namespaceUris) { string nodeId = Export(source, namespaceUris); if (!String.IsNullOrEmpty(nodeId)) { if (this.Aliases != null) { for (int ii = 0; ii < this.Aliases.Length; ii++) { if (this.Aliases[ii].Value == nodeId) { return this.Aliases[ii].Alias; } } } } return nodeId; } /// /// Exports a NodeId /// private string Export(Opc.Ua.NodeId source, NamespaceTable namespaceUris) { if (Opc.Ua.NodeId.IsNull(source)) { return String.Empty; } if (source.NamespaceIndex > 0) { ushort namespaceIndex = ExportNamespaceIndex(source.NamespaceIndex, namespaceUris); source = new Opc.Ua.NodeId(source.Identifier, namespaceIndex); } return source.ToString(); } /// /// Imports a NodeId /// private Opc.Ua.NodeId ImportNodeId(string source, NamespaceTable namespaceUris, bool lookupAlias) { if (String.IsNullOrEmpty(source)) { return Opc.Ua.NodeId.Null; } // lookup alias. if (lookupAlias && this.Aliases != null) { for (int ii = 0; ii < this.Aliases.Length; ii++) { if (this.Aliases[ii].Alias == source) { source = this.Aliases[ii].Value; break; } } } // parse the string. Opc.Ua.NodeId nodeId = Opc.Ua.NodeId.Parse(source); if (nodeId.NamespaceIndex > 0) { ushort namespaceIndex = ImportNamespaceIndex(nodeId.NamespaceIndex, namespaceUris); nodeId = new Opc.Ua.NodeId(nodeId.Identifier, namespaceIndex); } return nodeId; } /// /// Exports a ExpandedNodeId /// private string Export(Opc.Ua.ExpandedNodeId source, NamespaceTable namespaceUris, StringTable serverUris) { if (Opc.Ua.NodeId.IsNull(source)) { return String.Empty; } if (source.ServerIndex <= 0 && source.NamespaceIndex <= 0 && String.IsNullOrEmpty(source.NamespaceUri)) { return source.ToString(); } ushort namespaceIndex = 0; if (String.IsNullOrEmpty(source.NamespaceUri)) { namespaceIndex = ExportNamespaceIndex(source.NamespaceIndex, namespaceUris); } else { namespaceIndex = ExportNamespaceUri(source.NamespaceUri, namespaceUris); } uint serverIndex = ExportServerIndex(source.ServerIndex, serverUris); source = new Opc.Ua.ExpandedNodeId(source.Identifier, namespaceIndex, null, serverIndex); return source.ToString(); } /// /// Imports a ExpandedNodeId /// private Opc.Ua.ExpandedNodeId ImportExpandedNodeId(string source, NamespaceTable namespaceUris, StringTable serverUris) { if (String.IsNullOrEmpty(source)) { return Opc.Ua.ExpandedNodeId.Null; } // lookup aliases if (this.Aliases != null) { for (int ii = 0; ii < this.Aliases.Length; ii++) { if (this.Aliases[ii].Alias == source) { source = this.Aliases[ii].Value; break; } } } // parse the node. Opc.Ua.ExpandedNodeId nodeId = Opc.Ua.ExpandedNodeId.Parse(source); if (nodeId.ServerIndex <= 0 && nodeId.NamespaceIndex <= 0 && String.IsNullOrEmpty(nodeId.NamespaceUri)) { return nodeId; } uint serverIndex = ImportServerIndex(nodeId.ServerIndex, serverUris); ushort namespaceIndex = ImportNamespaceIndex(nodeId.NamespaceIndex, namespaceUris); if (serverIndex > 0) { string namespaceUri = nodeId.NamespaceUri; if (String.IsNullOrEmpty(nodeId.NamespaceUri)) { namespaceUri = namespaceUris.GetString(namespaceIndex); } nodeId = new Opc.Ua.ExpandedNodeId(nodeId.Identifier, 0, namespaceUri, serverIndex); return nodeId; } nodeId = new Opc.Ua.ExpandedNodeId(nodeId.Identifier, namespaceIndex, null, 0); return nodeId; } /// /// Exports a QualifiedName /// private string Export(Opc.Ua.QualifiedName source, NamespaceTable namespaceUris) { if (Opc.Ua.QualifiedName.IsNull(source)) { return String.Empty; } if (source.NamespaceIndex > 0) { ushort namespaceIndex = ExportNamespaceIndex(source.NamespaceIndex, namespaceUris); source = new Opc.Ua.QualifiedName(source.Name, namespaceIndex); } return source.ToString(); } /// /// Exports a DataTypeDefinition /// private Opc.Ua.Export.DataTypeDefinition Export( DataTypeState dataType, ExtensionObject source, NamespaceTable namespaceUris, bool outputRedundantNames) { if (source == null || source.Body == null) { return null; } DataTypeDefinition definition = new DataTypeDefinition(); if (outputRedundantNames || dataType.BrowseName != null) { definition.Name = Export(dataType.BrowseName, namespaceUris); } if (dataType.BrowseName.Name != dataType.SymbolicName) { definition.SymbolicName = dataType.SymbolicName; } switch (dataType.DataTypeModifier) { case DataTypeModifier.Union: { definition.IsUnion = true; break; } case DataTypeModifier.OptionSet: { definition.IsOptionSet = true; break; } } StructureDefinition structureDefinition = source.Body as StructureDefinition; if (structureDefinition != null) { if (structureDefinition.StructureType == StructureType.Union) { definition.IsUnion = true; } if (structureDefinition.Fields != null) { List fields = new List(); for (int ii = structureDefinition.FirstExplicitFieldIndex; ii < structureDefinition.Fields.Count; ii++) { StructureField field = structureDefinition.Fields[ii]; Opc.Ua.Export.DataTypeField output = new Opc.Ua.Export.DataTypeField(); output.Name = field.Name; output.Description = Export(new Opc.Ua.LocalizedText[] { field.Description }); output.IsOptional = field.IsOptional; if (NodeId.IsNull(field.DataType)) { output.DataType = Export(DataTypeIds.BaseDataType, namespaceUris); } else { output.DataType = Export(field.DataType, namespaceUris); } output.ValueRank = field.ValueRank; fields.Add(output); } definition.Field = fields.ToArray(); } } EnumDefinition enumDefinition = source.Body as EnumDefinition; if (enumDefinition != null) { if (enumDefinition.Fields != null) { List fields = new List(); foreach (EnumField field in enumDefinition.Fields) { Opc.Ua.Export.DataTypeField output = new Opc.Ua.Export.DataTypeField(); output.Name = field.Name; if (field.DisplayName != null && output.Name != field.DisplayName.Text) { output.DisplayName = Export(new Opc.Ua.LocalizedText[] { field.DisplayName }); } else { output.DisplayName = new LocalizedText[0]; } output.Description = Export(new Opc.Ua.LocalizedText[] { field.Description }); output.ValueRank = ValueRanks.Scalar; output.Value = (int)field.Value; fields.Add(output); } definition.Field = fields.ToArray(); } } return definition; } /// /// Imports a DataTypeDefinition /// private Opc.Ua.DataTypeDefinition Import(UADataType dataType, Opc.Ua.Export.DataTypeDefinition source, NamespaceTable namespaceUris) { if (source == null) { return null; } Opc.Ua.DataTypeDefinition definition = null; if (source.Field != null) { // check if definition is for enumeration or structure. bool isStructure = Array.Exists(source.Field, delegate (DataTypeField fieldLookup) { return fieldLookup.Value == -1; }); if (isStructure) { StructureDefinition structureDefinition = new StructureDefinition(); structureDefinition.BaseDataType = ImportNodeId(source.BaseType, namespaceUris, true); if (source.IsUnion) { structureDefinition.StructureType = StructureType.Union; } if (source.Field != null) { List fields = new List(); foreach (DataTypeField field in source.Field) { if (field.IsOptional) { structureDefinition.StructureType = StructureType.StructureWithOptionalFields; } StructureField output = new StructureField(); output.Name = field.Name; output.Description = Import(field.Description); output.DataType = ImportNodeId(field.DataType, namespaceUris, true); output.ValueRank = field.ValueRank; output.IsOptional = field.IsOptional; fields.Add(output); } structureDefinition.Fields = fields.ToArray(); } definition = structureDefinition; } else { EnumDefinition enumDefinition = new EnumDefinition(); if (source.Field != null) { List fields = new List(); foreach (DataTypeField field in source.Field) { EnumField output = new EnumField(); output.Name = field.Name; output.DisplayName = Import(field.DisplayName); output.Description = Import(field.Description); output.Value = field.Value; fields.Add(output); } enumDefinition.Fields = fields.ToArray(); } definition = enumDefinition; } } return definition; } /// /// Imports a QualifiedName /// private Opc.Ua.QualifiedName ImportQualifiedName(string source, NamespaceTable namespaceUris) { if (String.IsNullOrEmpty(source)) { return Opc.Ua.QualifiedName.Null; } Opc.Ua.QualifiedName qname = Opc.Ua.QualifiedName.Parse(source); if (qname.NamespaceIndex > 0) { ushort namespaceIndex = ImportNamespaceIndex(qname.NamespaceIndex, namespaceUris); qname = new Opc.Ua.QualifiedName(qname.Name, namespaceIndex); } return qname; } /// /// Exports the array dimensions. /// private string Export(IList arrayDimensions) { if (arrayDimensions == null) { return String.Empty; } StringBuilder buffer = new StringBuilder(); for (int ii = 0; ii < arrayDimensions.Count; ii++) { if (buffer.Length > 0) { buffer.Append(','); } buffer.Append(arrayDimensions[ii]); } return buffer.ToString(); } /// /// Imports the array dimensions. /// private uint[] ImportArrayDimensions(string arrayDimensions) { if (String.IsNullOrEmpty(arrayDimensions)) { return null; } string[] fields = arrayDimensions.Split(','); uint[] dimensions = new uint[fields.Length]; for (int ii = 0; ii < fields.Length; ii++) { try { dimensions[ii] = Convert.ToUInt32(fields[ii]); } catch { dimensions[ii] = 0; } } return dimensions; } /// /// Exports localized text. /// private Opc.Ua.Export.LocalizedText[] Export(Opc.Ua.LocalizedText[] input) { if (input == null) { return null; } List output = new List(); for (int ii = 0; ii < input.Length; ii++) { if (input[ii] != null) { Opc.Ua.Export.LocalizedText text = new LocalizedText(); text.Locale = input[ii].Locale; text.Value = input[ii].Text; output.Add(text); } } return output.ToArray(); } /// /// Exports localized text. /// private Opc.Ua.Export.LocalizedText Export(Opc.Ua.LocalizedText input) { if (input == null) { return null; } Opc.Ua.Export.LocalizedText text = new LocalizedText(); text.Locale = input.Locale; text.Value = input.Text; return text; } /// /// Imports localized text. /// private Opc.Ua.LocalizedText Import(params Opc.Ua.Export.LocalizedText[] input) { if (input == null) { return null; } for (int ii = 0; ii < input.Length; ii++) { if (input[ii] != null) { return new Opc.Ua.LocalizedText(input[ii].Locale, input[ii].Value); } } return null; } /// /// Exports a namespace index. /// private ushort ExportNamespaceIndex(ushort namespaceIndex, NamespaceTable namespaceUris) { // nothing special required for indexes 0. if (namespaceIndex < 1) { return namespaceIndex; } // return a bad value if parameters are bad. if (namespaceUris == null || namespaceUris.Count <= namespaceIndex) { return UInt16.MaxValue; } // find an existing index. int count = 1; string targetUri = namespaceUris.GetString(namespaceIndex); if (this.NamespaceUris != null) { for (int ii = 0; ii < this.NamespaceUris.Length; ii++) { if (this.NamespaceUris[ii] == targetUri) { return (ushort)(ii+1); // add 1 to adjust for the well-known URIs which are not stored. } } count += this.NamespaceUris.Length; } // add a new entry. string[] uris = new string[count]; if (this.NamespaceUris != null) { Array.Copy(this.NamespaceUris, uris, count - 1); } uris[count-1] = targetUri; this.NamespaceUris = uris; // return the new index. return (ushort)count; } /// /// Exports a namespace index. /// private ushort ImportNamespaceIndex(ushort namespaceIndex, NamespaceTable namespaceUris) { // nothing special required for indexes 0 and 1. if (namespaceIndex < 1) { return namespaceIndex; } // return a bad value if parameters are bad. if (namespaceUris == null || this.NamespaceUris == null || this.NamespaceUris.Length <= namespaceIndex-1) { return UInt16.MaxValue; } // find or append uri. return namespaceUris.GetIndexOrAppend(this.NamespaceUris[namespaceIndex-1]); } /// /// Exports a namespace uri. /// private ushort ExportNamespaceUri(string namespaceUri, NamespaceTable namespaceUris) { // return a bad value if parameters are bad. if (namespaceUris == null) { return UInt16.MaxValue; } int namespaceIndex = namespaceUris.GetIndex(namespaceUri); // nothing special required for the first two URIs. if (namespaceIndex == 0) { return (ushort)namespaceIndex; } // find an existing index. int count = 1;; if (this.NamespaceUris != null) { for (int ii = 0; ii < this.NamespaceUris.Length; ii++) { if (this.NamespaceUris[ii] == namespaceUri) { return (ushort)(ii + 1); // add 1 to adjust for the well-known URIs which are not stored. } } count += this.NamespaceUris.Length; } // add a new entry. string[] uris = new string[count]; if (this.NamespaceUris != null) { Array.Copy(this.NamespaceUris, uris, count - 1); } uris[count - 1] = namespaceUri; this.NamespaceUris = uris; // return the new index. return (ushort)(count + 1); } /// /// Exports a server index. /// private uint ExportServerIndex(uint serverIndex, StringTable serverUris) { // nothing special required for indexes 0. if (serverIndex <= 0) { return serverIndex; } // return a bad value if parameters are bad. if (serverUris == null || serverUris.Count < serverIndex) { return UInt16.MaxValue; } // find an existing index. int count = 1; string targetUri = serverUris.GetString(serverIndex); if (this.ServerUris != null) { for (int ii = 0; ii < this.ServerUris.Length; ii++) { if (this.ServerUris[ii] == targetUri) { return (ushort)(ii + 1); // add 1 to adjust for the well-known URIs which are not stored. } } count += this.ServerUris.Length; } // add a new entry. string[] uris = new string[count]; if (this.ServerUris != null) { Array.Copy(this.ServerUris, uris, count - 1); } uris[count-1] = targetUri; this.ServerUris = uris; // return the new index. return (ushort)count; } /// /// Exports a server index. /// private uint ImportServerIndex(uint serverIndex, StringTable serverUris) { // nothing special required for indexes 0. if (serverIndex <= 0) { return serverIndex; } // return a bad value if parameters are bad. if (serverUris == null || this.ServerUris == null || this.ServerUris.Length <= serverIndex-1) { return UInt16.MaxValue; } // find or append uri. return serverUris.GetIndexOrAppend(this.ServerUris[serverIndex - 1]); } #endregion #region Private Fields #endregion } }