Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix degradation to sync calls in the SDK on main paths used. #2316

Merged
merged 9 commits into from
Sep 22, 2023
219 changes: 112 additions & 107 deletions Libraries/Opc.Ua.Client.ComplexTypes/ComplexTypeSystem.cs

Large diffs are not rendered by default.

58 changes: 32 additions & 26 deletions Libraries/Opc.Ua.Client.ComplexTypes/IComplexTypeResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Copyright (c) 2005-2021 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
Expand All @@ -11,7 +11,7 @@
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Expand All @@ -30,6 +30,7 @@

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Opc.Ua.Client.ComplexTypes
Expand All @@ -53,7 +54,10 @@ public interface IComplexTypeResolver
/// Loads all dictionaries of the OPC binary or Xml schema type system.
/// </summary>
/// <param name="dataTypeSystem">The type system. Defaults to OPC Binary schema.</param>
Task<Dictionary<NodeId, DataDictionary>> LoadDataTypeSystem(NodeId dataTypeSystem = null);
/// <param name="ct"></param>
Task<Dictionary<NodeId, DataDictionary>> LoadDataTypeSystem(
NodeId dataTypeSystem = null,
CancellationToken ct = default);

/// <summary>
/// Browse for the type and encoding id for a dictionary component.
Expand All @@ -64,59 +68,59 @@ public interface IComplexTypeResolver
/// and data type dictionary.
/// To find the typeId and encodingId for a dictionary type definition:
/// i) inverse browse the description to get the encodingid
/// ii) from the description inverse browse for encoding
/// to get the subtype typeid
/// iii) load the DataType node
/// ii) from the description inverse browse for encoding
/// to get the subtype typeid
/// iii) load the DataType node
/// </remarks>
/// <param name="nodeId"></param>
/// <param name="typeId"></param>
/// <param name="encodingId"></param>
/// <param name="dataTypeNode"></param>
/// <returns>true if successful, false otherwise</returns>
bool BrowseTypeIdsForDictionaryComponent(
/// <param name="ct"></param>
/// <returns>type id, encoding id and data type node if successful, null otherwise</returns>
Task<(ExpandedNodeId typeId, ExpandedNodeId encodingId, DataTypeNode dataTypeNode)> BrowseTypeIdsForDictionaryComponentAsync(
ExpandedNodeId nodeId,
out ExpandedNodeId typeId,
out ExpandedNodeId encodingId,
out DataTypeNode dataTypeNode);
CancellationToken ct = default);

/// <summary>
/// Browse for the encodings of a datatype list.
/// </summary>
/// <remarks>
/// Is called to allow for caching of encoding information on the client.
/// </remarks>
IList<NodeId> BrowseForEncodings(
Task<IList<NodeId>> BrowseForEncodingsAsync(
IList<ExpandedNodeId> nodeIds,
string[] supportedEncodings);

string[] supportedEncodings,
CancellationToken ct = default);

/// <summary>
/// Browse for the encodings of a type.
/// </summary>
/// <remarks>
/// Browse for binary encoding of a structure datatype.
/// </remarks>
IList<NodeId> BrowseForEncodings(
Task<(IList<NodeId> encodings, ExpandedNodeId binaryEncodingId, ExpandedNodeId xmlEncodingId)> BrowseForEncodingsAsync(
ExpandedNodeId nodeId,
string[] supportedEncodings,
out ExpandedNodeId binaryEncodingId,
out ExpandedNodeId xmlEncodingId);
CancellationToken ct = default);

/// <summary>
/// Load all subTypes and optionally nested subtypes of a type definition.
/// Filter for all subtypes or only subtypes outside the default namespace.
/// </summary>
IList<INode> LoadDataTypes(
Task<IList<INode>> LoadDataTypesAsync(
ExpandedNodeId dataType,
bool nestedSubTypes = false,
bool addRootNode = false,
bool filterUATypes = true);
bool filterUATypes = true,
CancellationToken ct = default);

/// <summary>
/// Finds a node in the node set.
/// </summary>
/// <param name="nodeId">The node identifier.</param>
/// <param name="ct"></param>
/// <returns>Returns null if the node does not exist.</returns>
INode Find(ExpandedNodeId nodeId);
Task<INode> FindAsync(
ExpandedNodeId nodeId,
CancellationToken ct = default);

/// <summary>
/// Reads the enum type array of a enum type definition node.
Expand All @@ -126,19 +130,21 @@ public interface IComplexTypeResolver
/// reference of the enum type NodeId
/// </remarks>
/// <param name="nodeId">The enum type nodeId which has an enum array in the property.</param>
/// <param name="ct"></param>
/// <returns>
/// The value of the nodeId, which can be an array of
/// <see cref="ExtensionObject"/> or of <see cref="LocalizedText"/>.
/// <c>null</c> if the enum type array does not exist.
/// <c>null</c> if the enum type array does not exist.
/// </returns>
object GetEnumTypeArray(ExpandedNodeId nodeId);
Task<object> GetEnumTypeArrayAsync(ExpandedNodeId nodeId, CancellationToken ct = default);

/// <summary>
/// Returns the immediate supertype for the type.
/// </summary>
/// <param name="typeId">The type identifier.</param>
/// <param name="ct"></param>
/// <returns>The immediate supertype identifier for <paramref name="typeId"/></returns>
NodeId FindSuperType(NodeId typeId);
Task<NodeId> FindSuperTypeAsync(NodeId typeId, CancellationToken ct = default);

}
}//namespace
117 changes: 60 additions & 57 deletions Libraries/Opc.Ua.Client.ComplexTypes/TypeResolver/NodeCacheResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Copyright (c) 2005-2021 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
Expand All @@ -11,7 +11,7 @@
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Expand All @@ -32,6 +32,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Opc.Ua.Client.ComplexTypes
Expand Down Expand Up @@ -65,106 +66,107 @@
public IEncodeableFactory Factory => m_session.Factory;

/// <inheritdoc/>
public Task<Dictionary<NodeId, DataDictionary>> LoadDataTypeSystem(NodeId dataTypeSystem = null)
public Task<Dictionary<NodeId, DataDictionary>> LoadDataTypeSystem(
NodeId dataTypeSystem = null,
CancellationToken ct = default)
{
return m_session.LoadDataTypeSystem(dataTypeSystem);
return m_session.LoadDataTypeSystem(dataTypeSystem, ct);
}

/// <inheritdoc/>
public IList<NodeId> BrowseForEncodings(
public async Task<IList<NodeId>> BrowseForEncodingsAsync(
IList<ExpandedNodeId> nodeIds,
string[] supportedEncodings
)
string[] supportedEncodings,
CancellationToken ct = default)
{
// cache type encodings
var encodings = m_session.NodeCache.FindReferences(
IList<INode> encodings = await m_session.NodeCache.FindReferencesAsync(
nodeIds,
new NodeIdCollection { ReferenceTypeIds.HasEncoding },
false,
false
);
false,
ct).ConfigureAwait(false);

// cache dictionary descriptions
nodeIds = encodings.Select(r => r.NodeId).ToList();
var descriptions = m_session.NodeCache.FindReferences(
IList<INode> descriptions = await m_session.NodeCache.FindReferencesAsync(
nodeIds,
new NodeIdCollection { ReferenceTypeIds.HasDescription },
false,
false
);
false,
ct).ConfigureAwait(false);

return encodings.Where(r => supportedEncodings.Contains(r.BrowseName.Name))
.Select(r => ExpandedNodeId.ToNodeId(r.NodeId, m_session.NamespaceUris)).ToList();
}

/// <inheritdoc/>
public IList<NodeId> BrowseForEncodings(
public async Task<(IList<NodeId> encodings, ExpandedNodeId binaryEncodingId, ExpandedNodeId xmlEncodingId)> BrowseForEncodingsAsync(
ExpandedNodeId nodeId,
string[] supportedEncodings,
out ExpandedNodeId binaryEncodingId,
out ExpandedNodeId xmlEncodingId)
CancellationToken ct = default)
{
var references = m_session.NodeCache.FindReferences(
nodeId,
ReferenceTypeIds.HasEncoding,
false,
false
);
IList<INode> references = await m_session.NodeCache.FindReferencesAsync(
nodeId,
ReferenceTypeIds.HasEncoding,
false,
false,
ct).ConfigureAwait(false);

binaryEncodingId = references.FirstOrDefault(r => r.BrowseName.Name == BrowseNames.DefaultBinary)?.NodeId;
ExpandedNodeId binaryEncodingId = references.FirstOrDefault(r => r.BrowseName.Name == BrowseNames.DefaultBinary)?.NodeId;
binaryEncodingId = NormalizeExpandedNodeId(binaryEncodingId);
xmlEncodingId = references.FirstOrDefault(r => r.BrowseName.Name == BrowseNames.DefaultXml)?.NodeId;
ExpandedNodeId xmlEncodingId = references.FirstOrDefault(r => r.BrowseName.Name == BrowseNames.DefaultXml)?.NodeId;
xmlEncodingId = NormalizeExpandedNodeId(xmlEncodingId);
return references.Where(r => supportedEncodings.Contains(r.BrowseName.Name))
.Select(r => ExpandedNodeId.ToNodeId(r.NodeId, m_session.NamespaceUris)).ToList();
return (references
.Where(r => supportedEncodings.Contains(r.BrowseName.Name))
.Select(r => ExpandedNodeId.ToNodeId(r.NodeId, m_session.NamespaceUris))
.ToList(), binaryEncodingId, xmlEncodingId);
}

/// <inheritdoc/>
public bool BrowseTypeIdsForDictionaryComponent(
public async Task<(ExpandedNodeId typeId, ExpandedNodeId encodingId, DataTypeNode dataTypeNode)> BrowseTypeIdsForDictionaryComponentAsync(
ExpandedNodeId nodeId,
out ExpandedNodeId typeId,
out ExpandedNodeId encodingId,
out DataTypeNode dataTypeNode)
CancellationToken ct = default)
{
typeId = ExpandedNodeId.Null;
encodingId = ExpandedNodeId.Null;
dataTypeNode = null;
ExpandedNodeId encodingId;
DataTypeNode dataTypeNode;

var references = m_session.NodeCache.FindReferences(
IList<INode> references = await m_session.NodeCache.FindReferencesAsync(
nodeId,
ReferenceTypeIds.HasDescription,
true,
false
);
false,
ct).ConfigureAwait(false);

if (references.Count == 1)
{
encodingId = references[0].NodeId;
references = m_session.NodeCache.FindReferences(
references = await m_session.NodeCache.FindReferencesAsync(
encodingId,
ReferenceTypeIds.HasEncoding,
true,
false
);
false,
ct).ConfigureAwait(false);
encodingId = NormalizeExpandedNodeId(encodingId);

if (references.Count == 1)
{
typeId = references[0].NodeId;
ExpandedNodeId typeId = references[0].NodeId;
dataTypeNode = m_session.NodeCache.Find(typeId) as DataTypeNode;
typeId = NormalizeExpandedNodeId(typeId);
return true;
return (typeId, encodingId, dataTypeNode);
}
}
return false;
return (null, null, null);

Check warning on line 160 in Libraries/Opc.Ua.Client.ComplexTypes/TypeResolver/NodeCacheResolver.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Client.ComplexTypes/TypeResolver/NodeCacheResolver.cs#L160

Added line #L160 was not covered by tests
}

/// <inheritdoc/>
public IList<INode> LoadDataTypes(
public async Task<IList<INode>> LoadDataTypesAsync(
ExpandedNodeId dataType,
bool nestedSubTypes = false,
bool addRootNode = false,
bool filterUATypes = true)
bool filterUATypes = true,
CancellationToken ct = default)
{
var result = new List<INode>();
var nodesToBrowse = new ExpandedNodeIdCollection {
Expand All @@ -178,7 +180,7 @@

if (addRootNode)
{
var rootNode = m_session.NodeCache.Find(dataType);
INode rootNode = await m_session.NodeCache.FindAsync(dataType, ct).ConfigureAwait(false);

Check warning on line 183 in Libraries/Opc.Ua.Client.ComplexTypes/TypeResolver/NodeCacheResolver.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Client.ComplexTypes/TypeResolver/NodeCacheResolver.cs#L183

Added line #L183 was not covered by tests
if (!(rootNode is DataTypeNode))
{
throw new ServiceResultException("Root Node is not a DataType node.");
Expand All @@ -188,11 +190,12 @@

while (nodesToBrowse.Count > 0)
{
var response = m_session.NodeCache.FindReferences(
IList<INode> response = await m_session.NodeCache.FindReferencesAsync(
nodesToBrowse,
new NodeIdCollection { ReferenceTypeIds.HasSubtype },
false,
false);
false,
ct).ConfigureAwait(false);

var nextNodesToBrowse = new ExpandedNodeIdCollection();
if (nestedSubTypes)
Expand Down Expand Up @@ -221,35 +224,35 @@
}

/// <inheritdoc/>
public INode Find(ExpandedNodeId nodeId)
public Task<INode> FindAsync(ExpandedNodeId nodeId, CancellationToken ct)
{
return m_session.NodeCache.Find(nodeId);
return m_session.NodeCache.FindAsync(nodeId, ct);

Check warning on line 229 in Libraries/Opc.Ua.Client.ComplexTypes/TypeResolver/NodeCacheResolver.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Client.ComplexTypes/TypeResolver/NodeCacheResolver.cs#L229

Added line #L229 was not covered by tests
}

/// <inheritdoc/>
public object GetEnumTypeArray(ExpandedNodeId nodeId)
public async Task<object> GetEnumTypeArrayAsync(ExpandedNodeId nodeId, CancellationToken ct = default)
{
// find the property reference for the enum type
var references = m_session.NodeCache.FindReferences(
IList<INode> references = await m_session.NodeCache.FindReferencesAsync(

Check warning on line 236 in Libraries/Opc.Ua.Client.ComplexTypes/TypeResolver/NodeCacheResolver.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Client.ComplexTypes/TypeResolver/NodeCacheResolver.cs#L236

Added line #L236 was not covered by tests
nodeId,
ReferenceTypeIds.HasProperty,
false,
false
);
var property = references.FirstOrDefault();
false,
ct).ConfigureAwait(false);
INode property = references.FirstOrDefault();

Check warning on line 242 in Libraries/Opc.Ua.Client.ComplexTypes/TypeResolver/NodeCacheResolver.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Client.ComplexTypes/TypeResolver/NodeCacheResolver.cs#L240-L242

Added lines #L240 - L242 were not covered by tests
if (property != null)
{
// read the enum type array
DataValue value = m_session.ReadValue(ExpandedNodeId.ToNodeId(property.NodeId, NamespaceUris));
DataValue value = await m_session.ReadValueAsync(ExpandedNodeId.ToNodeId(property.NodeId, NamespaceUris), ct).ConfigureAwait(false);

Check warning on line 246 in Libraries/Opc.Ua.Client.ComplexTypes/TypeResolver/NodeCacheResolver.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Client.ComplexTypes/TypeResolver/NodeCacheResolver.cs#L246

Added line #L246 was not covered by tests
return value?.Value;
}
return null;
}

/// <inheritdoc/>
public NodeId FindSuperType(NodeId typeId)
public Task<NodeId> FindSuperTypeAsync(NodeId typeId, CancellationToken ct = default)
{
return m_session.NodeCache.FindSuperType(typeId);
return m_session.NodeCache.FindSuperTypeAsync(typeId, ct);
}
#endregion IComplexTypeResolver

Expand Down
Loading