/// <summary> /// Initializes a new instance of the <see cref="ComDaGroupItem"/> class. /// </summary> public ComDaGroupItem(ComDaGroup group, string itemId) { m_group = group; m_itemId = itemId; m_serverHandle = 0; m_active = true; m_euType = -1; m_samplingRate = -1; m_bufferEnabled = false; m_deadband = -1; }
/// <summary> /// Removes the group. /// </summary> /// <param name="group">The group.</param> public void RemoveGroup(ComDaGroup group) { m_groupManager.RemoveGroup(group); }
/// <summary> /// Recovers the session context. /// </summary> /// <param name="group">The group.</param> public void RecoverSessionContext(ComDaGroup group) { // create a new subscription and copy existing one. Subscription discardSubscription = group.Subscription; Subscription subscription = new Subscription(); subscription.DisplayName = discardSubscription.DisplayName; subscription.PublishingInterval = discardSubscription.PublishingInterval; subscription.KeepAliveCount = discardSubscription.KeepAliveCount; subscription.LifetimeCount = discardSubscription.LifetimeCount; subscription.MaxNotificationsPerPublish = discardSubscription.MaxNotificationsPerPublish; subscription.Priority = discardSubscription.Priority; subscription.PublishingEnabled = discardSubscription.PublishingEnabled; subscription.DisableMonitoredItemCache = discardSubscription.DisableMonitoredItemCache; try { discardSubscription.Dispose(); } catch (Exception) { } m_session.AddSubscription(subscription); try { // create the initial subscription. subscription.Create(); // set the keep alive interval to 30 seconds and the the lifetime interval to 5 minutes. subscription.KeepAliveCount = (uint)((30000 / (int)subscription.CurrentPublishingInterval) + 1); subscription.LifetimeCount = (uint)((600000 / (int)subscription.CurrentPublishingInterval) + 1); // update the subscription. subscription.Modify(); } catch (Exception e) { m_session.RemoveSubscription(subscription); throw ComUtils.CreateComException(e, ResultIds.E_FAIL); } // update the group. group.ActualUpdateRate = (int)(subscription.CurrentPublishingInterval * 2); group.Subscription = subscription; group.RecreateItems(); }
/// <summary> /// Validates the items by reading the attributes required to add them to the group. /// </summary> /// <param name="session">The session.</param> /// <param name="group">The group.</param> /// <param name="requests">The requests.</param> /// <param name="items">The items.</param> /// <param name="start">The start index.</param> /// <param name="count">The number of items to process.</param> private void ValidateItems( Session session, ComDaGroup group, ComDaCreateItemRequest[] requests, ComDaGroupItem[] items, int start, int count) { // build list of the UA attributes that need to be read. ReadValueIdCollection attributesToRead = new ReadValueIdCollection(); for (int ii = start; ii < start + count && ii < requests.Length; ii++) { // create the group item. ComDaCreateItemRequest request = requests[ii]; ComDaGroupItem item = items[ii] = new ComDaGroupItem(group, request.ItemId); item.NodeId = m_mapper.GetRemoteNodeId(request.ItemId); item.Active = request.Active; item.ClientHandle = request.ClientHandle; item.RequestedDataType = request.RequestedDataType; item.SamplingRate = -1; item.Deadband = -1; // add attributes. ReadValueId attributeToRead; attributeToRead = new ReadValueId(); attributeToRead.NodeId = item.NodeId; attributeToRead.AttributeId = Attributes.NodeClass; attributesToRead.Add(attributeToRead); attributeToRead = new ReadValueId(); attributeToRead.NodeId = item.NodeId; attributeToRead.AttributeId = Attributes.DataType; attributesToRead.Add(attributeToRead); attributeToRead = new ReadValueId(); attributeToRead.NodeId = item.NodeId; attributeToRead.AttributeId = Attributes.ValueRank; attributesToRead.Add(attributeToRead); attributeToRead = new ReadValueId(); attributeToRead.NodeId = item.NodeId; attributeToRead.AttributeId = Attributes.UserAccessLevel; attributesToRead.Add(attributeToRead); } // read attribute values from the server. DataValueCollection results = null; DiagnosticInfoCollection diagnosticInfos = null; try { session.Read( null, 0, TimestampsToReturn.Neither, attributesToRead, out results, out diagnosticInfos); ClientBase.ValidateResponse(results, attributesToRead); ClientBase.ValidateDiagnosticInfos(diagnosticInfos, attributesToRead); } catch (Exception e) { Utils.Trace(e, "Unexpected error reading attributes for items."); // set default values on error. for (int ii = start; ii < start + count && ii < requests.Length; ii++) { requests[ii].Error = ResultIds.E_INVALIDITEMID; } return; } // process results. int first = 0; for (int ii = start; ii < start + count && ii < requests.Length; ii++, first += 4) { ComDaGroupItem item = items[ii]; // verify node class. NodeClass nodeClass = (NodeClass)results[first].GetValue<int>((int)NodeClass.Unspecified); if (nodeClass != NodeClass.Variable) { requests[ii].Error = ResultIds.E_INVALIDITEMID; continue; } // verify data type. NodeId dataTypeId = results[first+1].GetValue<NodeId>(null); if (dataTypeId == null) { requests[ii].Error = ResultIds.E_INVALIDITEMID; continue; } // get value rank. int valueRank = results[first+2].GetValue<int>(ValueRanks.Scalar); // update datatypes. BuiltInType builtInType = DataTypes.GetBuiltInType(dataTypeId, session.TypeTree); item.RemoteDataType = new TypeInfo(builtInType, valueRank); item.CanonicalDataType = (short)ComUtils.GetVarType(item.RemoteDataType); // update access rights. byte userAccessLevel = results[first+3].GetValue<byte>(0); if ((userAccessLevel & AccessLevels.CurrentRead) != 0) { item.AccessRights |= OpcRcw.Da.Constants.OPC_READABLE; } if ((userAccessLevel & AccessLevels.CurrentWrite) != 0) { item.AccessRights |= OpcRcw.Da.Constants.OPC_WRITEABLE; } } }
/// <summary> /// Validates the items. /// </summary> /// <param name="group">The group.</param> /// <param name="requests">The requests.</param> /// <returns>The items. May contain null is validation failed.</returns> public ComDaGroupItem[] ValidateItems(ComDaGroup group, ComDaCreateItemRequest[] requests) { TraceState("ValidateItems", group.Name); // get the session to use for the operation. Session session = m_session; if (session == null) { throw ComUtils.CreateComException(ResultIds.E_FAIL); } // validate items. ComDaGroupItem[] items = new ComDaGroupItem[requests.Length]; for (int ii = 0; ii < requests.Length; ii += 10000) { ValidateItems(session, group, requests, items, ii, 10000); } // process results. for (int ii = 0; ii < requests.Length; ii++) { // check for the results. ComDaCreateItemRequest request = requests[ii]; if (request.Error < 0) { items[ii] = null; continue; } // check access path. if (!String.IsNullOrEmpty(request.AccessPath)) { items[ii] = null; request.Error = ResultIds.E_UNKNOWNPATH; continue; } ComDaGroupItem item = items[ii]; // validate the datatype. if (request.RequestedDataType != 0) { NodeId dataTypeId = ComUtils.GetDataTypeId(request.RequestedDataType); if (NodeId.IsNull(dataTypeId)) { items[ii] = null; request.Error = ResultIds.E_BADTYPE; continue; } bool reqTypeIsArray = (request.RequestedDataType & (short)VarEnum.VT_ARRAY) != 0; bool actualTypeIsArray = (item.CanonicalDataType & (short)VarEnum.VT_ARRAY) != 0; if (reqTypeIsArray != actualTypeIsArray) { items[ii] = null; request.Error = ResultIds.E_BADTYPE; continue; } } // create a new monitored item. MonitoredItem monitoredItem = new MonitoredItem(); monitoredItem.StartNodeId = item.NodeId; monitoredItem.RelativePath = null; monitoredItem.AttributeId = Attributes.Value; monitoredItem.MonitoringMode = (request.Active)?MonitoringMode.Reporting:MonitoringMode.Disabled; monitoredItem.SamplingInterval = group.UpdateRate/2; monitoredItem.QueueSize = 0; monitoredItem.DiscardOldest = true; monitoredItem.Filter = null; // update item. item.ServerHandle = (int)monitoredItem.ClientHandle; item.MonitoredItem = monitoredItem; // link the monitored item back to the group item. monitoredItem.Handle = item; // update return parameters. request.ServerHandle = item.ServerHandle; request.CanonicalDataType = item.CanonicalDataType; request.AccessRights = item.AccessRights; request.Error = ResultIds.S_OK; } return items; }
/// <summary> /// Updates the EUInfo for the items. /// </summary> /// <param name="group">The group.</param> /// <param name="items">The items. Null entries are ignored.</param> public void UpdateItemEuInfo( ComDaGroup group, IList<ComDaGroupItem> items) { // get the session to use for the operation. Session session = m_session; if (session == null) { throw ComUtils.CreateComException(ResultIds.E_FAIL); } // build list of properties that need to be read. BrowsePathCollection browsePaths = new BrowsePathCollection(); for (int ii = 0; ii < items.Count; ii++) { ComDaGroupItem item = (ComDaGroupItem)items[ii]; // ignore invalid items or items which have already checked their EU type. if (item == null || item.EuType >= 0) { continue; } BrowsePath browsePath = new BrowsePath(); browsePath.StartingNode = item.NodeId; RelativePathElement element = new RelativePathElement(); element.ReferenceTypeId = ReferenceTypeIds.HasProperty; element.IsInverse = false; element.IncludeSubtypes = false; element.TargetName = Opc.Ua.BrowseNames.EURange; browsePath.RelativePath.Elements.Add(element); browsePath.Handle = item; browsePaths.Add(browsePath); browsePath = new BrowsePath(); browsePath.StartingNode = item.NodeId; element = new RelativePathElement(); element.ReferenceTypeId = ReferenceTypeIds.HasProperty; element.IsInverse = false; element.IncludeSubtypes = false; element.TargetName = Opc.Ua.BrowseNames.EnumStrings; browsePath.RelativePath.Elements.Add(element); browsePath.Handle = item; browsePaths.Add(browsePath); } // check if nothing to do. if (browsePaths.Count == 0) { return; } // translate browse paths. BrowsePathResultCollection results = null; DiagnosticInfoCollection diagnosticInfos = null; try { session.TranslateBrowsePathsToNodeIds( null, browsePaths, out results, out diagnosticInfos); ClientBase.ValidateResponse(results, browsePaths); ClientBase.ValidateDiagnosticInfos(diagnosticInfos, browsePaths); } catch (Exception) { for (int ii = 0; ii < browsePaths.Count; ii++) { ComDaGroupItem item = (ComDaGroupItem)browsePaths[ii].Handle; item.EuType = 0; } return; } // build list of properties that need to be read. ReadValueIdCollection propertiesToRead = new ReadValueIdCollection(); for (int ii = 0; ii < results.Count; ii++) { ComDaGroupItem item = (ComDaGroupItem)browsePaths[ii].Handle; BrowsePathResult result = results[ii]; if (StatusCode.IsBad(result.StatusCode)) { if (item.EuType < 0 && result.StatusCode == StatusCodes.BadNoMatch) { item.EuType = (int)OpcRcw.Da.OPCEUTYPE.OPC_NOENUM; } continue; } if (result.Targets.Count == 0 || result.Targets[0].TargetId.IsAbsolute) { if (item.EuType < 0) { item.EuType = (int)OpcRcw.Da.OPCEUTYPE.OPC_NOENUM; } continue; } ReadValueId propertyToRead = new ReadValueId(); propertyToRead.NodeId = (NodeId)result.Targets[0].TargetId; propertyToRead.AttributeId = Attributes.Value; propertyToRead.Handle = item; propertiesToRead.Add(propertyToRead); if (browsePaths[ii].RelativePath.Elements[0].TargetName.Name == Opc.Ua.BrowseNames.EURange) { item.EuType = (int)OpcRcw.Da.OPCEUTYPE.OPC_ANALOG; } else { item.EuType = (int)OpcRcw.Da.OPCEUTYPE.OPC_ENUMERATED; } } // check if nothing to do. if (propertiesToRead.Count == 0) { return; } // read attribute values from the server. DataValueCollection values = null; try { session.Read( null, 0, TimestampsToReturn.Neither, propertiesToRead, out values, out diagnosticInfos); ClientBase.ValidateResponse(values, propertiesToRead); ClientBase.ValidateDiagnosticInfos(diagnosticInfos, propertiesToRead); } catch (Exception) { for (int ii = 0; ii < propertiesToRead.Count; ii++) { ComDaGroupItem item = (ComDaGroupItem)propertiesToRead[ii].Handle; item.EuType = 0; } return; } // process results. for (int ii = 0; ii < values.Count; ii++) { ComDaGroupItem item = (ComDaGroupItem)propertiesToRead[ii].Handle; if (StatusCode.IsBad(values[ii].StatusCode)) { item.EuType = 0; continue; } if (item.EuType == (int)OpcRcw.Da.OPCEUTYPE.OPC_ANALOG) { Range range = (Range)values[ii].GetValue<Range>(null); if (range == null) { item.EuType = 0; continue; } item.EuInfo = new double[] { range.Low, range.High }; continue; } if (item.EuType == (int)OpcRcw.Da.OPCEUTYPE.OPC_ENUMERATED) { LocalizedText[] texts = (LocalizedText[])values[ii].GetValue<LocalizedText[]>(null); if (texts == null) { item.EuType = 0; continue; } string[] strings = new string[texts.Length]; for (int jj = 0; jj < strings.Length; jj++) { if (!LocalizedText.IsNullOrEmpty(texts[jj])) { strings[jj] = texts[jj].Text; } } item.EuInfo = strings; continue; } } }
/// <summary> /// Returns the current set of groups. /// </summary> /// <returns>The list of groups.</returns> public ComDaGroup[] GetGroups() { TraceState("GetGroups"); lock (m_lock) { ComDaGroup[] groups = new ComDaGroup[m_groups.Count]; for (int ii = 0; ii < m_groups.Count; ii++) { groups[ii] = m_groups[ii]; } return groups; } }
/// <summary> /// Removes the group. /// </summary> /// <param name="group">The group.</param> public void RemoveGroup(ComDaGroup group) { TraceState("RemoveGroup", group.Name); lock (m_lock) { for (int ii = 0; ii < m_groups.Count; ii++) { if (Object.ReferenceEquals(group, m_groups[ii])) { m_groups.RemoveAt(ii); if (group.Subscription != null && group.Subscription.Session != null) { group.Subscription.Session.RemoveSubscription(group.Subscription); } } } group.Dispose(); } }
/// <summary> /// Adds the group. /// </summary> /// <param name="groupName">The group name.</param> /// <param name="active">if set to <c>true</c> the group is active.</param> /// <param name="updateRate">The update rate.</param> /// <param name="clientHandle">The client handle.</param> /// <param name="timeBias">The time bias.</param> /// <param name="deadband">The deadband.</param> /// <param name="lcid">The lcid.</param> /// <returns>The new group.</returns> public ComDaGroup AddGroup( string groupName, bool active, int updateRate, int clientHandle, int timeBias, float deadband, int lcid) { TraceState("AddGroup", groupName, active, updateRate, clientHandle, timeBias, deadband, lcid); // check for valid session. Session session = m_session; if (session == null) { throw ComUtils.CreateComException(ResultIds.E_FAIL); } ComDaGroup group = null; // check for duplicate name. lock (m_lock) { // ensure the name is unique. if (!String.IsNullOrEmpty(groupName)) { if (GetGroupByName(groupName) != null) { throw ComUtils.CreateComException(ResultIds.E_DUPLICATENAME); } } // assign a unique name. else { groupName = Utils.Format("Group{0}", m_groupCounter+1); } // validate the deadband. if (deadband < 0 || deadband > 100) { throw ComUtils.CreateComException(ResultIds.E_INVALIDARG); } // create the group. group = new ComDaGroup(this, groupName, ++m_groupCounter); m_groups.Add(group); group.ClientHandle = clientHandle; group.Active = active; group.UpdateRate = updateRate; group.TimeBias = timeBias; group.Deadband = deadband; group.Lcid = lcid; if (updateRate < 100) { updateRate = 100; } // create a new subscription. Subscription subscription = new Subscription(); subscription.DisplayName = groupName; subscription.PublishingInterval = updateRate/2; subscription.KeepAliveCount = 30; subscription.LifetimeCount = 600; subscription.MaxNotificationsPerPublish = 10000; subscription.Priority = 1; subscription.PublishingEnabled = active; subscription.DisableMonitoredItemCache = true; // create the subscription on the server. session.AddSubscription(subscription); try { // create the initial subscription. subscription.Create(); // set the keep alive interval to 30 seconds and the the lifetime interval to 5 minutes. subscription.KeepAliveCount = (uint)((30000/(int)subscription.CurrentPublishingInterval)+1); subscription.LifetimeCount = (uint)((600000/(int)subscription.CurrentPublishingInterval)+1); // update the subscription. subscription.Modify(); } catch (Exception e) { session.RemoveSubscription(subscription); Utils.Trace((int)Utils.TraceMasks.Error, "Create subscription failed: {0}", e.Message); throw ComUtils.CreateComException(e, ResultIds.E_FAIL); } // update the group. group.ActualUpdateRate = (int)(subscription.CurrentPublishingInterval*2); group.Subscription = subscription; } return group; }
/// <summary> /// Sets the name. /// </summary> /// <param name="group">The group.</param> /// <param name="groupName">Name of the group.</param> public void SetGroupName(ComDaGroup group, string groupName) { TraceState("SetGroupName", group.Name, groupName); if (String.IsNullOrEmpty(groupName)) { throw ComUtils.CreateComException(ResultIds.E_INVALIDARG); } lock (m_lock) { for (int ii = 0; ii < m_groups.Count; ii++) { ComDaGroup target = m_groups[ii]; if (target.ServerHandle != group.ServerHandle && target.Name == groupName) { throw ComUtils.CreateComException(ResultIds.E_DUPLICATENAME); } group.Name = groupName; } } }
/// <summary> /// Clones the item. /// </summary> /// <param name="group">The group.</param> /// <returns>The new item.</returns> public ComDaGroupItem CloneItem(ComDaGroup group) { ComDaGroupItem copy = new ComDaGroupItem(group, this.ItemId); copy.m_nodeId = this.m_nodeId; copy.m_serverHandle = this.m_serverHandle; copy.m_clientHandle = this.m_clientHandle; copy.m_active = this.m_active; copy.m_requestedDataType = this.m_requestedDataType; copy.m_canonicalDataType = this.m_canonicalDataType; copy.m_accessRights = this.m_accessRights; copy.m_euType = this.m_euType; copy.m_euInfo = this.m_euInfo; copy.m_samplingRate = this.m_samplingRate; copy.m_bufferEnabled = this.m_bufferEnabled; copy.m_actualSamplingRate = this.m_actualSamplingRate; copy.m_deadband = this.m_deadband; // create new monitored item and use its client handle as the server handle. copy.m_monitoredItem = new MonitoredItem(this.MonitoredItem); return copy; }