Skip to content

Commit

Permalink
Implement TimeAverage data function (#375)
Browse files Browse the repository at this point in the history
* Add default function definition for TimeAverage

* Create separate pre-/post-boundary classes

Removes `BoundaryInfo` and replaces it with separate `PreBoundaryInfo` and `PostBoundaryInfo` classes.

This is technically a **breaking change**, as `BoundaryInfo` and the associated properties on `TagValueBucket` are public.

The reason for this change is to allow time buckets to accurately describe the values immediately before and immediately after their start and end times. For a TimeAverage aggregation, this allows a value to be interpolated exactly at the bucket end time so that the area under the line between the last sample and the boundary sample can be calculated.

This also improves interpolation calculations, as it allows the final bucket in an interpolation query to interpolate a bucket at the query end time if end boundary information is available, instead of extrapolating forwards.

* Add common tag value property for a partial value

This property allows an aggregated sample to indicate that it was computed from a partial data set (e.g. if the end time for the data query is earlier than the end time for the time bucket).

* Add TimeAverage implementation + other changes

Adds the implementation for TimeAverage to AggregationHelper.

Refactors the Interpolated implementation to use the new TimeValueBucket boundary properties.

Refactors the private GetAggregatedValues implementation to simplify the way that boundary samples are added to new buckets.

* Add interpolated query start/end values

Updates PlotHelper so that values are interpolated exactly at the query start and end time.

Also modifies default behaviour for non-numeric tags so that a maximum of 6 samples per bucket are returned. In the previous implementation, every change in value or quality for a non-numeric tag was returned, meaning that it was possible to return every sample in the bucket no matter how many there were.

* Update aggregation tests

* Fix sample selection when bucket end > query end

* Clean-up after merging develop/4.0.0 changes

* Update release notes
  • Loading branch information
wazzamatazz committed Jan 18, 2024
1 parent fa47bb8 commit 8b593d7
Show file tree
Hide file tree
Showing 11 changed files with 781 additions and 150 deletions.
11 changes: 10 additions & 1 deletion docs/release-notes/v4.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,14 @@ New protected methods have been added to `KeyValueStore` to assist with serializ

## `DefaultDataFunctions` has moved

The [DefaultDataFunctions](../../src//DataCore.Adapter/RealTimeData/DefaultDataFunctions.cs) class has moved from the `IntelligentPlant.AppStoreConnect.Adapter.Abstractions` package to the `IntelligentPlant.AppStoreConnect.Adapter` package.
The [DefaultDataFunctions](../../src/DataCore.Adapter/RealTimeData/DefaultDataFunctions.cs) class has moved from the `IntelligentPlant.AppStoreConnect.Adapter.Abstractions` package to the `IntelligentPlant.AppStoreConnect.Adapter` package.


## `TagValueBucket` changes

The [TagValueBucket](../../src/DataCore.Adapter/RealTimeData/Utilities/TagValueBucket.cs) type used by the [AggregationHelper](../../src/DataCore.Adapter/RealTimeData/Utilities/AggregationHelper.cs) class has changed to assist with the calculation of time-weighted aggregates. The `StartBoundary` and `EndBoundary` properties have been removed and new `BeforeStartBoundary`, `AfterStartBoundary`, `BeforeEndBoundary` and `AfterEndBoundary` properties have been added.

Custom aggregate functions registered with `AggregationHelper` may need to be rewritten to account for these changes.


# Non-Breaking Changes
Expand Down Expand Up @@ -71,4 +78,6 @@ If you have written a custom `IKeyValueStore` implementation you will need to up

If you are using an `IKeyValueStore` in your adapter implementation, you should replace calls to the `KeyValueStoreExtensions.ReadJsonAsync<T>` and `KeyValueStoreExtensions.WriteJsonAsync<T>` extension methods with calls to `IKeyValueStore.ReadAsync<T>` and `IKeyValueStore.WriteAsync<T>` respectively. Additionally, you may wish to set the `KeyValueStoreOptions.JsonOptions` property to customise JSON serialization if you are using the Microsoft FASTER-, file system- or Sqlite-based stores.

Custom aggregate functions registered with the `AggregationHelper` class may need to be rewritten to account for the changes to the `TagValueBucket` type.

If you have installed the adapter host project template for Visual Studio and `dotnet new`, you can upgrade the template to the latest version by running `dotnet new update` from the command line.
7 changes: 0 additions & 7 deletions src/DataCore.Adapter/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -368,11 +368,6 @@ DataCore.Adapter.RealTimeData.Utilities.AggregationHelper.GetAggregatedValues(Sy
DataCore.Adapter.RealTimeData.Utilities.AggregationHelper.GetSupportedDataFunctions() -> System.Collections.Generic.IEnumerable<DataCore.Adapter.RealTimeData.DataFunctionDescriptor!>!
DataCore.Adapter.RealTimeData.Utilities.AggregationHelper.RegisterDataFunction(DataCore.Adapter.RealTimeData.DataFunctionDescriptor! descriptor, DataCore.Adapter.RealTimeData.Utilities.AggregateCalculator! calculator) -> bool
DataCore.Adapter.RealTimeData.Utilities.AggregationHelper.UnregisterDataFunction(string! functionId) -> bool
DataCore.Adapter.RealTimeData.Utilities.BoundaryInfo
DataCore.Adapter.RealTimeData.Utilities.BoundaryInfo.BestQualityValue.get -> DataCore.Adapter.RealTimeData.TagValueExtended?
DataCore.Adapter.RealTimeData.Utilities.BoundaryInfo.BoundaryInfo() -> void
DataCore.Adapter.RealTimeData.Utilities.BoundaryInfo.BoundaryStatus.get -> DataCore.Adapter.RealTimeData.TagValueStatus
DataCore.Adapter.RealTimeData.Utilities.BoundaryInfo.ClosestValue.get -> DataCore.Adapter.RealTimeData.TagValueExtended?
DataCore.Adapter.RealTimeData.Utilities.CommonTagValuePropertyNames
DataCore.Adapter.RealTimeData.Utilities.InterpolationCalculationType
DataCore.Adapter.RealTimeData.Utilities.InterpolationCalculationType.Interpolate = 0 -> DataCore.Adapter.RealTimeData.Utilities.InterpolationCalculationType
Expand All @@ -387,10 +382,8 @@ DataCore.Adapter.RealTimeData.Utilities.PlotValue.PlotValue(DataCore.Adapter.Rea
DataCore.Adapter.RealTimeData.Utilities.PlotValue.Sample.get -> DataCore.Adapter.RealTimeData.TagValueExtended!
DataCore.Adapter.RealTimeData.Utilities.PlotValueSelector
DataCore.Adapter.RealTimeData.Utilities.TagValueBucket
DataCore.Adapter.RealTimeData.Utilities.TagValueBucket.EndBoundary.get -> DataCore.Adapter.RealTimeData.Utilities.BoundaryInfo!
DataCore.Adapter.RealTimeData.Utilities.TagValueBucket.RawSampleCount.get -> int
DataCore.Adapter.RealTimeData.Utilities.TagValueBucket.RawSamples.get -> System.Collections.Generic.IEnumerable<DataCore.Adapter.RealTimeData.TagValueExtended!>!
DataCore.Adapter.RealTimeData.Utilities.TagValueBucket.StartBoundary.get -> DataCore.Adapter.RealTimeData.Utilities.BoundaryInfo!
DataCore.Adapter.RealTimeData.Utilities.TagValueBucket.TagValueBucket(System.DateTime utcBucketStart, System.DateTime utcBucketEnd, System.DateTime utcQueryStart, System.DateTime utcQueryEnd) -> void
DataCore.Adapter.RealTimeData.Utilities.TagValueBucket.UtcBucketEnd.get -> System.DateTime
DataCore.Adapter.RealTimeData.Utilities.TagValueBucket.UtcBucketStart.get -> System.DateTime
Expand Down
15 changes: 15 additions & 0 deletions src/DataCore.Adapter/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,26 @@ const DataCore.Adapter.RealTimeData.DefaultDataFunctions.Constants.FunctionIdRan
const DataCore.Adapter.RealTimeData.DefaultDataFunctions.Constants.FunctionIdStandardDeviation = "STDDEV" -> string!
const DataCore.Adapter.RealTimeData.DefaultDataFunctions.Constants.FunctionIdTimeAverage = "TIMEAVERAGE" -> string!
const DataCore.Adapter.RealTimeData.DefaultDataFunctions.Constants.FunctionIdVariance = "VARIANCE" -> string!
const DataCore.Adapter.RealTimeData.Utilities.CommonTagValuePropertyNames.Partial = "Partial" -> string!
DataCore.Adapter.DefaultAdapterCallContext.DefaultAdapterCallContext(System.Security.Claims.ClaimsPrincipal? user = null, string? connectionId = null, string? correlationId = null, System.Globalization.CultureInfo? cultureInfo = null, System.IServiceProvider? serviceProvider = null) -> void
DataCore.Adapter.DefaultAdapterCallContext.Services.get -> System.IServiceProvider!
DataCore.Adapter.RealTimeData.DefaultDataFunctions
DataCore.Adapter.RealTimeData.DefaultDataFunctions.Constants
DataCore.Adapter.RealTimeData.TagValueBuilder.WithSteppedTransition(bool stepped) -> DataCore.Adapter.RealTimeData.TagValueBuilder!
DataCore.Adapter.RealTimeData.Utilities.PostBoundaryInfo
DataCore.Adapter.RealTimeData.Utilities.PostBoundaryInfo.BestQualityValue.get -> DataCore.Adapter.RealTimeData.TagValueExtended?
DataCore.Adapter.RealTimeData.Utilities.PostBoundaryInfo.BoundaryStatus.get -> DataCore.Adapter.RealTimeData.TagValueStatus
DataCore.Adapter.RealTimeData.Utilities.PostBoundaryInfo.ClosestValue.get -> DataCore.Adapter.RealTimeData.TagValueExtended?
DataCore.Adapter.RealTimeData.Utilities.PostBoundaryInfo.PostBoundaryInfo() -> void
DataCore.Adapter.RealTimeData.Utilities.PreBoundaryInfo
DataCore.Adapter.RealTimeData.Utilities.PreBoundaryInfo.BestQualityValue.get -> DataCore.Adapter.RealTimeData.TagValueExtended?
DataCore.Adapter.RealTimeData.Utilities.PreBoundaryInfo.BoundaryStatus.get -> DataCore.Adapter.RealTimeData.TagValueStatus
DataCore.Adapter.RealTimeData.Utilities.PreBoundaryInfo.ClosestValue.get -> DataCore.Adapter.RealTimeData.TagValueExtended?
DataCore.Adapter.RealTimeData.Utilities.PreBoundaryInfo.PreBoundaryInfo() -> void
DataCore.Adapter.RealTimeData.Utilities.TagValueBucket.AfterEndBoundary.get -> DataCore.Adapter.RealTimeData.Utilities.PostBoundaryInfo!
DataCore.Adapter.RealTimeData.Utilities.TagValueBucket.AfterStartBoundary.get -> DataCore.Adapter.RealTimeData.Utilities.PostBoundaryInfo!
DataCore.Adapter.RealTimeData.Utilities.TagValueBucket.BeforeEndBoundary.get -> DataCore.Adapter.RealTimeData.Utilities.PreBoundaryInfo!
DataCore.Adapter.RealTimeData.Utilities.TagValueBucket.BeforeStartBoundary.get -> DataCore.Adapter.RealTimeData.Utilities.PreBoundaryInfo!
static DataCore.Adapter.AdapterAccessorExtensions.GetAdapterDescriptorAsync(this DataCore.Adapter.IAdapterAccessor! adapterAccessor, DataCore.Adapter.IAdapterCallContext! context, string! adapterId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<DataCore.Adapter.Common.AdapterDescriptorExtended?>!
static DataCore.Adapter.ChannelExtensions.ToEnumerable<T>(this System.Collections.Generic.IAsyncEnumerable<T>! enumerable, int maxItems, int expectedItems, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<T>!>!
static DataCore.Adapter.RealTimeData.DefaultDataFunctions.Average.get -> DataCore.Adapter.RealTimeData.DataFunctionDescriptor!
Expand Down
1 change: 1 addition & 0 deletions src/DataCore.Adapter/RealTimeData/DefaultDataFunctions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;

using DataCore.Adapter.Common;

namespace DataCore.Adapter.RealTimeData {
Expand Down
Loading

0 comments on commit 8b593d7

Please sign in to comment.