fix(historian): #679 Gateway — Average/Count not supported (no AVEVA equivalent), TimeAverage → TimeWeightedAverage

This commit is contained in:
Joseph Doherty
2026-10-11 15:19:19 -04:00
parent 6d5c1c768b
commit fb5ec80fe6
7 changed files with 56 additions and 22 deletions
@@ -37,6 +37,30 @@ public sealed class GatewayHistorianDataSourceTests
Assert.Equal(TimeSpan.FromSeconds(60), fake.LastAggregateInterval);
}
[Fact]
public async Task ReadProcessed_TimeAverage_requests_TimeWeightedAverage()
{
var fake = new FakeHistorianGatewayClient();
var ds = new GatewayHistorianDataSource(fake, NullLogger<GatewayHistorianDataSource>.Instance);
await ds.ReadProcessedAsync("T", default, default, TimeSpan.FromSeconds(60), HistoryAggregateType.TimeAverage, TestContext.Current.CancellationToken);
Assert.Equal(RetrievalMode.TimeWeightedAverage, fake.LastAggregateMode);
}
[Theory] // #679: no AVEVA equivalent — not supported, gateway never called, no backend failure recorded
[InlineData(HistoryAggregateType.Average)]
[InlineData(HistoryAggregateType.Count)]
public async Task ReadProcessed_unsupported_aggregate_throws_without_calling_gateway_or_recording_failure(HistoryAggregateType aggregate)
{
var fake = new FakeHistorianGatewayClient();
var ds = new GatewayHistorianDataSource(fake, NullLogger<GatewayHistorianDataSource>.Instance);
await Assert.ThrowsAsync<HistoryAggregateNotSupportedException>(() => ds.ReadProcessedAsync(
"T", default, default, TimeSpan.FromSeconds(60), aggregate, TestContext.Current.CancellationToken));
Assert.Equal(0, fake.ReadAggregateCallCount);
var h = ds.GetHealthSnapshot();
Assert.Equal(0, h.TotalFailures);
Assert.Equal(0, h.TotalQueries);
}
[Fact]
public async Task ReadAtTime_aligns_one_snapshot_per_timestamp_with_gaps_Bad()
{
@@ -8,11 +8,10 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests.Mapping;
public sealed class AggregateModeMapperTests
{
[Theory]
[InlineData(HistoryAggregateType.Average, RetrievalMode.TimeWeightedAverage)]
[InlineData(HistoryAggregateType.TimeAverage, RetrievalMode.TimeWeightedAverage)]
[InlineData(HistoryAggregateType.Minimum, RetrievalMode.MinimumWithTime)]
[InlineData(HistoryAggregateType.Maximum, RetrievalMode.MaximumWithTime)]
[InlineData(HistoryAggregateType.Total, RetrievalMode.Integral)]
[InlineData(HistoryAggregateType.Count, RetrievalMode.Counter)]
public void Maps_each_aggregate(HistoryAggregateType a, RetrievalMode expected)
=> Assert.Equal(expected, AggregateModeMapper.ToRetrievalMode(a));
@@ -21,8 +20,8 @@ public sealed class AggregateModeMapperTests
{
var original = new[]
{
HistoryAggregateType.Average, HistoryAggregateType.Minimum, HistoryAggregateType.Maximum,
HistoryAggregateType.Total, HistoryAggregateType.Count,
HistoryAggregateType.Minimum, HistoryAggregateType.Maximum,
HistoryAggregateType.Total, HistoryAggregateType.TimeAverage,
};
foreach (var a in Enum.GetValues<HistoryAggregateType>())
{
@@ -33,6 +32,12 @@ public sealed class AggregateModeMapperTests
}
}
[Theory] // #679: AVEVA has no arithmetic mean and no sample count (Counter is a rollover increase)
[InlineData(HistoryAggregateType.Average)]
[InlineData(HistoryAggregateType.Count)]
public void Average_and_Count_are_not_supported(HistoryAggregateType a)
=> Assert.Throws<HistoryAggregateNotSupportedException>(() => AggregateModeMapper.ToRetrievalMode(a));
[Fact]
public void Undefined_value_is_a_programming_error_not_a_not_supported()
=> Assert.Throws<ArgumentOutOfRangeException>(() => AggregateModeMapper.ToRetrievalMode((HistoryAggregateType)9999));