/// <summary> /// Returns the attribute with the specified attribute id. /// </summary> public AeEventAttribute GetAttribute(uint attributeId) { AeEventAttribute attribute = null; if (!m_attributes.TryGetValue(attributeId, out attribute)) { return(null); } return(attribute); }
/// <summary> /// Uses the BrowsePathDisplayText to compare two attributes. /// </summary> private static int CompareAttributes(AeEventAttribute x, AeEventAttribute y) { if (Object.ReferenceEquals(x, y)) { return(0); } if (Object.ReferenceEquals(x, null)) { return((Object.ReferenceEquals(y, null)) ? 0 : -1); } return(x.BrowsePathDisplayText.CompareTo(y.BrowsePathDisplayText)); }
/// <summary> /// Converts an event field to a locally useable attribute value. /// </summary> private object GetLocalAttributeValue(Variant fieldValue, AeEventAttribute attribute) { // check for null. if (fieldValue == Variant.Null) { return(null); } // check that the data type is what is expected. TypeInfo typeInfo = fieldValue.TypeInfo; if (typeInfo == null) { typeInfo = TypeInfo.Construct(fieldValue); } if (attribute.BuiltInType != BuiltInType.Variant && typeInfo.BuiltInType != attribute.BuiltInType) { return(null); } // check for expected array dimension. if (attribute.ValueRank >= 0 && typeInfo.ValueRank == ValueRanks.Scalar) { return(null); } if (typeInfo.ValueRank != ValueRanks.Scalar) { if (attribute.ValueRank == ValueRanks.ScalarOrOneDimension && typeInfo.ValueRank != 1) { return(null); } if (attribute.ValueRank != ValueRanks.Any && attribute.ValueRank != typeInfo.ValueRank) { return(null); } } // map to local value. return(m_mapper.GetLocalValue(fieldValue)); }
/// <summary> /// Gets the list of categories for the specified event types. /// </summary> public List <AeEventAttribute> GetAttributes(uint categoryId) { AeEventCategory category = null; if (!m_categories.TryGetValue(categoryId, out category)) { return(null); } List <AeEventAttribute> attributes = new List <AeEventAttribute>(); AeEventCategory subType = category; while (subType != null) { for (int ii = 0; ii < subType.Attributes.Count; ii++) { AeEventAttribute attribute = subType.Attributes[ii]; if (attribute.OverriddenDeclaration == null) { if (!attribute.Hidden) { attributes.Add(attribute); } } } AeEventCategory superType = null; if (!m_eventTypes.TryGetValue(subType.SuperTypeId, out superType)) { break; } subType = superType; } attributes.Sort(CompareAttributes); return(attributes); }
/// <summary> /// Updates the selected attributes for the current filters. /// </summary> private void UpdatedSelectAttributes() { SelectedAttributes = new List <AeEventAttribute>(); // add attributes which always requested. SelectedAttributes.Add(m_mapper.GetAttribute(Opc.Ua.ObjectTypeIds.BaseEventType, Opc.Ua.BrowseNames.EventId)); SelectedAttributes.Add(m_mapper.GetAttribute(Opc.Ua.ObjectTypeIds.BaseEventType, Opc.Ua.BrowseNames.EventType)); SelectedAttributes.Add(m_mapper.GetAttribute(Opc.Ua.ObjectTypeIds.BaseEventType, Opc.Ua.BrowseNames.SourceName)); SelectedAttributes.Add(m_mapper.GetAttribute(Opc.Ua.ObjectTypeIds.BaseEventType, Opc.Ua.BrowseNames.Time)); SelectedAttributes.Add(m_mapper.GetAttribute(Opc.Ua.ObjectTypeIds.BaseEventType, Opc.Ua.BrowseNames.Message)); SelectedAttributes.Add(m_mapper.GetAttribute(Opc.Ua.ObjectTypeIds.BaseEventType, Opc.Ua.BrowseNames.Severity)); // add tracking event attributes. if ((EventTypes & OpcRcw.Ae.Constants.TRACKING_EVENT) != 0) { SelectedAttributes.Add(m_mapper.GetAttribute(Opc.Ua.ObjectTypeIds.AuditEventType, Opc.Ua.BrowseNames.ClientUserId)); } // add condition event attributes. if ((EventTypes & OpcRcw.Ae.Constants.CONDITION_EVENT) != 0) { SelectedAttributes.Add(m_mapper.GetAttribute(Opc.Ua.ObjectTypeIds.ConditionType, Opc.Ua.BrowseNames.BranchId)); SelectedAttributes.Add(m_mapper.GetAttribute(Opc.Ua.ObjectTypeIds.ConditionType, Opc.Ua.BrowseNames.ConditionName)); SelectedAttributes.Add(m_mapper.GetAttribute(Opc.Ua.ObjectTypeIds.ConditionType, Opc.Ua.BrowseNames.Quality)); SelectedAttributes.Add(m_mapper.GetAttribute(Opc.Ua.ObjectTypeIds.ConditionType, Opc.Ua.BrowseNames.Comment)); SelectedAttributes.Add(m_mapper.GetAttribute(Opc.Ua.ObjectTypeIds.ConditionType, Opc.Ua.BrowseNames.ClientUserId)); SelectedAttributes.Add(m_mapper.GetAttribute(Opc.Ua.ObjectTypeIds.ConditionType, Opc.Ua.BrowseNames.EnabledState, Opc.Ua.BrowseNames.Id)); SelectedAttributes.Add(m_mapper.GetAttribute(Opc.Ua.ObjectTypeIds.AcknowledgeableConditionType, Opc.Ua.BrowseNames.AckedState, Opc.Ua.BrowseNames.Id)); SelectedAttributes.Add(m_mapper.GetAttribute(Opc.Ua.ObjectTypeIds.AlarmConditionType, Opc.Ua.BrowseNames.ActiveState, Opc.Ua.BrowseNames.Id)); SelectedAttributes.Add(m_mapper.GetAttribute(Opc.Ua.ObjectTypeIds.AlarmConditionType, Opc.Ua.BrowseNames.ActiveState, Opc.Ua.BrowseNames.TransitionTime)); SelectedAttributes.Add(m_mapper.GetAttribute(Opc.Ua.ObjectTypeIds.ExclusiveLimitAlarmType, Opc.Ua.BrowseNames.LimitState, Opc.Ua.BrowseNames.CurrentState)); SelectedAttributes.Add(m_mapper.GetAttribute(Opc.Ua.ObjectTypeIds.NonExclusiveLimitAlarmType, Opc.Ua.BrowseNames.HighHighState)); SelectedAttributes.Add(m_mapper.GetAttribute(Opc.Ua.ObjectTypeIds.NonExclusiveLimitAlarmType, Opc.Ua.BrowseNames.HighState)); SelectedAttributes.Add(m_mapper.GetAttribute(Opc.Ua.ObjectTypeIds.NonExclusiveLimitAlarmType, Opc.Ua.BrowseNames.LowState)); SelectedAttributes.Add(m_mapper.GetAttribute(Opc.Ua.ObjectTypeIds.NonExclusiveLimitAlarmType, Opc.Ua.BrowseNames.LowLowState)); } if (RequestedAttributeIds != null) { // update list for all requested attributes. foreach (KeyValuePair <uint, uint[]> pair in RequestedAttributeIds) { for (int ii = 0; ii < pair.Value.Length; ii++) { // check if already in list. bool found = false; for (int jj = 0; jj < SelectedAttributes.Count; jj++) { if (SelectedAttributes[jj] != null && SelectedAttributes[jj].LocalId == pair.Value[ii]) { found = true; break; } } if (found) { continue; } // verify that it exists. AeEventAttribute attribute = m_mapper.GetAttribute(pair.Value[ii]); if (attribute != null) { SelectedAttributes.Add(attribute); } } } } }
/// <summary> /// Translates an event notification in an AE event. /// </summary> public AeEvent TranslateNotification(Session session, EventFieldList e) { AeEvent e2 = new AeEvent(); // extract the required event fields. int index = 0; e2.EventId = ExtractField <byte[]>(e, index++, null); e2.EventType = ExtractField <NodeId>(e, index++, null); e2.SourceName = ExtractField <string>(e, index++, null); e2.Time = ExtractField <DateTime>(e, index++, DateTime.MinValue); e2.Message = ExtractField <LocalizedText>(e, index++, null); e2.Severity = ExtractField <ushort>(e, index++, 0); if ((EventTypes & OpcRcw.Ae.Constants.TRACKING_EVENT) != 0) { e2.AuditUserId = ExtractField <string>(e, index++, null); } if ((EventTypes & OpcRcw.Ae.Constants.CONDITION_EVENT) != 0) { e2.BranchId = ExtractField <byte[]>(e, index++, null); e2.ConditionName = ExtractField <string>(e, index++, null); e2.Quality = ExtractField <StatusCode>(e, index++, StatusCodes.Good); e2.Comment = ExtractField <LocalizedText>(e, index++, null); e2.CommentUserId = ExtractField <string>(e, index++, null); e2.EnabledState = ExtractField <bool>(e, index++, false); e2.AckedState = ExtractField <bool>(e, index++, false); e2.ActiveState = ExtractField <bool>(e, index++, false); e2.ActiveTime = ExtractField <DateTime>(e, index++, DateTime.MinValue); e2.LimitState = ExtractField <LocalizedText>(e, index++, null); e2.HighHighState = ExtractField <LocalizedText>(e, index++, null); e2.HighState = ExtractField <LocalizedText>(e, index++, null); e2.LowState = ExtractField <LocalizedText>(e, index++, null); e2.LowLowState = ExtractField <LocalizedText>(e, index++, null); // condition id is always last. e2.ConditionId = ExtractField <NodeId>(e, e.EventFields.Count - 1, null); } // find the category for the event. e2.Category = FindCategory(session, e2); // extract any additional attributes. if (RequestedAttributeIds != null) { uint[] attributeIds = null; if (!RequestedAttributeIds.TryGetValue(e2.Category.LocalId, out attributeIds)) { return(e2); } // nothing more to do. if (attributeIds == null || attributeIds.Length == 0) { return(e2); } // search for the requested attributes. object[] values = new object[attributeIds.Length]; for (int ii = 0; ii < attributeIds.Length; ii++) { // look for matching attribute. for (int jj = 0; jj < SelectedAttributes.Count; jj++) { if (jj >= e.EventFields.Count) { break; } AeEventAttribute attribute = SelectedAttributes[jj]; if (attribute == null || attribute.LocalId != attributeIds[ii]) { continue; } values[ii] = GetLocalAttributeValue(e.EventFields[jj], attribute); } } e2.AttributeValues = values; } return(e2); }
/// <summary> /// Assigns a locally unique numeric id to each event type. /// </summary> private void UpdateEventAttributeMappings(NodeIdMappingSet mappingSet) { NodeIdMappingCollection mappingsToKeep = new NodeIdMappingCollection(); // collect all unique declarations. List <AeEventAttribute> list = new List <AeEventAttribute>(); foreach (AeEventCategory type in m_eventTypes.Values) { for (int ii = 0; ii < type.Attributes.Count; ii++) { AeEventAttribute declaration = type.Attributes[ii]; // only variables can be attributes. if (declaration.NodeClass != NodeClass.Variable) { continue; } // need to link attributes to any attributes that they override. AeEventCategory subType = type; AeEventCategory superType = null; while (subType != null) { if (NodeId.IsNull(subType.SuperTypeId) || subType.TypeId == subType.SuperTypeId || subType.SuperTypeId == Opc.Ua.ObjectTypeIds.BaseObjectType) { list.Add(declaration); break; } if (!m_eventTypes.TryGetValue(subType.SuperTypeId, out superType)) { break; } for (int jj = 0; jj < superType.Attributes.Count; jj++) { if (superType.Attributes[jj].BrowsePathDisplayText == declaration.BrowsePathDisplayText) { declaration.OverriddenDeclaration = superType.Attributes[jj]; declaration = declaration.OverriddenDeclaration; break; } } subType = superType; } } } // look up ids for all attributes in master list. Dictionary <uint, AeEventAttribute> attributes = new Dictionary <uint, AeEventAttribute>(); for (int ii = 0; ii < list.Count; ii++) { AeEventAttribute declaration = list[ii]; for (int jj = 0; jj < mappingSet.Mappings.Count; jj++) { NodeIdMapping mapping = mappingSet.Mappings[jj]; try { // browse display paths always use local namespa indexes. if (declaration.BrowsePathDisplayText != mapping.BrowePath) { continue; } // need to convert the cached type id to a remote id. NodeId localId = NodeId.Parse(mapping.NodeId); NodeId remoteId = this.GetRemoteNodeId(localId); if (declaration.RootTypeId != remoteId) { continue; } // must update the saved integer id. if (!attributes.ContainsKey(mapping.IntegerId)) { declaration.LocalId = mapping.IntegerId; attributes[declaration.LocalId] = declaration; mappingsToKeep.Add(mapping); } // must assign a new one if a duplicate found. else { declaration.LocalId = 0; } } catch (Exception) { // ignore invalid mappings. } } } // assign new ids. uint nextId = 1; for (int ii = 0; ii < list.Count; ii++) { AeEventAttribute declaration = list[ii]; if (declaration.LocalId == 0) { // find a unique id. while (attributes.ContainsKey(nextId)) { nextId++; } // assign the id. declaration.LocalId = nextId; attributes[declaration.LocalId] = declaration; // save the mapping. NodeIdMapping mapping = new NodeIdMapping(); mapping.IntegerId = nextId; mapping.NodeId = this.GetLocalNodeId(declaration.RootTypeId).ToString(); mapping.BrowePath = declaration.BrowsePathDisplayText; mappingsToKeep.Add(mapping); } } // update mapping set. mappingSet.Mappings = mappingsToKeep; m_attributes = attributes; }
/// <summary> /// Collects instance declarations nodes from with a type. /// </summary> public static void CollectInstanceDeclarations( Session session, ComNamespaceMapper mapper, NodeState node, AeEventAttribute parent, List<AeEventAttribute> instances, IDictionary<string, AeEventAttribute> map) { List<BaseInstanceState> children = new List<BaseInstanceState>(); node.GetChildren(session.SystemContext, children); if (children.Count == 0) { return; } // process the children. for (int ii = 0; ii < children.Count; ii++) { BaseInstanceState instance = children[ii]; // only interested in objects and variables. if (instance.NodeClass != NodeClass.Object && instance.NodeClass != NodeClass.Variable) { return; } // ignore instances without a modelling rule. if (NodeId.IsNull(instance.ModellingRuleId)) { return; } // create a new declaration. AeEventAttribute declaration = new AeEventAttribute(); declaration.RootTypeId = (parent != null)?parent.RootTypeId:node.NodeId; declaration.NodeId = (NodeId)instance.NodeId; declaration.BrowseName = instance.BrowseName; declaration.NodeClass = instance.NodeClass; declaration.Description = (instance.Description != null)?instance.Description.ToString():null; // get data type information. BaseVariableState variable = instance as BaseVariableState; if (variable != null) { declaration.DataType = variable.DataType; declaration.ValueRank = variable.ValueRank; if (!NodeId.IsNull(variable.DataType)) { declaration.BuiltInType = DataTypes.GetBuiltInType(declaration.DataType, session.TypeTree); } } if (!LocalizedText.IsNullOrEmpty(instance.DisplayName)) { declaration.DisplayName = instance.DisplayName.Text; } else { declaration.DisplayName = instance.BrowseName.Name; } if (parent != null) { declaration.BrowsePath = new QualifiedNameCollection(parent.BrowsePath); declaration.BrowsePathDisplayText = Utils.Format("{0}/{1}", parent.BrowsePathDisplayText, mapper.GetLocalBrowseName(instance.BrowseName)); declaration.DisplayPath = Utils.Format("{0}/{1}", parent.DisplayPath, instance.DisplayName); } else { declaration.BrowsePath = new QualifiedNameCollection(); declaration.BrowsePathDisplayText = Utils.Format("{0}", instance.BrowseName); declaration.DisplayPath = Utils.Format("{0}", instance.DisplayName); } declaration.BrowsePath.Add(instance.BrowseName); // check if reading an overridden declaration. AeEventAttribute overriden = null; if (map.TryGetValue(declaration.BrowsePathDisplayText, out overriden)) { declaration.OverriddenDeclaration = overriden; } map[declaration.BrowsePathDisplayText] = declaration; // only interested in variables. if (instance.NodeClass == NodeClass.Variable) { instances.Add(declaration); } // recusively build tree. CollectInstanceDeclarations(session, mapper, instance, declaration, instances, map); } }
/// <summary> /// Collects instance declarations nodes from with a type. /// </summary> public static void CollectInstanceDeclarations( Session session, ComNamespaceMapper mapper, NodeId typeId, AeEventAttribute parent, List<AeEventAttribute> instances, IDictionary<string, AeEventAttribute> map) { // find the children. BrowseDescription nodeToBrowse = new BrowseDescription(); if (parent == null) { nodeToBrowse.NodeId = typeId; } else { nodeToBrowse.NodeId = parent.NodeId; } nodeToBrowse.BrowseDirection = BrowseDirection.Forward; nodeToBrowse.ReferenceTypeId = ReferenceTypeIds.HasChild; nodeToBrowse.IncludeSubtypes = true; nodeToBrowse.NodeClassMask = (uint)(NodeClass.Object | NodeClass.Variable); nodeToBrowse.ResultMask = (uint)BrowseResultMask.All; // ignore any browsing errors. ReferenceDescriptionCollection references = Browse(session, nodeToBrowse, false); if (references == null) { return; } // process the children. List<NodeId> nodeIds = new List<NodeId>(); List<AeEventAttribute> children = new List<AeEventAttribute>(); for (int ii = 0; ii < references.Count; ii++) { ReferenceDescription reference = references[ii]; if (reference.NodeId.IsAbsolute) { continue; } // create a new declaration. AeEventAttribute child = new AeEventAttribute(); child.RootTypeId = typeId; child.NodeId = (NodeId)reference.NodeId; child.BrowseName = reference.BrowseName; child.NodeClass = reference.NodeClass; if (!LocalizedText.IsNullOrEmpty(reference.DisplayName)) { child.DisplayName = reference.DisplayName.Text; } else { child.DisplayName = reference.BrowseName.Name; } if (parent != null) { child.BrowsePath = new QualifiedNameCollection(parent.BrowsePath); child.BrowsePathDisplayText = Utils.Format("{0}/{1}", parent.BrowsePathDisplayText, mapper.GetLocalBrowseName(reference.BrowseName)); child.DisplayPath = Utils.Format("{0}/{1}", parent.DisplayPath, reference.DisplayName); } else { child.BrowsePath = new QualifiedNameCollection(); child.BrowsePathDisplayText = Utils.Format("{0}", reference.BrowseName); child.DisplayPath = Utils.Format("{0}", reference.DisplayName); } child.BrowsePath.Add(reference.BrowseName); // check if reading an overridden declaration. AeEventAttribute overriden = null; if (map.TryGetValue(child.BrowsePathDisplayText, out overriden)) { child.OverriddenDeclaration = overriden; } map[child.BrowsePathDisplayText] = child; // add to list. children.Add(child); nodeIds.Add(child.NodeId); } // check if nothing more to do. if (children.Count == 0) { return; } // find the modelling rules. List<NodeId> modellingRules = FindTargetOfReference(session, nodeIds, Opc.Ua.ReferenceTypeIds.HasModellingRule, false); if (modellingRules != null) { for (int ii = 0; ii < nodeIds.Count; ii++) { children[ii].ModellingRule = modellingRules[ii]; // if the modelling rule is null then the instance is not part of the type declaration. if (NodeId.IsNull(modellingRules[ii])) { map.Remove(children[ii].BrowsePathDisplayText); } } } // update the descriptions. UpdateInstanceDescriptions(session, children, false); // recusively collect instance declarations for the tree below. for (int ii = 0; ii < children.Count; ii++) { if (!NodeId.IsNull(children[ii].ModellingRule)) { instances.Add(children[ii]); CollectInstanceDeclarations(session, mapper, typeId, children[ii], instances, map); } } }
/// <summary> /// Finds the targets for the specified reference. /// </summary> private static void UpdateInstanceDescriptions(Session session, List <AeEventAttribute> instances, bool throwOnError) { try { ReadValueIdCollection nodesToRead = new ReadValueIdCollection(); for (int ii = 0; ii < instances.Count; ii++) { ReadValueId nodeToRead = new ReadValueId(); nodeToRead.NodeId = instances[ii].NodeId; nodeToRead.AttributeId = Attributes.Description; nodesToRead.Add(nodeToRead); nodeToRead = new ReadValueId(); nodeToRead.NodeId = instances[ii].NodeId; nodeToRead.AttributeId = Attributes.DataType; nodesToRead.Add(nodeToRead); nodeToRead = new ReadValueId(); nodeToRead.NodeId = instances[ii].NodeId; nodeToRead.AttributeId = Attributes.ValueRank; nodesToRead.Add(nodeToRead); } // start the browse operation. DataValueCollection results = null; DiagnosticInfoCollection diagnosticInfos = null; session.Read( null, 0, TimestampsToReturn.Neither, nodesToRead, out results, out diagnosticInfos); ClientBase.ValidateResponse(results, nodesToRead); ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead); // update the instances. for (int ii = 0; ii < nodesToRead.Count; ii += 3) { AeEventAttribute instance = instances[ii / 3]; instance.Description = results[ii].GetValue <LocalizedText>(LocalizedText.Null).Text; instance.DataType = results[ii + 1].GetValue <NodeId>(NodeId.Null); instance.ValueRank = results[ii + 2].GetValue <int>(ValueRanks.Any); if (!NodeId.IsNull(instance.DataType)) { instance.BuiltInType = DataTypes.GetBuiltInType(instance.DataType, session.TypeTree); } } } catch (Exception exception) { if (throwOnError) { throw new ServiceResultException(exception, StatusCodes.BadUnexpectedError); } } }
/// <summary> /// Collects instance declarations nodes from with a type. /// </summary> public static void CollectInstanceDeclarations( Session session, ComNamespaceMapper mapper, NodeState node, AeEventAttribute parent, List <AeEventAttribute> instances, IDictionary <string, AeEventAttribute> map) { List <BaseInstanceState> children = new List <BaseInstanceState>(); node.GetChildren(session.SystemContext, children); if (children.Count == 0) { return; } // process the children. for (int ii = 0; ii < children.Count; ii++) { BaseInstanceState instance = children[ii]; // only interested in objects and variables. if (instance.NodeClass != NodeClass.Object && instance.NodeClass != NodeClass.Variable) { return; } // ignore instances without a modelling rule. if (NodeId.IsNull(instance.ModellingRuleId)) { return; } // create a new declaration. AeEventAttribute declaration = new AeEventAttribute(); declaration.RootTypeId = (parent != null)?parent.RootTypeId:node.NodeId; declaration.NodeId = (NodeId)instance.NodeId; declaration.BrowseName = instance.BrowseName; declaration.NodeClass = instance.NodeClass; declaration.Description = (instance.Description != null)?instance.Description.ToString():null; // get data type information. BaseVariableState variable = instance as BaseVariableState; if (variable != null) { declaration.DataType = variable.DataType; declaration.ValueRank = variable.ValueRank; if (!NodeId.IsNull(variable.DataType)) { declaration.BuiltInType = DataTypes.GetBuiltInType(declaration.DataType, session.TypeTree); } } if (!LocalizedText.IsNullOrEmpty(instance.DisplayName)) { declaration.DisplayName = instance.DisplayName.Text; } else { declaration.DisplayName = instance.BrowseName.Name; } if (parent != null) { declaration.BrowsePath = new QualifiedNameCollection(parent.BrowsePath); declaration.BrowsePathDisplayText = Utils.Format("{0}/{1}", parent.BrowsePathDisplayText, mapper.GetLocalBrowseName(instance.BrowseName)); declaration.DisplayPath = Utils.Format("{0}/{1}", parent.DisplayPath, instance.DisplayName); } else { declaration.BrowsePath = new QualifiedNameCollection(); declaration.BrowsePathDisplayText = Utils.Format("{0}", instance.BrowseName); declaration.DisplayPath = Utils.Format("{0}", instance.DisplayName); } declaration.BrowsePath.Add(instance.BrowseName); // check if reading an overridden declaration. AeEventAttribute overriden = null; if (map.TryGetValue(declaration.BrowsePathDisplayText, out overriden)) { declaration.OverriddenDeclaration = overriden; } map[declaration.BrowsePathDisplayText] = declaration; // only interested in variables. if (instance.NodeClass == NodeClass.Variable) { instances.Add(declaration); } // recusively build tree. CollectInstanceDeclarations(session, mapper, instance, declaration, instances, map); } }
/// <summary> /// Collects instance declarations nodes from with a type. /// </summary> public static void CollectInstanceDeclarations( Session session, ComNamespaceMapper mapper, NodeId typeId, AeEventAttribute parent, List <AeEventAttribute> instances, IDictionary <string, AeEventAttribute> map) { // find the children. BrowseDescription nodeToBrowse = new BrowseDescription(); if (parent == null) { nodeToBrowse.NodeId = typeId; } else { nodeToBrowse.NodeId = parent.NodeId; } nodeToBrowse.BrowseDirection = BrowseDirection.Forward; nodeToBrowse.ReferenceTypeId = ReferenceTypeIds.HasChild; nodeToBrowse.IncludeSubtypes = true; nodeToBrowse.NodeClassMask = (uint)(NodeClass.Object | NodeClass.Variable); nodeToBrowse.ResultMask = (uint)BrowseResultMask.All; // ignore any browsing errors. ReferenceDescriptionCollection references = Browse(session, nodeToBrowse, false); if (references == null) { return; } // process the children. List <NodeId> nodeIds = new List <NodeId>(); List <AeEventAttribute> children = new List <AeEventAttribute>(); for (int ii = 0; ii < references.Count; ii++) { ReferenceDescription reference = references[ii]; if (reference.NodeId.IsAbsolute) { continue; } // create a new declaration. AeEventAttribute child = new AeEventAttribute(); child.RootTypeId = typeId; child.NodeId = (NodeId)reference.NodeId; child.BrowseName = reference.BrowseName; child.NodeClass = reference.NodeClass; if (!LocalizedText.IsNullOrEmpty(reference.DisplayName)) { child.DisplayName = reference.DisplayName.Text; } else { child.DisplayName = reference.BrowseName.Name; } if (parent != null) { child.BrowsePath = new QualifiedNameCollection(parent.BrowsePath); child.BrowsePathDisplayText = Utils.Format("{0}/{1}", parent.BrowsePathDisplayText, mapper.GetLocalBrowseName(reference.BrowseName)); child.DisplayPath = Utils.Format("{0}/{1}", parent.DisplayPath, reference.DisplayName); } else { child.BrowsePath = new QualifiedNameCollection(); child.BrowsePathDisplayText = Utils.Format("{0}", reference.BrowseName); child.DisplayPath = Utils.Format("{0}", reference.DisplayName); } child.BrowsePath.Add(reference.BrowseName); // check if reading an overridden declaration. AeEventAttribute overriden = null; if (map.TryGetValue(child.BrowsePathDisplayText, out overriden)) { child.OverriddenDeclaration = overriden; } map[child.BrowsePathDisplayText] = child; // add to list. children.Add(child); nodeIds.Add(child.NodeId); } // check if nothing more to do. if (children.Count == 0) { return; } // find the modelling rules. List <NodeId> modellingRules = FindTargetOfReference(session, nodeIds, Opc.Ua.ReferenceTypeIds.HasModellingRule, false); if (modellingRules != null) { for (int ii = 0; ii < nodeIds.Count; ii++) { children[ii].ModellingRule = modellingRules[ii]; // if the modelling rule is null then the instance is not part of the type declaration. if (NodeId.IsNull(modellingRules[ii])) { map.Remove(children[ii].BrowsePathDisplayText); } } } // update the descriptions. UpdateInstanceDescriptions(session, children, false); // recusively collect instance declarations for the tree below. for (int ii = 0; ii < children.Count; ii++) { if (!NodeId.IsNull(children[ii].ModellingRule)) { instances.Add(children[ii]); CollectInstanceDeclarations(session, mapper, typeId, children[ii], instances, map); } } }
/// <summary> /// Uses the BrowsePathDisplayText to compare two attributes. /// </summary> private static int CompareAttributes(AeEventAttribute x, AeEventAttribute y) { if (Object.ReferenceEquals(x, y)) { return 0; } if (Object.ReferenceEquals(x, null)) { return (Object.ReferenceEquals(y, null)) ? 0 : -1; } return x.BrowsePathDisplayText.CompareTo(y.BrowsePathDisplayText); }