/// <summary> /// Saves the element in cache. /// </summary> /// <param name="element">The element.</param> private void SaveElementInCache(DaElement element) { lock (m_cache) { m_cache[element.ItemId] = element; } }
/// <summary> /// Finds the element and updates the name if it is not already cached. /// </summary> /// <param name="itemId">The item id.</param> /// <param name="name">The name.</param> /// <param name="parentId">The parent id.</param> /// <returns>The element.</returns> private DaElement FindElement(string itemId, string name, string parentId) { if (String.IsNullOrEmpty(itemId)) { return(null); } // look in cache for existing element. DaElement element = null; lock (m_cache) { if (!m_cache.TryGetValue(itemId, out element)) { element = null; } } // create a new element. if (element == null) { element = CreateElement(itemId, name, parentId); SaveElementInCache(element); } // update the element. element.Name = name; element.ParentId = parentId; return(element); }
/// <summary> /// Creates a new element. /// </summary> /// <param name="itemId">The item id.</param> /// <param name="name">The name.</param> /// <param name="parentId">The parent id.</param> /// <returns>The element.</returns> private DaElement CreateElement(string itemId, string name, string parentId) { DaElement element = new DaElement(); element.ItemId = itemId; UpdateElement(element, name, parentId); return(element); }
/// <summary> /// Constructs a branch or item node from a DaElement returned from the COM server. /// </summary> /// <param name="context">The context.</param> /// <param name="element">The element.</param> /// <param name="namespaceIndex">Index of the namespace for the NodeId.</param> /// <returns>The node.</returns> public static NodeState ConstructElement(ISystemContext context, DaElement element, ushort namespaceIndex) { if (element.ElementType == DaElementType.Branch) { return(new DaBranchState(context, element, namespaceIndex)); } return(new DaItemState(context, element, namespaceIndex)); }
/// <summary> /// Finds the property metadata for the specified item id. /// </summary> /// <param name="itemId">The item id.</param> /// <param name="propertyId">The property id.</param> /// <returns>The metadata for the property.</returns> public DaProperty FindProperty(string itemId, int propertyId) { if (String.IsNullOrEmpty(itemId)) { return(null); } // check the cache. DaElement element = null; lock (m_cache) { if (m_cache.TryGetValue(itemId, out element)) { if (element.Properties != null) { for (int ii = 0; ii < element.Properties.Length; ii++) { if (element.Properties[ii].PropertyId == propertyId) { return(element.Properties[ii]); } } } } } // check if the element has to be loaded. if (element == null) { element = FindElement(itemId); if (element == null) { return(null); } } // update the property list. element.Properties = ReadAvailableProperties(itemId, false); if (element.Properties != null) { for (int ii = 0; ii < element.Properties.Length; ii++) { if (element.Properties[ii].PropertyId == propertyId) { return(element.Properties[ii]); } } } // not found. return(null); }
/// <summary> /// Initializes the node from the element. /// </summary> /// <param name="context">The context.</param> /// <param name="element">The element.</param> /// <param name="namespaceIndex">Index of the namespace.</param> public void Initialize(ISystemContext context, DaElement element, ushort namespaceIndex) { m_element = element; if (element == null) { return; } this.NodeId = ModelUtils.ConstructIdForDaElement(element.ItemId, -1, namespaceIndex); this.BrowseName = new QualifiedName(element.Name, namespaceIndex); this.DisplayName = new LocalizedText(element.Name); }
/// <summary> /// Initializes a new instance of the <see cref="DaItemState"/> class. /// </summary> /// <param name="context">The context.</param> /// <param name="element">The element.</param> /// <param name="namespaceIndex">Index of the namespace.</param> public DaItemState( ISystemContext context, DaElement element, ushort namespaceIndex) : base(null) { this.TypeDefinitionId = Opc.Ua.VariableTypeIds.DataItemType; this.Description = null; this.WriteMask = 0; this.UserWriteMask = 0; if (element != null) { Initialize(context, element, namespaceIndex); } }
/// <summary> /// Finds the item id for the parent of the element. /// </summary> /// <param name="itemId">The item id.</param> /// <returns>The item id for the parent of the element.</returns> public string FindElementParentId(string itemId) { if (String.IsNullOrEmpty(itemId)) { return(null); } // check in cache. DaElement element = null; lock (m_cache) { if (m_cache.TryGetValue(itemId, out element)) { if (element.ParentId != null) { return(element.ParentId); } } } // try extracting the name by parsing the item id. string name = null; string parentId = null; if (ParseItemId(itemId, out name, out parentId)) { element = CreateElement(itemId, name, parentId); } // need to do it the hard way by searching the address space. else { IDaElementBrowser browser = CreateBrowser(itemId); element = browser.Find(itemId, false); browser.Dispose(); } // save element in the cache. if (element != null) { SaveElementInCache(element); } return(element.ParentId); }
/// <summary> /// Initializes a new instance of the <see cref="DaBranchState"/> class. /// </summary> /// <param name="context">The context.</param> /// <param name="element">The element.</param> /// <param name="namespaceIndex">Index of the namespace.</param> public DaBranchState( ISystemContext context, DaElement element, ushort namespaceIndex) : base(null) { this.TypeDefinitionId = Opc.Ua.ObjectTypeIds.FolderType; this.Description = null; this.WriteMask = 0; this.UserWriteMask = 0; this.EventNotifier = EventNotifiers.None; if (element != null) { Initialize(context, element, namespaceIndex); } }
/// <summary> /// Initializes a new instance of the <see cref="DaBranchState"/> class. /// </summary> /// <param name="context">The context.</param> /// <param name="element">The element.</param> /// <param name="namespaceIndex">Index of the namespace.</param> public DaBranchState( ISystemContext context, DaElement element, ushort namespaceIndex) : base(null) { this.TypeDefinitionId = Opc.Ua.ObjectTypeIds.FolderType; this.Description = null; this.WriteMask = 0; this.UserWriteMask = 0; this.EventNotifier = EventNotifiers.None; if (element != null) { Initialize(context, element, namespaceIndex); } }
/// <summary> /// Initializes the node from the element. /// </summary> /// <param name="context">The context.</param> /// <param name="element">The element.</param> /// <param name="namespaceIndex">Index of the namespace.</param> public void Initialize(ISystemContext context, DaElement element, ushort namespaceIndex) { m_element = element; if (element == null) { return; } this.NodeId = ModelUtils.ConstructIdForDaElement(element.ItemId, -1, namespaceIndex); this.BrowseName = new QualifiedName(element.Name, namespaceIndex); this.DisplayName = new LocalizedText(element.Name); }
/// <summary> /// Finds the element with the specified item id. /// </summary> /// <param name="parentId">The parent id.</param> /// <param name="targetId">The target id.</param> /// <param name="isItem">if set to <c>true</c> the element is a item.</param> /// <param name="recursive">if set to <c>true</c> [recursive].</param> /// <returns>The element if found.</returns> public DaElement Find(string parentId, string targetId, bool isItem, bool recursive) { // create the enumerator if not already created. m_enumerator = m_clone.CreateEnumerator(false); // a null indicates an error. if (m_enumerator == null) { return(null); } string name = null; // process all items. do { // fetch the next name. name = m_enumerator.Next(); // a null indicates the end of list. if (name == null) { break; } // fetch the item id. string itemId = m_clone.GetItemId(name); // fetch the metadata. DaElement element = m_client.FindElement(itemId, name, parentId); // check if target found. if (targetId == itemId) { return(element); } }while (true); m_enumerator.Dispose(); m_enumerator = null; List <DaElement> branches = new List <DaElement>(); // fetch the branches of the target is a branch or if a recursive search is required. if (!isItem || recursive) { // need to fetch list of branches to search. m_enumerator = m_clone.CreateEnumerator(true); if (m_enumerator == null) { return(null); } // process all branches. do { name = m_enumerator.Next(); // a null indicates the end of list. if (name == null) { break; } // fetch the item id. string itemId = m_clone.GetItemId(name); // fetch the metadata. DaElement element = m_client.FindElement(itemId, name, parentId); // save branch for recursive search if not found at this level. if (recursive) { branches.Add(element); } // check if target found. if (targetId == itemId) { return(element); } }while (name != null); m_enumerator.Dispose(); m_enumerator = null; } // all done if not doing a recursive search. if (!recursive) { return(null); } // recursively search hierarchy. for (int ii = 0; ii < branches.Count; ii++) { m_clone.ChangeBrowsePosition(OPCBROWSEDIRECTION.OPC_BROWSE_DOWN, branches[ii].Name); DaElement element = Find(branches[ii].ItemId, targetId, isItem, recursive); if (element != null) { return(element); } m_clone.ChangeBrowsePosition(OPCBROWSEDIRECTION.OPC_BROWSE_UP, String.Empty); } // not found. return(null); }
/// <summary> /// Returns the next child. /// </summary> private NodeStateReference NextChild(Stage stage) { ComDaClientManager system = (ComDaClientManager)this.SystemContext.SystemHandle; ComDaClient client = system.SelectClient((ServerSystemContext)SystemContext); DaElement element = null; if (stage == Stage.Children) { if (m_browser == null) { return(null); } element = m_browser.Next(); if (element == null) { return(null); } // construct the node. NodeState node = ModelUtils.ConstructElement(SystemContext, element, m_namespaceIndex); // return the reference. return(new NodeStateReference(ReferenceTypeIds.Organizes, false, node)); } if (stage == Stage.Properties) { if (m_properties == null) { return(null); } for (int ii = m_position; ii < m_properties.Length; ii++) { if (m_properties[ii].PropertyId <= PropertyIds.TimeZone) { continue; } m_position = ii + 1; // construct the node. NodeState node = ModelUtils.ConstructProperty(SystemContext, m_itemId, m_properties[ii], m_namespaceIndex); // return the reference. return(new NodeStateReference(ReferenceTypeIds.HasProperty, false, node)); } // all done. return(null); } if (stage == Stage.Parents) { if (m_parentId != null) { NodeId parentId = ModelUtils.ConstructIdForDaElement(m_parentId, -1, m_namespaceIndex); m_parentId = null; return(new NodeStateReference(ReferenceTypeIds.Organizes, true, parentId)); } } return(null); }
/// <summary> /// Initializes the node from the element. /// </summary> /// <param name="context">The context.</param> /// <param name="element">The element.</param> /// <param name="namespaceIndex">Index of the namespace.</param> public void Initialize(ISystemContext context, DaElement element, ushort namespaceIndex) { m_element = element; if (element == null) { return; } this.NodeId = ModelUtils.ConstructIdForDaElement(element.ItemId, -1, namespaceIndex); this.BrowseName = new QualifiedName(element.Name, namespaceIndex); this.DisplayName = new LocalizedText(element.Name); // check if TimeZone is supported. if (element.TimeZone != null) { PropertyState property = this.AddProperty <Range>(Opc.Ua.BrowseNames.TimeZone, DataTypeIds.TimeZoneDataType, ValueRanks.Scalar); property.NodeId = ModelUtils.ConstructIdForComponent(property, namespaceIndex); property.Value = new Range(element.HighIR, element.LowIR); } // set the TypeDefinition based on the ElementType. switch (element.ElementType) { case DaElementType.AnalogItem: { this.TypeDefinitionId = Opc.Ua.VariableTypeIds.AnalogItemType; // EURange is always present. PropertyState property = this.AddProperty <Range>(Opc.Ua.BrowseNames.EURange, DataTypeIds.Range, ValueRanks.Scalar); property.NodeId = ModelUtils.ConstructIdForComponent(property, namespaceIndex); property.Value = new Range(element.HighEU, element.LowEU); // check if InstrumentRange is supported. if (element.HighIR != 0 || element.LowIR != 0) { property = this.AddProperty <Range>(Opc.Ua.BrowseNames.InstrumentRange, DataTypeIds.Range, ValueRanks.Scalar); property.NodeId = ModelUtils.ConstructIdForComponent(property, namespaceIndex); property.Value = new Range(element.HighIR, element.LowIR); } // check if EngineeringUnits is supported. if (element.EngineeringUnits != null) { property = this.AddProperty <EUInformation>(Opc.Ua.BrowseNames.EngineeringUnits, DataTypeIds.EUInformation, ValueRanks.Scalar); property.NodeId = ModelUtils.ConstructIdForComponent(property, namespaceIndex); // use the server's namespace uri to qualify the engineering units. string namespaceUri = context.NamespaceUris.GetString(namespaceIndex); property.Value = new EUInformation(element.EngineeringUnits, namespaceUri); } break; } case DaElementType.DigitalItem: { this.TypeDefinitionId = Opc.Ua.VariableTypeIds.TwoStateDiscreteType; this.EnumStrings = new PropertyState <LocalizedText[]>(this); // check if CloseLabel is supported. if (element.CloseLabel != null) { PropertyState property = this.AddProperty <LocalizedText>(Opc.Ua.BrowseNames.TrueState, DataTypeIds.LocalizedText, ValueRanks.Scalar); property.NodeId = ModelUtils.ConstructIdForComponent(property, namespaceIndex); property.Value = element.CloseLabel; } // check if OpenLabel is supported. if (element.OpenLabel != null) { PropertyState property = this.AddProperty <LocalizedText>(Opc.Ua.BrowseNames.FalseState, DataTypeIds.LocalizedText, ValueRanks.Scalar); property.NodeId = ModelUtils.ConstructIdForComponent(property, namespaceIndex); property.Value = element.OpenLabel; } break; } case DaElementType.EnumeratedItem: { this.TypeDefinitionId = Opc.Ua.VariableTypeIds.MultiStateDiscreteType; // check if EuInfo is supported. if (element.EuInfo != null) { PropertyState property = this.AddProperty <LocalizedText[]>(Opc.Ua.BrowseNames.EnumStrings, DataTypeIds.LocalizedText, ValueRanks.OneDimension); property.NodeId = ModelUtils.ConstructIdForComponent(property, namespaceIndex); LocalizedText[] strings = new LocalizedText[element.EuInfo.Length]; for (int ii = 0; ii < strings.Length; ii++) { strings[ii] = element.EuInfo[ii]; } property.Value = strings; } break; } } if (element.Description != null) { this.Description = element.Description; } this.Value = null; this.StatusCode = StatusCodes.BadWaitingForInitialData; this.Timestamp = DateTime.UtcNow; bool isArray = false; this.DataType = ComUtils.GetDataTypeId(element.DataType, out isArray); this.ValueRank = (isArray)?ValueRanks.OneOrMoreDimensions:ValueRanks.Scalar; this.AccessLevel = AccessLevels.None; if ((element.AccessRights & OpcRcw.Da.Constants.OPC_READABLE) != 0) { this.AccessLevel |= AccessLevels.CurrentRead; } if ((element.AccessRights & OpcRcw.Da.Constants.OPC_WRITEABLE) != 0) { this.AccessLevel |= AccessLevels.CurrentWrite; } this.UserAccessLevel = this.AccessLevel; this.MinimumSamplingInterval = element.ScanRate; }
/// <summary> /// Returns the next DA element. /// </summary> /// <returns>A DA element. Null if nothing left to browse.</returns> public DaElement Next() { // check if already completed. if (m_completed) { return(null); } // create the enumerator if not already created. if (m_enumerator == null) { // need to clone the client since ChangeBrowsePosition prevents multiple // simultaneous browse operations. m_clone = m_client.CloneClient(); m_clone.CreateInstance(); // nothing to browse if change browse position failed. if (!m_clone.ChangeBrowsePosition(OPCBROWSEDIRECTION.OPC_BROWSE_TO, m_itemId)) { return(null); } m_enumerator = m_clone.CreateEnumerator(false); m_branches = false; // a null indicates an error. if (m_enumerator == null) { m_completed = true; return(null); } } // need a loop in case errors occur fetching element metadata. DaElement element = null; do { // fetch the next name. string name = m_enumerator.Next(); // a null indicates the end of list. if (name == null) { if (!m_branches) { m_branches = true; m_enumerator.Dispose(); m_enumerator = m_clone.CreateEnumerator(true); continue; } m_completed = true; return(null); } // suppress duplicates when a item is also a branch. if (m_branches) { if (m_itemNames != null) { if (m_itemNames.ContainsKey(name)) { continue; } } } // save the item name to allow checks for duplicates. else { if (m_itemNames == null) { m_itemNames = new Dictionary <string, object>(); } m_itemNames[name] = null; } // fetch the item id. string itemId = m_clone.GetItemId(name); // fetch the metadata. element = m_client.FindElement(itemId, name, m_itemId); }while (element == null); // return element. return(element); }
/// <summary> /// Updates a element. /// </summary> /// <param name="element">The element.</param> /// <param name="name">The name.</param> /// <param name="parentId">The parent id.</param> private void UpdateElement(DaElement element, string name, string parentId) { // only update the name if specified. if (name != null) { element.Name = name; } // same for the parent id. if (parentId != null) { element.ParentId = parentId; } // read item property values. DaValue[] values = ReadPropertyValues(element.ItemId, m_CoreProperties); // must be an item if the data type property exists. if (values[0].Error >= 0) { element.ElementType = DaElementType.Item; element.DataType = values[0].GetValue <short>(); element.AccessRights = values[1].GetValue <int>(); element.ScanRate = values[2].GetValue <float>(); element.Description = values[3].GetValue <string>(); element.EngineeringUnits = values[4].GetValue <string>(); element.EuInfo = values[5].GetValue <string[]>(); element.EuType = values[6].GetValue <int>(); element.HighEU = values[7].GetValue <double>(); element.LowEU = values[8].GetValue <double>(); element.OpenLabel = values[9].GetValue <string>(); element.CloseLabel = values[10].GetValue <string>(); element.HighIR = values[11].GetValue <double>(); element.LowIR = values[12].GetValue <double>(); // check if the time zone is specified. if (values[13].Error >= 0) { element.TimeZone = values[13].GetValue <int>(); } // check for analog item (checks for HighEU if EuType property not supported). if ((values[7].Error < 0 && values[7].Error >= 0) || element.EuType == (int)OPCEUTYPE.OPC_ANALOG) { element.ElementType = DaElementType.AnalogItem; } // check for enumerated item. else if (element.EuType == (int)OPCEUTYPE.OPC_ENUMERATED) { element.ElementType = DaElementType.EnumeratedItem; } // check for digital item (checks for CloseLabel property). else if (values[10].Error >= 0) { element.ElementType = DaElementType.DigitalItem; } } // the element must be a branch. else { element.ElementType = DaElementType.Branch; // branches could have description property. element.Description = values[3].GetValue <string>(); } }
/// <summary> /// Read the available non-built in properties from the server. /// </summary> /// <param name="itemId">The item id.</param> /// <param name="updateCache">if set to <c>true</c> the cache is updated.</param> /// <returns>The array of properties.</returns> public DaProperty[] ReadAvailableProperties(string itemId, bool updateCache) { string methodName = "IOPCItemProperties.QueryAvailableProperties"; // query for available properties. int count = 0; IntPtr pPropertyIds = IntPtr.Zero; IntPtr pDescriptions = IntPtr.Zero; IntPtr pDataTypes = IntPtr.Zero; try { IOPCItemProperties server = BeginComCall <IOPCItemProperties>(methodName, true); server.QueryAvailableProperties( itemId, out count, out pPropertyIds, out pDescriptions, out pDataTypes); } catch (Exception e) { if (ComUtils.IsUnknownError(e, ResultIds.E_FAIL, ResultIds.E_UNKNOWNITEMID, ResultIds.E_INVALIDITEMID)) { ComUtils.TraceComError(e, methodName); } return(null); } finally { EndComCall(methodName); } // unmarshal results. int[] propertyIds = ComUtils.GetInt32s(ref pPropertyIds, count, true); string[] descriptions = ComUtils.GetUnicodeStrings(ref pDescriptions, count, true); short[] datatype = ComUtils.GetInt16s(ref pDataTypes, count, true); List <DaProperty> properties = new List <DaProperty>(); for (int ii = 0; ii < count; ii++) { // do not return any of the built in properties. if (propertyIds[ii] <= PropertyIds.TimeZone) { continue; } DaProperty property = new DaProperty(); property.PropertyId = propertyIds[ii]; property.Name = descriptions[ii]; property.DataType = datatype[ii]; properties.Add(property); } // fetch the item ids. if (properties.Count > 0) { DaProperty[] array = properties.ToArray(); GetPropertyItemIds(itemId, array); // update the cache. if (updateCache) { lock (m_cache) { DaElement element = null; if (m_cache.TryGetValue(itemId, out element)) { element.Properties = array; } } } return(array); } return(null); }