diff --git a/Libraries/Opc.Ua.Server/NodeManager/INodeManager.cs b/Libraries/Opc.Ua.Server/NodeManager/INodeManager.cs index 4c8ed1b746..7eba8a9d4f 100644 --- a/Libraries/Opc.Ua.Server/NodeManager/INodeManager.cs +++ b/Libraries/Opc.Ua.Server/NodeManager/INodeManager.cs @@ -33,11 +33,24 @@ using System.Text; namespace Opc.Ua.Server { + /// + /// An interface to an object that creates a INodeManager object. + /// + public interface INodeManagerFactory + { + /// + /// The INodeManager factory. + /// + /// The server instance. + /// The application configuration. + INodeManager Create(IServerInternal server, ApplicationConfiguration configuration); + } + /// /// An interface to an object that manages a set of nodes in the address space. /// public interface INodeManager - { + { /// /// Returns the NamespaceUris for the Nodes belonging to the NodeManager. /// @@ -61,8 +74,8 @@ namespace Opc.Ua.Server /// by other node managers. In these cases, the node managers only manage one half of those references. The /// other half of the reference should be returned to the MasterNodeManager. /// - void CreateAddressSpace(IDictionary> externalReferences); - + void CreateAddressSpace(IDictionary> externalReferences); + /// /// Deletes the address by releasing all resources and disconnecting from any underlying system. /// @@ -85,17 +98,17 @@ namespace Opc.Ua.Server /// /// The node manager checks the dictionary for nodes that it owns and ensures the associated references exist. /// - void AddReferences(IDictionary> references); - + void AddReferences(IDictionary> references); + /// /// Deletes a reference. /// ServiceResult DeleteReference( - object sourceHandle, - NodeId referenceTypeId, - bool isInverse, - ExpandedNodeId targetId, - bool deleteBidirectional); + object sourceHandle, + NodeId referenceTypeId, + bool isInverse, + ExpandedNodeId targetId, + bool deleteBidirectional); /// /// Returns the metadata associated with the node. @@ -125,8 +138,8 @@ namespace Opc.Ua.Server /// Thrown if the context, continuationPoint or references parameters are null. /// Thrown if an error occurs during processing. void Browse( - OperationContext context, - ref ContinuationPoint continuationPoint, + OperationContext context, + ref ContinuationPoint continuationPoint, IList references); /// @@ -146,11 +159,11 @@ namespace Opc.Ua.Server /// /// Thrown if the sourceHandle, relativePath or targetIds parameters are null. void TranslateBrowsePath( - OperationContext context, - object sourceHandle, - RelativePathElement relativePath, + OperationContext context, + object sourceHandle, + RelativePathElement relativePath, IList targetIds, - IList unresolvedTargetIds); + IList unresolvedTargetIds); /// /// Reads the attribute values for a set of nodes. @@ -170,23 +183,23 @@ namespace Opc.Ua.Server /// The node manager must set the Processed flag for any ReadValueId that it processes. /// void Read( - OperationContext context, - double maxAge, - IList nodesToRead, - IList values, + OperationContext context, + double maxAge, + IList nodesToRead, + IList values, IList errors); - + /// /// Reads the history of a set of items. /// void HistoryRead( - OperationContext context, - HistoryReadDetails details, - TimestampsToReturn timestampsToReturn, - bool releaseContinuationPoints, - IList nodesToRead, - IList results, - IList errors); + OperationContext context, + HistoryReadDetails details, + TimestampsToReturn timestampsToReturn, + bool releaseContinuationPoints, + IList nodesToRead, + IList results, + IList errors); /// /// Writes a set of values. @@ -196,28 +209,28 @@ namespace Opc.Ua.Server /// must set the Processed flag in the WriteValue structure. /// void Write( - OperationContext context, - IList nodesToWrite, + OperationContext context, + IList nodesToWrite, IList errors); - + /// /// Updates the history for a set of nodes. /// void HistoryUpdate( - OperationContext context, - Type detailsType, - IList nodesToUpdate, - IList results, - IList errors); + OperationContext context, + Type detailsType, + IList nodesToUpdate, + IList results, + IList errors); /// /// Calls a method defined on a object. /// void Call( - OperationContext context, + OperationContext context, IList methodsToCall, - IList results, - IList errors); + IList results, + IList errors); /// /// Tells the NodeManager to report events from the specified notifier. @@ -226,13 +239,13 @@ namespace Opc.Ua.Server /// This method may be called multiple times for the name monitoredItemId if the /// context for that MonitoredItem changes (i.e. UserIdentity and/or Locales). /// - ServiceResult SubscribeToEvents( - OperationContext context, - object sourceId, - uint subscriptionId, + ServiceResult SubscribeToEvents( + OperationContext context, + object sourceId, + uint subscriptionId, IEventMonitoredItem monitoredItem, - bool unsubscribe); - + bool unsubscribe); + /// /// Tells the NodeManager to report events all events from all sources. /// @@ -240,64 +253,64 @@ namespace Opc.Ua.Server /// This method may be called multiple times for the name monitoredItemId if the /// context for that MonitoredItem changes (i.e. UserIdentity and/or Locales). /// - ServiceResult SubscribeToAllEvents( - OperationContext context, - uint subscriptionId, + ServiceResult SubscribeToAllEvents( + OperationContext context, + uint subscriptionId, IEventMonitoredItem monitoredItem, - bool unsubscribe); - + bool unsubscribe); + /// /// Tells the NodeManager to refresh any conditions. /// - ServiceResult ConditionRefresh( - OperationContext context, + ServiceResult ConditionRefresh( + OperationContext context, IList monitoredItems); /// /// Creates a set of monitored items. /// void CreateMonitoredItems( - OperationContext context, - uint subscriptionId, - double publishingInterval, - TimestampsToReturn timestampsToReturn, + OperationContext context, + uint subscriptionId, + double publishingInterval, + TimestampsToReturn timestampsToReturn, IList itemsToCreate, - IList errors, - IList filterErrors, - IList monitoredItems, - ref long globalIdCounter); - + IList errors, + IList filterErrors, + IList monitoredItems, + ref long globalIdCounter); + /// /// Modifies a set of monitored items. /// void ModifyMonitoredItems( - OperationContext context, - TimestampsToReturn timestampsToReturn, - IList monitoredItems, + OperationContext context, + TimestampsToReturn timestampsToReturn, + IList monitoredItems, IList itemsToModify, - IList errors, - IList filterErrors); + IList errors, + IList filterErrors); /// /// Deletes a set of monitored items. /// void DeleteMonitoredItems( - OperationContext context, - IList monitoredItems, - IList processedItems, - IList errors); - + OperationContext context, + IList monitoredItems, + IList processedItems, + IList errors); + /// /// Changes the monitoring mode for a set of monitored items. /// void SetMonitoringMode( - OperationContext context, - MonitoringMode monitoringMode, - IList monitoredItems, - IList processedItems, - IList errors); + OperationContext context, + MonitoringMode monitoringMode, + IList monitoredItems, + IList processedItems, + IList errors); } - + /// /// An interface to an object that manages a set of nodes in the address space. /// @@ -328,7 +341,7 @@ namespace Opc.Ua.Server Dictionary> uniqueNodesServiceAttributesCache, bool permissionsOnly); } - + /// /// Stores metadata required to process requests related to a node. /// @@ -344,7 +357,7 @@ namespace Opc.Ua.Server m_nodeId = nodeId; } #endregion - + #region Public Properties /// /// The handle assigned by the NodeManager that owns the Node. @@ -360,14 +373,14 @@ namespace Opc.Ua.Server public NodeId NodeId { get { return m_nodeId; } - } + } /// /// The NodeClass for the Node. /// public NodeClass NodeClass { - get { return m_nodeClass; } + get { return m_nodeClass; } set { m_nodeClass = value; } } @@ -376,7 +389,7 @@ namespace Opc.Ua.Server /// public QualifiedName BrowseName { - get { return m_browseName; } + get { return m_browseName; } set { m_browseName = value; } } @@ -385,7 +398,7 @@ namespace Opc.Ua.Server /// public LocalizedText DisplayName { - get { return m_displayName; } + get { return m_displayName; } set { m_displayName = value; } } @@ -394,7 +407,7 @@ namespace Opc.Ua.Server /// public ExpandedNodeId TypeDefinition { - get { return m_typeDefinition; } + get { return m_typeDefinition; } set { m_typeDefinition = value; } } @@ -403,7 +416,7 @@ namespace Opc.Ua.Server /// public NodeId ModellingRule { - get { return m_modellingRule; } + get { return m_modellingRule; } set { m_modellingRule = value; } } @@ -412,7 +425,7 @@ namespace Opc.Ua.Server /// public AttributeWriteMask WriteMask { - get { return m_writeMask; } + get { return m_writeMask; } set { m_writeMask = value; } } @@ -421,16 +434,16 @@ namespace Opc.Ua.Server /// public byte EventNotifier { - get { return m_eventNotifier; } + get { return m_eventNotifier; } set { m_eventNotifier = value; } } - + /// /// Whether the Node can be use to read or write current or historical values. /// public byte AccessLevel { - get { return m_accessLevel; } + get { return m_accessLevel; } set { m_accessLevel = value; } } @@ -439,7 +452,7 @@ namespace Opc.Ua.Server /// public bool Executable { - get { return m_executable; } + get { return m_executable; } set { m_executable = value; } } @@ -448,7 +461,7 @@ namespace Opc.Ua.Server /// public NodeId DataType { - get { return m_dataType; } + get { return m_dataType; } set { m_dataType = value; } } @@ -457,7 +470,7 @@ namespace Opc.Ua.Server /// public int ValueRank { - get { return m_valueRank; } + get { return m_valueRank; } set { m_valueRank = value; } } @@ -466,7 +479,7 @@ namespace Opc.Ua.Server /// public IList ArrayDimensions { - get { return m_arrayDimensions; } + get { return m_arrayDimensions; } set { m_arrayDimensions = value; } } diff --git a/Stack/Opc.Ua.Core/Stack/Nodes/TypeTable.cs b/Stack/Opc.Ua.Core/Stack/Nodes/TypeTable.cs index 6e19363289..e3def2fbb6 100644 --- a/Stack/Opc.Ua.Core/Stack/Nodes/TypeTable.cs +++ b/Stack/Opc.Ua.Core/Stack/Nodes/TypeTable.cs @@ -15,7 +15,7 @@ using System.Collections.Generic; namespace Opc.Ua { /// - /// Stores the type tree for a server. + /// Stores the type tree for a server. /// public class TypeTable : ITypeTable { @@ -26,24 +26,24 @@ namespace Opc.Ua /// The namespace URIs. public TypeTable(NamespaceTable namespaceUris) { - m_namespaceUris = namespaceUris; - m_referenceTypes = new SortedDictionary(); + m_namespaceUris = namespaceUris; + m_referenceTypes = new SortedDictionary(); m_nodes = new NodeIdDictionary(); m_encodings = new NodeIdDictionary(); } #endregion #region ITypeTable Methods - /// + /// public bool IsKnown(ExpandedNodeId typeId) - { + { if (NodeId.IsNull(typeId) || typeId.ServerIndex != 0) { return false; } NodeId localId = ExpandedNodeId.ToNodeId(typeId, m_namespaceUris); - + if (localId == null) { return false; @@ -55,9 +55,9 @@ namespace Opc.Ua } } - /// + /// public bool IsKnown(NodeId typeId) - { + { if (NodeId.IsNull(typeId)) { return false; @@ -67,18 +67,18 @@ namespace Opc.Ua { return m_nodes.ContainsKey(typeId); } - } - - /// + } + + /// public NodeId FindSuperType(ExpandedNodeId typeId) { if (NodeId.IsNull(typeId) || typeId.ServerIndex != 0) { return NodeId.Null; } - + NodeId localId = ExpandedNodeId.ToNodeId(typeId, m_namespaceUris); - + if (localId == null) { return NodeId.Null; @@ -101,10 +101,10 @@ namespace Opc.Ua return NodeId.Null; } } - - /// + + /// public NodeId FindSuperType(NodeId typeId) - { + { if (typeId == null) { return NodeId.Null; @@ -128,7 +128,7 @@ namespace Opc.Ua } } - /// + /// public IList FindSubTypes(ExpandedNodeId typeId) { List subtypes = new List(); @@ -158,14 +158,14 @@ namespace Opc.Ua } } - /// + /// public bool IsTypeOf(ExpandedNodeId subTypeId, ExpandedNodeId superTypeId) { if (NodeId.IsNull(subTypeId) || subTypeId.ServerIndex != 0) { return false; } - + if (NodeId.IsNull(superTypeId) || superTypeId.ServerIndex != 0) { return false; @@ -178,14 +178,14 @@ namespace Opc.Ua } NodeId startId = ExpandedNodeId.ToNodeId(subTypeId, m_namespaceUris); - + if (startId == null) { return false; } NodeId targetId = ExpandedNodeId.ToNodeId(superTypeId, m_namespaceUris); - + if (targetId == null) { return false; @@ -204,7 +204,7 @@ namespace Opc.Ua } } - /// + /// public bool IsTypeOf(NodeId subTypeId, NodeId superTypeId) { // check for null. @@ -218,7 +218,7 @@ namespace Opc.Ua { return true; } - + lock (m_lock) { TypeInfo typeInfo = null; @@ -231,9 +231,8 @@ namespace Opc.Ua return typeInfo.IsTypeOf(superTypeId); } } - - - /// + + /// public QualifiedName FindReferenceTypeName(NodeId referenceTypeId) { lock (m_lock) @@ -249,7 +248,7 @@ namespace Opc.Ua } } - /// + /// public NodeId FindReferenceType(QualifiedName browseName) { // check for empty name. @@ -270,8 +269,8 @@ namespace Opc.Ua return typeInfo.NodeId; } } - - /// + + /// public bool IsEncodingOf(ExpandedNodeId encodingId, ExpandedNodeId datatypeId) { // check for invalid ids. @@ -279,7 +278,7 @@ namespace Opc.Ua { return false; } - + NodeId localId = ExpandedNodeId.ToNodeId(encodingId, m_namespaceUris); if (localId == null) @@ -288,7 +287,7 @@ namespace Opc.Ua } NodeId localTypeId = ExpandedNodeId.ToNodeId(datatypeId, m_namespaceUris); - + if (localTypeId == null) { return false; @@ -327,8 +326,8 @@ namespace Opc.Ua return false; } } - - /// + + /// public bool IsEncodingFor(NodeId expectedTypeId, ExtensionObject value) { // no match on null values. @@ -347,7 +346,7 @@ namespace Opc.Ua return false; } - /// + /// public bool IsEncodingFor(NodeId expectedTypeId, object value) { // null actual datatype matches nothing. @@ -381,12 +380,12 @@ namespace Opc.Ua // for structure types must try to determine the subtype. ExtensionObject extension = value as ExtensionObject; - + if (extension != null) { return IsEncodingFor(expectedTypeId, extension); } - + // every element in an array must match. ExtensionObject[] extensions = value as ExtensionObject[]; @@ -406,10 +405,10 @@ namespace Opc.Ua // can only get here if the value is an unrecognized data type. return false; } - - /// - public NodeId FindDataTypeId(ExpandedNodeId encodingId) - { + + /// + public NodeId FindDataTypeId(ExpandedNodeId encodingId) + { NodeId localId = ExpandedNodeId.ToNodeId(encodingId, m_namespaceUris); if (localId == null) @@ -430,8 +429,8 @@ namespace Opc.Ua } } - /// - public NodeId FindDataTypeId(NodeId encodingId) + /// + public NodeId FindDataTypeId(NodeId encodingId) { lock (m_lock) { @@ -472,7 +471,7 @@ namespace Opc.Ua { return; } - + // ignore non-types. if ((node.NodeClass & (NodeClass.ObjectType | NodeClass.VariableType | NodeClass.ReferenceType | NodeClass.DataType)) == 0) { @@ -487,11 +486,11 @@ namespace Opc.Ua if (superTypeId != null) { localsuperTypeId = ExpandedNodeId.ToNodeId(superTypeId, m_namespaceUris); - + if (localsuperTypeId == null) { throw ServiceResultException.Create(StatusCodes.BadNodeIdInvalid, "A valid supertype identifier is required."); - } + } } lock (m_lock) @@ -515,7 +514,7 @@ namespace Opc.Ua typeInfo = new TypeInfo(); m_nodes.Add(node.NodeId, typeInfo); } - + // update the info. typeInfo.NodeId = node.NodeId; typeInfo.SuperType = superTypeInfo; @@ -526,7 +525,7 @@ namespace Opc.Ua { superTypeInfo.AddSubType(typeInfo); } - + // remove the encodings. if (typeInfo.Encodings != null) { @@ -549,7 +548,7 @@ namespace Opc.Ua m_encodings[typeInfo.Encodings[ii]] = typeInfo; } } - + // add reference type. if ((node.NodeClass & NodeClass.ReferenceType) != 0) { @@ -557,14 +556,14 @@ namespace Opc.Ua { m_referenceTypes.Remove(typeInfo.BrowseName); } - + typeInfo.BrowseName = node.BrowseName; m_referenceTypes[node.BrowseName] = typeInfo; } } } - + /// /// Adds type to the table. A browse name is only required if it is a ReferenceType. /// @@ -607,7 +606,7 @@ namespace Opc.Ua lock (m_lock) { TypeInfo typeInfo = null; - + if (!m_nodes.TryGetValue(dataTypeId, out typeInfo)) { return false; @@ -662,7 +661,7 @@ namespace Opc.Ua typeInfo = new TypeInfo(); m_nodes.Add(subTypeId, typeInfo); } - + // update the info. typeInfo.NodeId = subTypeId; typeInfo.SuperType = superTypeInfo; @@ -682,7 +681,7 @@ namespace Opc.Ua m_encodings.Remove(encoding); } } - + // add reference type. if (!QualifiedName.IsNull(browseName)) { @@ -701,7 +700,7 @@ namespace Opc.Ua { return; } - + NodeId localId = ExpandedNodeId.ToNodeId(typeId, m_namespaceUris); if (localId == null) @@ -716,7 +715,7 @@ namespace Opc.Ua if (!m_nodes.TryGetValue(localId, out typeInfo)) { - return; + return; } m_nodes.Remove(localId); @@ -738,7 +737,7 @@ namespace Opc.Ua m_encodings.Remove(typeInfo.Encodings[ii]); } } - + // remove reference type. if (!QualifiedName.IsNull(typeInfo.BrowseName)) { @@ -771,7 +770,7 @@ namespace Opc.Ua public bool IsTypeOf(NodeId nodeId) { TypeInfo typeInfo = SuperType; - + while (typeInfo != null) { if (!typeInfo.Deleted && typeInfo.NodeId == nodeId) @@ -834,11 +833,11 @@ namespace Opc.Ua } } #endregion - + #region Private Fields private object m_lock = new object(); private NamespaceTable m_namespaceUris; - private SortedDictionary m_referenceTypes; + private SortedDictionary m_referenceTypes; private NodeIdDictionary m_nodes; private NodeIdDictionary m_encodings; #endregion diff --git a/Stack/Opc.Ua.Core/Stack/State/DataTypeState.cs b/Stack/Opc.Ua.Core/Stack/State/DataTypeState.cs index e8a654902e..25e9999df7 100644 --- a/Stack/Opc.Ua.Core/Stack/State/DataTypeState.cs +++ b/Stack/Opc.Ua.Core/Stack/State/DataTypeState.cs @@ -64,6 +64,7 @@ namespace Opc.Ua /// The purpose of the data type. /// public Opc.Ua.Export.DataTypePurpose Purpose { get; set; } + #region Serialization Functions /// /// Saves the attributes from the stream. @@ -179,21 +180,27 @@ namespace Opc.Ua switch (attributeId) { case Attributes.DataTypeDefinition: + { + ExtensionObject dataTypeDefinition = m_dataTypeDefinition; + + if (OnReadDataTypeDefinition != null) { - ExtensionObject dataTypeDefinition = m_dataTypeDefinition; - - if (OnReadDataTypeDefinition != null) - { - result = OnReadDataTypeDefinition(context, this, ref dataTypeDefinition); - } - - if (ServiceResult.IsGood(result)) - { - value = dataTypeDefinition; - } - - return result; + result = OnReadDataTypeDefinition(context, this, ref dataTypeDefinition); } + + if (ServiceResult.IsGood(result)) + { + if (dataTypeDefinition?.Body is StructureDefinition structureType && + structureType.DefaultEncodingId.IsNullNodeId) + { + // one time set the id for binary encoding, currently the only supported encoding + structureType.SetDefaultEncodingId(context, NodeId, null); + } + value = dataTypeDefinition; + } + + return result; + } } return base.ReadNonValueAttribute(context, attributeId, ref value); @@ -214,26 +221,26 @@ namespace Opc.Ua switch (attributeId) { case Attributes.DataTypeDefinition: + { + ExtensionObject dataTypeDefinition = value as ExtensionObject; + + if ((WriteMask & AttributeWriteMask.DataTypeDefinition) == 0) { - ExtensionObject dataTypeDefinition = value as ExtensionObject; - - if ((WriteMask & AttributeWriteMask.DataTypeDefinition) == 0) - { - return StatusCodes.BadNotWritable; - } - - if (OnWriteDataTypeDefinition != null) - { - result = OnWriteDataTypeDefinition(context, this, ref dataTypeDefinition); - } - - if (ServiceResult.IsGood(result)) - { - m_dataTypeDefinition = dataTypeDefinition; - } - - return result; + return StatusCodes.BadNotWritable; } + + if (OnWriteDataTypeDefinition != null) + { + result = OnWriteDataTypeDefinition(context, this, ref dataTypeDefinition); + } + + if (ServiceResult.IsGood(result)) + { + m_dataTypeDefinition = dataTypeDefinition; + } + + return result; + } } return base.WriteNonValueAttribute(context, attributeId, value); diff --git a/Stack/Opc.Ua.Core/Stack/State/ModelCompilerExtension.cs b/Stack/Opc.Ua.Core/Stack/State/ModelCompilerExtension.cs index 478c70bebf..33c53b3480 100644 --- a/Stack/Opc.Ua.Core/Stack/State/ModelCompilerExtension.cs +++ b/Stack/Opc.Ua.Core/Stack/State/ModelCompilerExtension.cs @@ -1,3 +1,15 @@ +/* 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.Linq; diff --git a/Stack/Opc.Ua.Core/Stack/Types/StructureDefinition.cs b/Stack/Opc.Ua.Core/Stack/Types/StructureDefinition.cs new file mode 100644 index 0000000000..28aafe9c24 --- /dev/null +++ b/Stack/Opc.Ua.Core/Stack/Types/StructureDefinition.cs @@ -0,0 +1,59 @@ +/* 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.Text; +using System.Xml; +using System.Runtime.Serialization; + +namespace Opc.Ua +{ + #region StructureDefinition Class + /// + /// + /// + /// + public partial class StructureDefinition : DataTypeDefinition + { + /// + /// Set the default encoding id for the requested data encoding. + /// + /// The system context with the encodeable factory. + /// The type id of the Data Type. + /// The data encoding to apply to the default encoding id. + public void SetDefaultEncodingId(ISystemContext context, NodeId typeId, QualifiedName dataEncoding) + { + if (context == null) throw new ArgumentNullException(nameof(context)); + if (dataEncoding?.Name == BrowseNames.DefaultJson) + { + DefaultEncodingId = ExpandedNodeId.ToNodeId(typeId, context.NamespaceUris); + return; + } + + // note: custom types must be added to the encodeable factory by the node manager to be found + var systemType = context.EncodeableFactory?.GetSystemType(NodeId.ToExpandedNodeId(typeId, context.NamespaceUris)); + if (systemType != null && Activator.CreateInstance(systemType) is IEncodeable encodeable) + { + if (dataEncoding == null || dataEncoding.Name == BrowseNames.DefaultBinary) + { + DefaultEncodingId = ExpandedNodeId.ToNodeId(encodeable.BinaryEncodingId, context.NamespaceUris); + } + else if (dataEncoding.Name == BrowseNames.DefaultXml) + { + DefaultEncodingId = ExpandedNodeId.ToNodeId(encodeable.XmlEncodingId, context.NamespaceUris); + } + } + } + } + #endregion +} diff --git a/Stack/Opc.Ua.Core/Types/Encoders/EncodeableFactory.cs b/Stack/Opc.Ua.Core/Types/Encoders/EncodeableFactory.cs index 93d23e987d..3f4ded6e2b 100644 --- a/Stack/Opc.Ua.Core/Types/Encoders/EncodeableFactory.cs +++ b/Stack/Opc.Ua.Core/Types/Encoders/EncodeableFactory.cs @@ -341,6 +341,26 @@ namespace Opc.Ua } } + /// + /// Adds an enumerable of extension types to the factory. + /// + /// The underlying system types to add to the factory + public void AddEncodeableTypes(IEnumerable systemTypes) + { + lock (m_lock) + { + foreach (var type in systemTypes) + { + if (type.GetTypeInfo().IsAbstract) + { + continue; + } + + AddEncodeableType(type); + } + } + } + /// /// Returns the system type for the specified type id. /// diff --git a/Stack/Opc.Ua.Core/Types/Encoders/IEncodeableFactory.cs b/Stack/Opc.Ua.Core/Types/Encoders/IEncodeableFactory.cs index 26196f88c9..e1c6096d1e 100644 --- a/Stack/Opc.Ua.Core/Types/Encoders/IEncodeableFactory.cs +++ b/Stack/Opc.Ua.Core/Types/Encoders/IEncodeableFactory.cs @@ -46,9 +46,6 @@ namespace Opc.Ua /// /// Adds an extension type to the factory. /// - /// - /// Adds an extension type to the factory. - /// /// The underlying system type to add to the factory void AddEncodeableType(Type systemType); @@ -75,6 +72,12 @@ namespace Opc.Ua /// The assembly containing the types to add to the factory void AddEncodeableTypes(Assembly assembly); + /// + /// Adds an enumerable of extension types to the factory. + /// + /// The underlying system types to add to the factory + void AddEncodeableTypes(IEnumerable systemTypes); + /// /// Returns the system type for the specified type id. /// diff --git a/Tests/Opc.Ua.Client.Tests/ClientTest.cs b/Tests/Opc.Ua.Client.Tests/ClientTest.cs index 262724b179..6e1358317f 100644 --- a/Tests/Opc.Ua.Client.Tests/ClientTest.cs +++ b/Tests/Opc.Ua.Client.Tests/ClientTest.cs @@ -303,6 +303,23 @@ namespace Opc.Ua.Client.Tests m_session.ChangePreferredLocales(locale); } + [Test] + public void ReadDataTypeDefinition() + { + // Test Read a DataType Node + var node = m_session.ReadNode(DataTypeIds.ProgramDiagnosticDataType); + Assert.NotNull(node); + var dataTypeNode = (DataTypeNode)node; + Assert.NotNull(dataTypeNode); + var dataTypeDefinition = dataTypeNode.DataTypeDefinition; + Assert.NotNull(dataTypeDefinition); + Assert.True(dataTypeDefinition is ExtensionObject); + Assert.NotNull(dataTypeDefinition.Body); + Assert.True(dataTypeDefinition.Body is StructureDefinition); + StructureDefinition structureDefinition = dataTypeDefinition.Body as StructureDefinition; + Assert.AreEqual(ObjectIds.ProgramDiagnosticDataType_Encoding_DefaultBinary, structureDefinition.DefaultEncodingId); + } + [Theory, Order(400)] public async Task BrowseFullAddressSpace(string securityPolicy) {