in src/Azure.IIoT.OpcUa.Publisher/src/Stack/Extensions/SessionEx.cs [680:839]
internal static async Task<ServiceResultModel?> CollectInstanceDeclarationsAsync(
this IOpcUaSession session, RequestHeader requestHeader, NodeId typeId,
InstanceDeclarationModel? parent, List<InstanceDeclarationModel> instances,
IDictionary<ImmutableRelativePath, InstanceDeclarationModel> map,
NamespaceFormat namespaceFormat, Opc.Ua.NodeClass? nodeClassMask = null,
CancellationToken ct = default)
{
// find the children of the type.
var nodeToBrowse = new BrowseDescriptionCollection
{
new BrowseDescription
{
NodeId = parent == null ? typeId : parent.NodeId.ToNodeId(session.MessageContext),
BrowseDirection = Opc.Ua.BrowseDirection.Forward,
ReferenceTypeId = ReferenceTypeIds.HasChild,
IncludeSubtypes = true,
NodeClassMask = (uint)Opc.Ua.NodeClass.Object |
(((uint?)nodeClassMask)
?? (uint)Opc.Ua.NodeClass.Variable | (uint)Opc.Ua.NodeClass.Method),
ResultMask = (uint)BrowseResultMask.All
}
};
await foreach (var result in session.BrowseAsync(requestHeader, null,
nodeToBrowse, ct).ConfigureAwait(false))
{
if (result.ErrorInfo != null)
{
return result.ErrorInfo;
}
if (result.References == null)
{
continue;
}
var references = result.References
.Where(r => !r.NodeId.IsAbsolute)
.ToList();
if (references.Count == 0)
{
continue;
}
// find the modelling rules.
var (targets, errorInfo2) = await session.FindAsync(requestHeader,
references.Select(r => (NodeId)r.NodeId),
ReferenceTypeIds.HasModellingRule, maxResults: 1, ct: ct).ConfigureAwait(false);
if (errorInfo2 != null)
{
return errorInfo2;
}
var referencesWithRules = targets
.Zip(references)
.ToList();
// Get the children.
var children = new Dictionary<NodeId, InstanceDeclarationModel>();
foreach (var (modellingRule, reference) in referencesWithRules)
{
var browseName = reference.BrowseName.AsString(session.MessageContext,
namespaceFormat);
var relativePath = ImmutableRelativePath.Create(parent?.BrowsePath,
"/" + browseName);
var nodeClass = reference.NodeClass.ToServiceType();
if (NodeId.IsNull(modellingRule.Node) || nodeClass == null)
{
// if the modelling rule is null then the instance is not part
// of the type declaration.
map.Remove(relativePath);
continue;
}
// create a new declaration.
map.TryGetValue(relativePath, out var overriden);
var displayName =
LocalizedText.IsNullOrEmpty(reference.DisplayName?.Text) ?
reference.BrowseName.Name : reference.DisplayName.AsString();
var child = new InstanceDeclarationModel
{
RootTypeId = typeId.AsString(session.MessageContext,
namespaceFormat),
NodeId = reference.NodeId.AsString(session.MessageContext,
namespaceFormat),
BrowseName = reference.BrowseName.AsString(session.MessageContext,
namespaceFormat),
NodeClass = nodeClass.Value,
DisplayPath = parent == null ?
displayName : $"{parent.DisplayPath}/{displayName}",
DisplayName = displayName,
BrowsePath = relativePath.Path,
ModellingRule = modellingRule.Name.AsString(session.MessageContext,
namespaceFormat),
ModellingRuleId = modellingRule.Node.AsString(session.MessageContext,
namespaceFormat),
OverriddenDeclaration = overriden
};
map[relativePath] = child;
// add to list.
children.Add((NodeId)reference.NodeId, child);
}
// check if nothing more to do.
if (children.Count == 0)
{
return null;
}
// Add variable metadata
var variables = children
.Where(v => v.Value.NodeClass == NodeClass.Variable)
.Select(v => v.Key);
var variableMetadata = new List<VariableMetadataModel>();
var errorInfo = await session.CollectVariableMetadataAsync(requestHeader,
variables, variableMetadata, namespaceFormat, ct).ConfigureAwait(false);
if (errorInfo != null)
{
return errorInfo;
}
foreach (var (nodeId, variable) in variables.Zip(variableMetadata))
{
children[nodeId] = children[nodeId] with { VariableMetadata = variable };
}
// Add method metadata
var methods = children
.Where(v => v.Value.NodeClass == NodeClass.Method)
.Select(v => v.Key);
var methodMetadata = new List<MethodMetadataModel>();
errorInfo = await session.CollectMethodMetadataAsync(requestHeader,
variables, methodMetadata, namespaceFormat, ct).ConfigureAwait(false);
if (errorInfo != null)
{
return errorInfo;
}
foreach (var (nodeId, method) in methods.Zip(methodMetadata))
{
children[nodeId] = children[nodeId] with { MethodMetadata = method };
}
// Add descriptions
var attributes = await session.ReadAttributeAsync<LocalizedText>(requestHeader,
children.Keys, Attributes.Description, ct).ConfigureAwait(false);
// TODO: Check attribute error info
foreach (var (nodeId, description) in children.Keys.Zip(attributes))
{
children[nodeId] = children[nodeId] with
{
Description = description.Item1.AsString()
};
}
// recusively collect instance declarations for the tree below.
foreach (var child in children.Values)
{
instances.Add(child);
await session.CollectInstanceDeclarationsAsync(requestHeader,
typeId, child, instances, map, namespaceFormat, ct: ct).ConfigureAwait(false);
}
}
return null;
}