/// <summary> /// Validates the monitoring filter specified by the client. /// </summary> protected virtual StatusCode ValidateMonitoringFilter( ServerSystemContext context, NodeHandle handle, uint attributeId, double samplingInterval, uint queueSize, ExtensionObject filter, out MonitoringFilter filterToUse, out Range range, out MonitoringFilterResult result) { range = null; filterToUse = null; result = null; // nothing to do if the filter is not specified. if (ExtensionObject.IsNull(filter)) { return StatusCodes.Good; } // extension objects wrap any data structure. must check that the client provided the correct structure. DataChangeFilter deadbandFilter = ExtensionObject.ToEncodeable(filter) as DataChangeFilter; if (deadbandFilter == null) { AggregateFilter aggregateFilter = ExtensionObject.ToEncodeable(filter) as AggregateFilter; if (aggregateFilter == null || attributeId != Attributes.Value) { return StatusCodes.BadFilterNotAllowed; } if (!Server.AggregateManager.IsSupported(aggregateFilter.AggregateType)) { return StatusCodes.BadAggregateNotSupported; } ServerAggregateFilter revisedFilter = new ServerAggregateFilter(); revisedFilter.AggregateType = aggregateFilter.AggregateType; revisedFilter.StartTime = aggregateFilter.StartTime; revisedFilter.ProcessingInterval = aggregateFilter.ProcessingInterval; revisedFilter.AggregateConfiguration = aggregateFilter.AggregateConfiguration; revisedFilter.Stepped = false; StatusCode error = ReviseAggregateFilter(context, handle, samplingInterval, queueSize, revisedFilter); if (StatusCode.IsBad(error)) { return error; } AggregateFilterResult aggregateFilterResult = new AggregateFilterResult(); aggregateFilterResult.RevisedProcessingInterval = aggregateFilter.ProcessingInterval; aggregateFilterResult.RevisedStartTime = aggregateFilter.StartTime; aggregateFilterResult.RevisedAggregateConfiguration = aggregateFilter.AggregateConfiguration; filterToUse = revisedFilter; result = aggregateFilterResult; return StatusCodes.Good; } // deadband filters only allowed for variable values. if (attributeId != Attributes.Value) { return StatusCodes.BadFilterNotAllowed; } BaseVariableState variable = handle.Node as BaseVariableState; if (variable == null) { return StatusCodes.BadFilterNotAllowed; } // check for status filter. if (deadbandFilter.DeadbandType == (uint)DeadbandType.None) { filterToUse = deadbandFilter; return StatusCodes.Good; } // deadband filters can only be used for numeric values. if (!Server.TypeTree.IsTypeOf(variable.DataType, DataTypeIds.Number)) { return StatusCodes.BadFilterNotAllowed; } // nothing more to do for absolute filters. if (deadbandFilter.DeadbandType == (uint)DeadbandType.Absolute) { filterToUse = deadbandFilter; return StatusCodes.Good; } // need to look up the EU range if a percent filter is requested. if (deadbandFilter.DeadbandType == (uint)DeadbandType.Percent) { PropertyState property = handle.Node.FindChild(context, Opc.Ua.BrowseNames.EURange) as PropertyState; if (property == null) { return StatusCodes.BadFilterNotAllowed; } range = property.Value as Range; if (range == null) { return StatusCodes.BadFilterNotAllowed; } filterToUse = deadbandFilter; return StatusCodes.Good; } // no other type of filter supported. return StatusCodes.BadFilterNotAllowed; }
/// <summary> /// Revises an aggregate filter (may require knowledge of the variable being used). /// </summary> /// <param name="context">The context.</param> /// <param name="handle">The handle.</param> /// <param name="samplingInterval">The sampling interval for the monitored item.</param> /// <param name="queueSize">The queue size for the monitored item.</param> /// <param name="filterToUse">The filter to revise.</param> /// <returns>Good if the </returns> protected virtual StatusCode ReviseAggregateFilter( ServerSystemContext context, NodeHandle handle, double samplingInterval, uint queueSize, ServerAggregateFilter filterToUse) { if (filterToUse.ProcessingInterval < samplingInterval) { filterToUse.ProcessingInterval = samplingInterval; } if (filterToUse.ProcessingInterval < Server.AggregateManager.MinimumProcessingInterval) { filterToUse.ProcessingInterval = Server.AggregateManager.MinimumProcessingInterval; } DateTime earliestStartTime = DateTime.UtcNow.AddMilliseconds(-(queueSize - 1)*filterToUse.ProcessingInterval); if (earliestStartTime > filterToUse.StartTime) { filterToUse.StartTime = earliestStartTime; } if (filterToUse.AggregateConfiguration.UseServerCapabilitiesDefaults) { filterToUse.AggregateConfiguration = Server.AggregateManager.GetDefaultConfiguration(null); } return StatusCodes.Good; }
/// <summary> /// Revises an aggregate filter (may require knowledge of the variable being used). /// </summary> /// <param name="context">The context.</param> /// <param name="handle">The handle.</param> /// <param name="samplingInterval">The sampling interval for the monitored item.</param> /// <param name="queueSize">The queue size for the monitored item.</param> /// <param name="filterToUse">The filter to revise.</param> /// <returns>Good if the filter is acceptable.</returns> protected override StatusCode ReviseAggregateFilter( ServerSystemContext context, NodeHandle handle, double samplingInterval, uint queueSize, ServerAggregateFilter filterToUse) { // use the sampling interval to limit the processing interval. if (filterToUse.ProcessingInterval < samplingInterval) { filterToUse.ProcessingInterval = samplingInterval; } // check if an archive item. ArchiveItemState item = handle.Node as ArchiveItemState; if (item == null) { // no historial data so must start in the future. while (filterToUse.StartTime < DateTime.UtcNow) { filterToUse.StartTime = filterToUse.StartTime.AddMilliseconds(filterToUse.ProcessingInterval); } // use suitable defaults for values which are are not archived items. filterToUse.AggregateConfiguration.UseServerCapabilitiesDefaults = false; filterToUse.AggregateConfiguration.UseSlopedExtrapolation = false; filterToUse.AggregateConfiguration.TreatUncertainAsBad = false; filterToUse.AggregateConfiguration.PercentDataBad = 100; filterToUse.AggregateConfiguration.PercentDataGood = 100; filterToUse.Stepped = true; } else { // use the archive acquisition sampling interval to limit the processing interval. if (filterToUse.ProcessingInterval < item.ArchiveItem.SamplingInterval) { filterToUse.ProcessingInterval = item.ArchiveItem.SamplingInterval; } // ensure the buffer does not get overfilled. while (filterToUse.StartTime.AddMilliseconds(queueSize*filterToUse.ProcessingInterval) < DateTime.UtcNow) { filterToUse.StartTime = filterToUse.StartTime.AddMilliseconds(filterToUse.ProcessingInterval); } filterToUse.Stepped = item.ArchiveItem.Stepped; // revise the configration. ReviseAggregateConfiguration(context, item, filterToUse.AggregateConfiguration); } return StatusCodes.Good; }