/// <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); }
/// <summary> /// Sets the data types. /// </summary> /// <param name="serverHandles">The server handles.</param> /// <param name="dataTypes">The data types.</param> /// <returns>Any errors.</returns> public int[] SetDataTypes(int[] serverHandles, short[] dataTypes) { TraceState("SetDataTypes", serverHandles.Length); ThrowIfDisposed(); lock (m_lock) { int[] results = new int[serverHandles.Length]; ComDaGroupItem[] items = new ComDaGroupItem[serverHandles.Length]; for (int ii = 0; ii < serverHandles.Length; ii++) { ComDaGroupItem item = null; if (!m_itemsByHandle.TryGetValue(serverHandles[ii], out item)) { results[ii] = ResultIds.E_INVALIDHANDLE; continue; } // validate the datatype. if (dataTypes[ii] != 0) { NodeId dataTypeId = ComUtils.GetDataTypeId(dataTypes[ii]); if (NodeId.IsNull(dataTypeId)) { results[ii] = ResultIds.E_BADTYPE; continue; } bool reqTypeIsArray = (dataTypes[ii] & (short)VarEnum.VT_ARRAY) != 0; bool actualTypeIsArray = (item.CanonicalDataType & (short)VarEnum.VT_ARRAY) != 0; if (reqTypeIsArray != actualTypeIsArray) { results[ii] = ResultIds.E_BADTYPE; continue; } } item.RequestedDataType = dataTypes[ii]; results[ii] = ResultIds.S_OK; } return results; } }
/// <summary> /// Sets the client handles. /// </summary> /// <param name="serverHandles">The server handles.</param> /// <param name="clientHandles">The client handles.</param> /// <returns>Any errors.</returns> public int[] SetClientHandles(int[] serverHandles, int[] clientHandles) { TraceState("SetClientHandles", serverHandles.Length); ThrowIfDisposed(); lock (m_lock) { int[] results = new int[serverHandles.Length]; ComDaGroupItem[] items = new ComDaGroupItem[serverHandles.Length]; for (int ii = 0; ii < serverHandles.Length; ii++) { ComDaGroupItem item = null; if (!m_itemsByHandle.TryGetValue(serverHandles[ii], out item)) { results[ii] = ResultIds.E_INVALIDHANDLE; continue; } item.ClientHandle = clientHandles[ii]; results[ii] = ResultIds.S_OK; } return results; } }
/// <summary> /// Sets the active state for the items. /// </summary> /// <param name="serverHandles">The server handles.</param> /// <param name="active">if set to <c>true</c> the items are activated.</param> /// <returns>Any error codes.</returns> public int[] SetActive(int[] serverHandles, bool active) { TraceState("SetActive", serverHandles.Length, active); ThrowIfDisposed(); lock (m_lock) { // get net monitoring mode. MonitoringMode monitoringMode = MonitoringMode.Disabled; if (m_active && active) { monitoringMode = MonitoringMode.Reporting; } // validate the handles. int[] results = new int[serverHandles.Length]; ComDaGroupItem[] items = new ComDaGroupItem[serverHandles.Length]; List<MonitoredItem> monitoredItems = new List<MonitoredItem>(); for (int ii = 0; ii < serverHandles.Length; ii++) { ComDaGroupItem item = null; if (!m_itemsByHandle.TryGetValue(serverHandles[ii], out item)) { results[ii] = ResultIds.E_INVALIDHANDLE; continue; } // nothing to do if already in the correct state. if (item.MonitoredItem.Status.MonitoringMode == monitoringMode) { item.Active = active; results[ii] = ResultIds.S_OK; continue; } items[ii] = item; monitoredItems.Add(item.MonitoredItem); } // update the subscription. try { m_subscription.SetMonitoringMode(monitoringMode, monitoredItems); } catch (Exception e) { SetFatalError(items, results, e, ResultIds.E_FAIL); } // update active state on success for (int ii = 0; ii < items.Length; ii++) { if (items[ii] != null) { // clear the last sent value when deactivated. // this prevents the change filter from suppressing // updates if the item is reactivated and the value // has not changed. if (items[ii].Active && !active) { items[ii].LastSentValue = null; } items[ii].Active = active; } } // return any errors. return results; } }
/// <summary> /// Deletes the items. /// </summary> /// <param name="serverHandles">The server handles.</param> /// <returns>Any errors.</returns> public int[] DeleteItems(int[] serverHandles) { TraceState("DeleteItems", serverHandles.Length); ThrowIfDisposed(); lock (m_lock) { // validate the handles. int[] results = new int[serverHandles.Length]; ComDaGroupItem[] items = new ComDaGroupItem[serverHandles.Length]; for (int ii = 0; ii < serverHandles.Length; ii++) { ComDaGroupItem item = null; if (!m_itemsByHandle.TryGetValue(serverHandles[ii], out item)) { results[ii] = ResultIds.E_INVALIDHANDLE; continue; } items[ii] = item; RemoveItemFromSubscription(item); m_itemsByHandle.Remove(serverHandles[ii]); m_items.Remove(item); } // update the subscription. try { m_subscription.ApplyChanges(); } catch (Exception e) { SetFatalError(items, results, e, ResultIds.E_FAIL); } // start the update timer. CheckUpdateTimerStatus(); // return any errors. return results; } }
/// <summary> /// Updates the read result by converting the value to the requested data type. /// </summary> /// <param name="item">The item.</param> /// <param name="value">The value.</param> private void UpdateReadResult(ComDaGroupItem item, DaValue value) { if (value.Value == null || item.RequestedDataType == (short)VarEnum.VT_EMPTY) { return; } object convertedValue = null; int error = ComUtils.ChangeTypeForCOM(value.Value, (VarEnum)item.RequestedDataType, out convertedValue); if (error < 0) { value.Value = null; value.Error = error; return; } value.Value = convertedValue; }
/// <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> /// 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> /// Clears the sampling rates for the items. /// </summary> /// <param name="serverHandles">The server handles.</param> /// <returns>Any errors.</returns> public int[] ClearItemSamplingRates(int[] serverHandles) { TraceState("ClearItemSamplingRates", serverHandles.Length); ThrowIfDisposed(); int[] errors = new int[serverHandles.Length]; ComDaGroupItem[] items = new ComDaGroupItem[serverHandles.Length]; lock (m_lock) { // update the filter for analog items that have not overriden the deadband. for (int ii = 0; ii < serverHandles.Length; ii++) { ComDaGroupItem item = null; if (!m_itemsByHandle.TryGetValue(serverHandles[ii], out item)) { errors[ii] = ResultIds.E_INVALIDHANDLE; continue; } if (item.SamplingRate == -1) { errors[ii] = ResultIds.E_RATENOTSET; continue; } item.MonitoredItem.SamplingInterval = (int)(this.UpdateRate/2); items[ii] = item; } // update the items on the server. try { m_subscription.ApplyChanges(); } catch (Exception e) { int error = ComUtils.GetErrorCode(e, ResultIds.E_FAIL); for (int ii = 0; ii < items.Length; ii++) { if (items[ii] != null) { errors[ii] = error; } } return errors; } // check results for monitored items. for (int ii = 0; ii < items.Length; ii++) { ComDaGroupItem item = items[ii]; if (item == null) { continue; } if (ServiceResult.IsBad(item.MonitoredItem.Status.Error)) { errors[ii] = ResultIds.E_FAIL; continue; } item.SamplingRate = -1; item.ActualSamplingRate = 0; item.NextUpdateTime = -1; } return errors; } }
/// <summary> /// Sets the sampling rate for the items. /// </summary> /// <param name="serverHandles">The server handles.</param> /// <param name="samplingRates">The sampling rates.</param> /// <param name="revisedSamplingRates">The revised sampling rates.</param> /// <returns>Any errors.</returns> public int[] SetItemSamplingRates(int[] serverHandles, int[] samplingRates, int[] revisedSamplingRates) { TraceState("SetItemSamplingRates", serverHandles.Length); ThrowIfDisposed(); int[] errors = new int[serverHandles.Length]; ComDaGroupItem[] items = new ComDaGroupItem[serverHandles.Length]; lock (m_lock) { // update the filter for analog items that have not overriden the deadband. for (int ii = 0; ii < serverHandles.Length; ii++) { ComDaGroupItem item = null; if (!m_itemsByHandle.TryGetValue(serverHandles[ii], out item)) { errors[ii] = ResultIds.E_INVALIDHANDLE; continue; } item.MonitoredItem.SamplingInterval = (int)(samplingRates[ii]/2); revisedSamplingRates[ii] = 0; items[ii] = item; } // update the items on the server. try { m_subscription.ApplyChanges(); } catch (Exception e) { int error = ComUtils.GetErrorCode(e, ResultIds.E_FAIL); for (int ii = 0; ii < items.Length; ii++) { if (items[ii] != null) { errors[ii] = error; } } return errors; } // check results for monitored items. for (int ii = 0; ii < items.Length; ii++) { ComDaGroupItem item = items[ii]; if (item == null) { continue; } if (ServiceResult.IsBad(item.MonitoredItem.Status.Error)) { errors[ii] = ResultIds.E_FAIL; continue; } item.SamplingRate = samplingRates[ii]; item.ActualSamplingRate = (int)(items[ii].MonitoredItem.Status.SamplingInterval); // if 1/2 the sampling rate is accepted then it is possible to guarantee the sampling rate. if (item.ActualSamplingRate < item.SamplingRate) { item.ActualSamplingRate = item.SamplingRate; } // can only guarantee a sampling rate of twice what the UA server can provide. else { item.ActualSamplingRate *= 2; } revisedSamplingRates[ii] = item.ActualSamplingRate; item.NextUpdateTime = HiResClock.UtcNow.Ticks + item.ActualSamplingRate*TimeSpan.TicksPerMillisecond; if (revisedSamplingRates[ii] != samplingRates[ii]) { errors[ii] = ResultIds.S_UNSUPPORTEDRATE; } TraceState( "ItemSamplingUpdated", this.ServerHandle, item.ServerHandle, item.ClientHandle, m_subscription.CurrentPublishingEnabled, m_subscription.CurrentPublishingInterval, item.SamplingRate, item.MonitoredItem.Status.SamplingInterval); } return errors; } }
/// <summary> /// Clears the deadbands for the items. /// </summary> /// <param name="serverHandles">The server handles.</param> /// <returns>Any errors.</returns> public int[] ClearItemDeadbands(int[] serverHandles) { TraceState("ClearItemSamplingRates", serverHandles.Length); ThrowIfDisposed(); int[] errors = new int[serverHandles.Length]; ComDaGroupItem[] items = new ComDaGroupItem[serverHandles.Length]; int count = 0; lock (m_lock) { // update the filter for analog items that have not overriden the deadband. for (int ii = 0; ii < serverHandles.Length; ii++) { ComDaGroupItem item = null; if (!m_itemsByHandle.TryGetValue(serverHandles[ii], out item)) { errors[ii] = ResultIds.E_INVALIDHANDLE; continue; } if (item.EuType >= 0 && item.EuType != (int)OpcRcw.Da.OPCEUTYPE.OPC_ANALOG) { errors[ii] = ResultIds.E_DEADBANDNOTSUPPORTED; continue; } if (item.Deadband == -1) { errors[ii] = ResultIds.E_DEADBANDNOTSET; continue; } items[ii] = item; item.Deadband = -1; count++; } } if (count == 0) { return errors; } // need the EU Info if the deadband is specified. m_manager.UpdateItemEuInfo(this, items); lock (m_lock) { // need to check the EU type after updating the EU info. for (int ii = 0; ii < items.Length; ii++) { ComDaGroupItem item = items[ii]; if (item != null && item.EuType != (int)OpcRcw.Da.OPCEUTYPE.OPC_ANALOG) { errors[ii] = ResultIds.E_DEADBANDNOTSUPPORTED; items[ii] = null; continue; } } UpdateDeadbandFilters(items); // update the items on the server. try { m_subscription.ApplyChanges(); } catch (Exception e) { int error = ComUtils.GetErrorCode(e, ResultIds.E_FAIL); for (int ii = 0; ii < items.Length; ii++) { if (items[ii] != null) { errors[ii] = error; } } return errors; } return errors; } }
/// <summary> /// Gets the deadband for the items. /// </summary> /// <param name="serverHandles">The server handles.</param> /// <param name="deadbands">The deadbands.</param> /// <returns>Any errors.</returns> public int[] GetItemDeadbands(int[] serverHandles, float[] deadbands) { TraceState("GetItemDeadbands", serverHandles.Length); ThrowIfDisposed(); int[] errors = new int[serverHandles.Length]; ComDaGroupItem[] items = new ComDaGroupItem[serverHandles.Length]; int count = 0; lock (m_lock) { for (int ii = 0; ii < serverHandles.Length; ii++) { ComDaGroupItem item = null; if (!m_itemsByHandle.TryGetValue(serverHandles[ii], out item)) { errors[ii] = ResultIds.E_INVALIDHANDLE; continue; } if (item.EuType >= 0 && item.EuType != (int)OpcRcw.Da.OPCEUTYPE.OPC_ANALOG) { errors[ii] = ResultIds.E_DEADBANDNOTSUPPORTED; continue; } if (item.Deadband == -1) { errors[ii] = ResultIds.E_DEADBANDNOTSET; continue; } deadbands[ii] = item.Deadband; item.Deadband = deadbands[ii]; items[ii] = item; count++; } } if (count == 0) { return errors; } // need the EU Info if the deadband is specified. m_manager.UpdateItemEuInfo(this, items); lock (m_lock) { for (int ii = 0; ii < serverHandles.Length; ii++) { ComDaGroupItem item = items[ii]; if (item != null) { if (item.EuType != (int)OpcRcw.Da.OPCEUTYPE.OPC_ANALOG) { errors[ii] = ResultIds.E_DEADBANDNOTSUPPORTED; continue; } deadbands[ii] = item.Deadband; } } } return errors; }
/// <summary> /// Sets the deadband for the items. /// </summary> /// <param name="serverHandles">The server handles.</param> /// <param name="deadbands">The deadbands.</param> /// <returns>Any errors.</returns> public int[] SetItemDeadbands(int[] serverHandles, float[] deadbands) { TraceState("SetItemDeadbands", serverHandles.Length); ThrowIfDisposed(); int[] errors = new int[serverHandles.Length]; ComDaGroupItem[] items = new ComDaGroupItem[serverHandles.Length]; int count = 0; lock (m_lock) { // update the filter for analog items that have not overriden the deadband. for (int ii = 0; ii < serverHandles.Length; ii++) { ComDaGroupItem item = null; if (!m_itemsByHandle.TryGetValue(serverHandles[ii], out item)) { errors[ii] = ResultIds.E_INVALIDHANDLE; continue; } if (item.EuType >= 0 && item.EuType != (int)OpcRcw.Da.OPCEUTYPE.OPC_ANALOG) { errors[ii] = ResultIds.E_DEADBANDNOTSUPPORTED; continue; } if (deadbands[ii] < 0 || deadbands[ii] > 100) { errors[ii] = ResultIds.E_INVALIDARG; continue; } item.Deadband = deadbands[ii]; items[ii] = item; count++; } } if (count == 0) { return errors; } // need the EU Info if the deadband is specified. m_manager.UpdateItemEuInfo(this, items); lock (m_lock) { // need to check the EU type after updating the EU info. for (int ii = 0; ii < items.Length; ii++) { ComDaGroupItem item = items[ii]; if (item != null && item.EuType != (int)OpcRcw.Da.OPCEUTYPE.OPC_ANALOG) { errors[ii] = ResultIds.E_DEADBANDNOTSUPPORTED; items[ii] = null; continue; } } // update filters for selected items. UpdateDeadbandFilters(items); // update the items on the server. try { m_subscription.ApplyChanges(); } catch (Exception e) { int error = ComUtils.GetErrorCode(e, ResultIds.E_FAIL); for (int ii = 0; ii < items.Length; ii++) { if (items[ii] != null) { errors[ii] = error; } } return errors; } // check results for monitored items. for (int ii = 0; ii < items.Length; ii++) { ComDaGroupItem item = items[ii]; if (item == null) { continue; } if (ServiceResult.IsBad(item.MonitoredItem.Status.Error)) { item.MonitoredItem.Filter = item.MonitoredItem.Status.Filter; DataChangeFilter filter = item.MonitoredItem.Filter as DataChangeFilter; if (filter != null) { item.Deadband = (float)filter.DeadbandValue; } errors[ii] = ResultIds.E_FAIL; continue; } } return errors; } }
/// <summary> /// Updates the cache. /// </summary> /// <param name="item">The item.</param> /// <param name="value">The value.</param> /// <param name="isInitialRefresh">if set to <c>true</c> the change flag is set to false because the values are sent in the refresh.</param> private void UpdateCache(ComDaGroupItem item, DaValue value, bool isInitialRefresh) { lock (m_lock) { // get sampling rate. long now = HiResClock.UtcNow.Ticks; int samplingRate = item.ActualSamplingRate; if (item.SamplingRate == -1) { samplingRate = m_actualUpdateRate; } // check existing cache contents. DaValue oldValue = null; DaCacheValue entry = null; if (item.CacheEntry != null) { // do not update cache if a newer value exists. if (item.CacheEntry.Timestamp >= value.Timestamp) { /* TraceState( "UpdateCache OLD VALUE RECEIVED", this.m_serverHandle, item.ServerHandle, new Variant(item.CacheEntry.Value), item.CacheEntry.Timestamp.ToString("HH:mm:ss.fff"), new Variant(value.Value), value.Timestamp.ToString("HH:mm:ss.fff")); */ return; } oldValue = item.CacheEntry; // replace the newest value if sampling interval has not elasped. if (!item.BufferEnabled || item.NextUpdateTime > now) { // TraceState("UpdateCache ENTRY REPLACED", this.m_serverHandle, item.ServerHandle, samplingRate); entry = item.CacheEntry; } } // create a new cache entry. if (entry == null) { entry = new DaCacheValue(); entry.CacheTimestamp = DateTime.UtcNow; if (item.BufferEnabled) { entry.NextEntry = item.CacheEntry; item.NextUpdateTime += samplingRate*TimeSpan.TicksPerMillisecond; if (entry.NextEntry != null) { // TraceState("UpdateCache ENTRY BUFFERED", this.m_serverHandle, item.ServerHandle, samplingRate); } } item.CacheEntry = entry; } // check if the value has changed. bool changed = !isInitialRefresh; if (oldValue != null) { if (oldValue.Error == value.Error) { if (oldValue.Quality == value.Quality) { if (Utils.IsEqual(oldValue.Value, value.Value)) { changed = false; } } } } // save values. item.CacheEntry.Value = value.Value; item.CacheEntry.Quality = value.Quality; item.CacheEntry.Timestamp = value.Timestamp; item.CacheEntry.Error = value.Error; item.CacheEntry.Changed = changed; TraceState( "UpdateCache COMPLETE", this.m_serverHandle, item.ServerHandle, item.ClientHandle, new Variant(value.Value), value.Timestamp.ToString("HH:mm:ss.fff"), item.CacheEntry.Changed); } }
/// <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> /// 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> /// Sets the error for all items after an unexpected error occurs. /// </summary> /// <param name="itemsInRequest">The items in request.</param> /// <param name="results">The results.</param> /// <param name="e">The exception.</param> /// <param name="defaultError">The default error.</param> private void SetFatalError(ComDaGroupItem[] itemsInRequest, int[] results, Exception e, int defaultError) { int error = ComUtils.GetErrorCode(e, defaultError); for (int ii = 0; ii < itemsInRequest.Length; ii++) { if (itemsInRequest[ii] != null) { results[ii] = error; } } }
/// <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); }
private void AddItemToSubscription(ComDaGroupItem item) { m_subscription.AddItem(item.MonitoredItem); m_itemsByMonitoredItem.Add(item.MonitoredItem.ClientHandle, item); }
private void RemoveItemFromSubscription(ComDaGroupItem item) { m_itemsByMonitoredItem.Remove(item.MonitoredItem.ClientHandle); m_subscription.RemoveItem(item.MonitoredItem); }
/// <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; }