/// <summary> /// Recursively updates a hierarchy with the nodes that already exist in an instance. /// </summary> /// <param name="instanceId">The instance identifier.</param> /// <param name="hierarchy">The hierarchy.</param> private void UpdateInstanceHierarchyWithInstance( ExpandedNodeId instanceId, InstanceDeclarationHierarchy hierarchy) { // the instance must be local to the address space. ILocalNode instance = m_nodes.Find(instanceId) as ILocalNode; if (instance == null) { throw ServiceResultException.Create( StatusCodes.BadNodeIdUnknown, "The node is not in the local address space.\r\nNodeId = {0}", instanceId); } // must be an object, variable or method. if ((instance.NodeClass & (NodeClass.Object | NodeClass.Variable | NodeClass.Method)) == 0) { throw ServiceResultException.Create( StatusCodes.BadNodeClassInvalid, "The instance node is not any Object, Variable or Method.\r\nNodeId = {0}\r\nNodeClass = {1}", instanceId, instance.NodeClass); } // find the root of the hierarchy. HierarchyBrowsePath root = null; if (!hierarchy.BrowsePaths.TryGetValue("/", out root)) { throw ServiceResultException.Create( StatusCodes.BadUnexpectedError, "The hierarchy does not have a root.\r\nHierarchy = {0}", hierarchy); } // the instance type definition must match the hierarchy. if (instance.TypeDefinitionId != root.DeclarationId) { throw ServiceResultException.Create( StatusCodes.BadTypeDefinitionInvalid, "The instance node is not an instance of a {0} type.\r\nNodeId = {1}\r\nTypeDefinition = {2}", root.DeclarationId, instanceId, instance.TypeDefinitionId); } // set the instance id for the root. root.InstanceId = instance.NodeId; // follow children. foreach (IReference reference in instance.References.Find(ReferenceTypeIds.HierarchicalReferences, false, true, m_nodes.TypeTree)) { UpdateInstanceHierarchyWithInstance(root, reference.TargetId, hierarchy); } }
/// <summary> /// Recursively collects the nodes within a type hierarchy. /// </summary> private void GetInstanceHierarchyForType( HierarchyBrowsePath parent, ExpandedNodeId instanceId, InstanceDeclarationHierarchy hierarchy) { // the instance must be local to the address space. ILocalNode instance = m_nodes.Find(instanceId) as ILocalNode; if (instance == null) { return; } // must be an object, variable or method. if ((instance.NodeClass & (NodeClass.Object | NodeClass.Variable | NodeClass.Method)) == 0) { return; } // get the naming rule. NamingRule namingRule = GetNamingRule(instance.ModellingRule); // only include instances with unique browse names in the hierarchy. if (namingRule != NamingRule.Unique && namingRule != NamingRule.UniqueOptional) { return; } // construct the browse path that identifies the node. string browsePath = null; if (parent.BrowsePath == "/") { browsePath = Utils.Format("/{0}", instance.BrowseName); } else { browsePath = Utils.Format("{0}/{1}", parent.BrowsePath, instance.BrowseName); } // check if the browse path already exists in the hierarchy. HierarchyBrowsePath child = null; if (!hierarchy.BrowsePaths.TryGetValue(browsePath, out child)) { child = new HierarchyBrowsePath(); child.BrowsePath = browsePath; child.DeclarationId = instance.NodeId; child.InstanceId = null; child.IsModelParent = false; child.IsOptional = namingRule != NamingRule.Unique; // add new browse path to hierarchy. hierarchy.BrowsePaths.Add(browsePath, child); } // override any declaration specified in a supertype. child.DeclarationId = instance.NodeId; // check if node has been processed via another path. HierarchyBrowsePath alternatePath = null; if (hierarchy.Declarations.TryGetValue(instance.NodeId, out alternatePath)) { // keep the model parent path as the primary path. if (!alternatePath.IsModelParent && child.IsModelParent) { hierarchy.Declarations[instance.NodeId] = child; } // nothing more to do since node has been processed once. return; } // save child. hierarchy.Declarations.Add(instance.NodeId, child); // follow children. foreach (IReference reference in instance.References.Find(ReferenceTypeIds.HierarchicalReferences, false, true, m_nodes.TypeTree)) { GetInstanceHierarchyForType(child, reference.TargetId, hierarchy); } }
/// <summary> /// Adds or updates a reference declaration. /// </summary> private void UpdateReferenceDeclaration( HierarchyBrowsePath source, IReference reference, InstanceDeclarationHierarchy hierarchy) { // ignore inverse references. if (reference.IsInverse) { return; } // ignore subtype references. if (m_nodes.TypeTree.IsTypeOf(reference.ReferenceTypeId, ReferenceTypeIds.HasSubtype)) { return; } // find the target browse path. HierarchyBrowsePath targetBrowsePath = null; if (!reference.TargetId.IsAbsolute) { if (!hierarchy.Declarations.TryGetValue((NodeId)reference.TargetId, out targetBrowsePath)) { targetBrowsePath = null; } } // ignore hierarchical references with targets that are outside the hierarchy. if (targetBrowsePath == null) { if (m_nodes.TypeTree.IsTypeOf(reference.ReferenceTypeId, ReferenceTypeIds.HierarchicalReferences)) { return; } } // type definition references are never relative to the hierarchy. if (m_nodes.TypeTree.IsTypeOf(reference.ReferenceTypeId, ReferenceTypeIds.HasTypeDefinition)) { targetBrowsePath = null; } // update existing references. int count = hierarchy.References.Count; for (int ii = 0; ii < count; ii++) { HierarchyReference existingReference = hierarchy.References[ii]; // match source browse path. if (existingReference.SourceBrowsePath != source.BrowsePath) { continue; } // allow subtypes of the reference type. if (!m_nodes.TypeTree.IsTypeOf(reference.ReferenceTypeId, existingReference.ReferenceTypeId)) { continue; } // match target browse path for internal references. if (targetBrowsePath != null) { if (existingReference.TargetBrowsePath != targetBrowsePath.BrowsePath) { continue; } } else { // check for external reference types where exactly one reference is permitted. bool singletonReferenceType = false; if (reference.ReferenceTypeId.IdType == IdType.Numeric && reference.ReferenceTypeId.NamespaceIndex == 0) { switch ((uint)reference.ReferenceTypeId.Identifier) { case ReferenceTypes.HasTypeDefinition: { singletonReferenceType = true; break; } case ReferenceTypes.HasModellingRule: { singletonReferenceType = true; break; } default: { break; } } } // add new reference if multiples permitted. if (!singletonReferenceType) { break; } } // update existing reference. existingReference.SourceDeclarationId = source.DeclarationId; existingReference.SourceInstanceId = null; existingReference.ReferenceTypeId = reference.ReferenceTypeId; existingReference.TargetBrowsePath = (targetBrowsePath != null)?targetBrowsePath.BrowsePath:null; existingReference.TargetDeclarationId = reference.TargetId; existingReference.TargetInstanceId = null; // only one reference with the same source/reference type/target allowed. return; } // create a new reference. HierarchyReference newReference = new HierarchyReference(); newReference.SourceBrowsePath = source.BrowsePath; newReference.SourceDeclarationId = source.DeclarationId; newReference.SourceInstanceId = null; newReference.ReferenceTypeId = reference.ReferenceTypeId; newReference.TargetBrowsePath = (targetBrowsePath != null)?targetBrowsePath.BrowsePath:null; newReference.TargetDeclarationId = reference.TargetId; newReference.TargetInstanceId = null; hierarchy.References.Add(newReference); }
/// <summary> /// Adds or updates the references /// </summary> private void InstantiateReferences( HierarchyBrowsePath source, InstanceDeclarationHierarchy hierarchy) { // don't add references to nodes that are not owned. if (!source.IsModelParent) { return; } // get the instance. ILocalNode instance = hierarchy.Instances[(NodeId)source.InstanceId] as ILocalNode; if (instance == null) { return; } // find references for the source. foreach (HierarchyReference reference in hierarchy.References) { // match source browse path. if (reference.SourceBrowsePath != source.BrowsePath) { continue; } // check if the target is in the hierarchy. HierarchyBrowsePath targetBrowsePath = null; // check declaration. if (!reference.TargetDeclarationId.IsAbsolute) { if (!hierarchy.Declarations.TryGetValue((NodeId)reference.TargetDeclarationId, out targetBrowsePath)) { targetBrowsePath = null; } } // check browse path. if (targetBrowsePath == null && reference.TargetBrowsePath != null) { if (!hierarchy.BrowsePaths.TryGetValue(reference.TargetBrowsePath, out targetBrowsePath)) { targetBrowsePath = null; } } // type definition references are never relative to the hierarchy. if (m_nodes.TypeTree.IsTypeOf(reference.ReferenceTypeId, ReferenceTypeIds.HasTypeDefinition)) { targetBrowsePath = null; } // target outside hierarchy. if (targetBrowsePath == null) { if (reference.TargetDeclarationId != null) { // check for existing type definition. if (reference.ReferenceTypeId == ReferenceTypeIds.HasTypeDefinition) { IList <IReference> existingReferences = instance.References.Find(reference.ReferenceTypeId, false, true, m_nodes.TypeTree); if (existingReferences.Count > 0) { if (m_nodes.TypeTree.IsTypeOf(existingReferences[0].TargetId, reference.TargetDeclarationId)) { continue; } instance.References.RemoveAll(ReferenceTypeIds.HasTypeDefinition, false); } } instance.References.Add(reference.ReferenceTypeId, false, reference.TargetDeclarationId); } continue; } // target inside hierarchy. if (targetBrowsePath.InstanceId != null) { instance.References.Add(reference.ReferenceTypeId, false, targetBrowsePath.InstanceId); } } }
/// <summary> /// Recursively builds the full inhierited type hierarchy starting with the top-level type. /// </summary> private void GetInstanceHierarchyForType(ExpandedNodeId typeId, InstanceDeclarationHierarchy hierarchy) { // the type must be local to the address space. ILocalNode type = m_nodes.Find(typeId) as ILocalNode; if (type == null) { throw ServiceResultException.Create( StatusCodes.BadNodeIdUnknown, "The type is not in the local address space.\r\nNodeId = {0}", typeId); } // must be an object or variable type. if ((type.NodeClass & (NodeClass.ObjectType | NodeClass.VariableType)) == 0) { throw ServiceResultException.Create( StatusCodes.BadNodeClassInvalid, "The type node is not an ObjectType or VariableType.\r\nNodeId = {0}\r\nNodeClass = {1}", typeId, type.NodeClass); } // find hierarchy in supertypes first. foreach (IReference reference in type.References.Find(ReferenceTypeIds.HasSubtype, true, false, null)) { GetInstanceHierarchyForType(reference.TargetId, hierarchy); } string browsePath = "/"; // check if the browse path already exists in the hierarchy. HierarchyBrowsePath parent = null; if (!hierarchy.BrowsePaths.TryGetValue(browsePath, out parent)) { parent = new HierarchyBrowsePath(); parent.BrowsePath = browsePath; parent.DeclarationId = type.NodeId; parent.InstanceId = null; parent.IsModelParent = true; parent.IsOptional = false; // add new browse path to hierarchy. hierarchy.BrowsePaths.Add(browsePath, parent); } // override any declaration specified in a supertype. parent.DeclarationId = type.NodeId; hierarchy.Declarations[type.NodeId] = parent; // follow hierarchial references to nodes with a naming rule of unique or unique optional. foreach (IReference reference in type.References.Find(ReferenceTypeIds.HierarchicalReferences, false, true, m_nodes.TypeTree)) { GetInstanceHierarchyForType(parent, reference.TargetId, hierarchy); } // update references defined in the type. foreach (HierarchyBrowsePath declaration in hierarchy.Declarations.Values) { // the declaration must be local to the address space. ILocalNode declarationNode = m_nodes.Find(declaration.DeclarationId) as ILocalNode; if (declarationNode == null) { continue; } // process all references. foreach (IReference reference in declarationNode.References) { UpdateReferenceDeclaration(declaration, reference, hierarchy); } } }
/// <summary> /// Recursively collects the nodes within a type hierarchy. /// </summary> private void UpdateInstanceHierarchyWithInstance( HierarchyBrowsePath parent, ExpandedNodeId instanceId, InstanceDeclarationHierarchy hierarchy) { INode instance = m_nodes.Find(instanceId) as INode; // ignore instances not in the address space. if (instance == null) { return; } // must be an object, variable or method. if ((instance.NodeClass & (NodeClass.Object | NodeClass.Variable | NodeClass.Method)) == 0) { return; } // construct the browse path that identifies the node. string browsePath = null; if (parent.BrowsePath == "/") { browsePath = Utils.Format("/{0}", instance.BrowseName); } else { browsePath = Utils.Format("{0}/{1}", parent.BrowsePath, instance.BrowseName); } // check if the browse path exists in the hierarchy. HierarchyBrowsePath child = null; if (!hierarchy.BrowsePaths.TryGetValue(browsePath, out child)) { return; } // update the instance. child.InstanceId = instance.NodeId; // check if already followed. if (hierarchy.Instances.ContainsKey((NodeId)instance.NodeId)) { return; } // save child. hierarchy.Instances.Add((NodeId)instance.NodeId, instance); // check for local node. ILocalNode localInstance = instance as ILocalNode; if (localInstance == null) { return; } // follow children. foreach (IReference reference in localInstance.References.Find(ReferenceTypeIds.HierarchicalReferences, false, true, m_nodes.TypeTree)) { UpdateInstanceHierarchyWithInstance(child, reference.TargetId, hierarchy); } }