Skip to content

Commit

Permalink
Set DefaultEncodingId in DataTypeDefinition, was always null NodeId (#…
Browse files Browse the repository at this point in the history
…1523)

- DefaultEncodingId is always set to binary encoding id. Helper added to StructureDefinition for other use cases (e.g. PubSub)
- the default encoding id is derived for the encodeable factory, since the typetree does not contain sufficent information to find the proper binary encoding id
- add a few helper functions in core for an upcoming PR that was used for testing with custom types, will simplify to add more nodemanagers to servers with custom types
  • Loading branch information
mregen committed Sep 24, 2021
1 parent 0d34b7d commit 33ae8bb
Show file tree
Hide file tree
Showing 8 changed files with 311 additions and 181 deletions.
199 changes: 106 additions & 93 deletions Libraries/Opc.Ua.Server/NodeManager/INodeManager.cs

Large diffs are not rendered by default.

117 changes: 58 additions & 59 deletions Stack/Opc.Ua.Core/Stack/Nodes/TypeTable.cs

Large diffs are not rendered by default.

59 changes: 33 additions & 26 deletions Stack/Opc.Ua.Core/Stack/State/DataTypeState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public ExtensionObject DataTypeDefinition
/// The purpose of the data type.
/// </summary>
public Opc.Ua.Export.DataTypePurpose Purpose { get; set; }

#region Serialization Functions
/// <summary>
/// Saves the attributes from the stream.
Expand Down Expand Up @@ -179,21 +180,27 @@ public override void Update(ISystemContext context, BinaryDecoder decoder, Attri
switch (attributeId)
{
case Attributes.DataTypeDefinition:
{
ExtensionObject dataTypeDefinition = m_dataTypeDefinition;
{
ExtensionObject dataTypeDefinition = m_dataTypeDefinition;

if (OnReadDataTypeDefinition != null)
{
result = OnReadDataTypeDefinition(context, this, ref dataTypeDefinition);
}
if (OnReadDataTypeDefinition != null)
{
result = OnReadDataTypeDefinition(context, this, ref dataTypeDefinition);
}

if (ServiceResult.IsGood(result))
if (ServiceResult.IsGood(result))
{
if (dataTypeDefinition?.Body is StructureDefinition structureType &&
structureType.DefaultEncodingId.IsNullNodeId)
{
value = dataTypeDefinition;
// one time set the id for binary encoding, currently the only supported encoding
structureType.SetDefaultEncodingId(context, NodeId, null);
}

return result;
value = dataTypeDefinition;
}

return result;
}
}

return base.ReadNonValueAttribute(context, attributeId, ref value);
Expand All @@ -214,26 +221,26 @@ public override void Update(ISystemContext context, BinaryDecoder decoder, Attri
switch (attributeId)
{
case Attributes.DataTypeDefinition:
{
ExtensionObject dataTypeDefinition = value as ExtensionObject;

if ((WriteMask & AttributeWriteMask.DataTypeDefinition) == 0)
{
return StatusCodes.BadNotWritable;
}
{
ExtensionObject dataTypeDefinition = value as ExtensionObject;

if (OnWriteDataTypeDefinition != null)
{
result = OnWriteDataTypeDefinition(context, this, ref dataTypeDefinition);
}
if ((WriteMask & AttributeWriteMask.DataTypeDefinition) == 0)
{
return StatusCodes.BadNotWritable;
}

if (ServiceResult.IsGood(result))
{
m_dataTypeDefinition = dataTypeDefinition;
}
if (OnWriteDataTypeDefinition != null)
{
result = OnWriteDataTypeDefinition(context, this, ref dataTypeDefinition);
}

return result;
if (ServiceResult.IsGood(result))
{
m_dataTypeDefinition = dataTypeDefinition;
}

return result;
}
}

return base.WriteNonValueAttribute(context, attributeId, value);
Expand Down
12 changes: 12 additions & 0 deletions Stack/Opc.Ua.Core/Stack/State/ModelCompilerExtension.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
59 changes: 59 additions & 0 deletions Stack/Opc.Ua.Core/Stack/Types/StructureDefinition.cs
Original file line number Diff line number Diff line change
@@ -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
/// <summary>
///
/// </summary>
/// <exclude />
public partial class StructureDefinition : DataTypeDefinition
{
/// <summary>
/// Set the default encoding id for the requested data encoding.
/// </summary>
/// <param name="context">The system context with the encodeable factory.</param>
/// <param name="typeId">The type id of the Data Type.</param>
/// <param name="dataEncoding">The data encoding to apply to the default encoding id.</param>
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
}
20 changes: 20 additions & 0 deletions Stack/Opc.Ua.Core/Types/Encoders/EncodeableFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,26 @@ public void AddEncodeableTypes(Assembly assembly)
}
}

/// <summary>
/// Adds an enumerable of extension types to the factory.
/// </summary>
/// <param name="systemTypes">The underlying system types to add to the factory</param>
public void AddEncodeableTypes(IEnumerable<System.Type> systemTypes)
{
lock (m_lock)
{
foreach (var type in systemTypes)
{
if (type.GetTypeInfo().IsAbstract)
{
continue;
}

AddEncodeableType(type);
}
}
}

/// <summary>
/// Returns the system type for the specified type id.
/// </summary>
Expand Down
9 changes: 6 additions & 3 deletions Stack/Opc.Ua.Core/Types/Encoders/IEncodeableFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@ public interface IEncodeableFactory
/// <summary>
/// Adds an extension type to the factory.
/// </summary>
/// <remarks>
/// Adds an extension type to the factory.
/// </remarks>
/// <param name="systemType">The underlying system type to add to the factory</param>
void AddEncodeableType(Type systemType);

Expand All @@ -75,6 +72,12 @@ public interface IEncodeableFactory
/// <param name="assembly">The assembly containing the types to add to the factory</param>
void AddEncodeableTypes(Assembly assembly);

/// <summary>
/// Adds an enumerable of extension types to the factory.
/// </summary>
/// <param name="systemTypes">The underlying system types to add to the factory</param>
void AddEncodeableTypes(IEnumerable<Type> systemTypes);

/// <summary>
/// Returns the system type for the specified type id.
/// </summary>
Expand Down
17 changes: 17 additions & 0 deletions Tests/Opc.Ua.Client.Tests/ClientTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,23 @@ public void ReadValues()
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)
{
Expand Down

0 comments on commit 33ae8bb

Please sign in to comment.