feat(historian-gateway): HistoryAggregateType->RetrievalMode mapper (matrix-guarded)
Claude-Session: https://claude.ai/code/session_012SDSQ3AcaXqPcBtDESBRii
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
using ZB.MOM.WW.HistorianGateway.Contracts.Grpc;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Mapping;
|
||||
|
||||
/// <summary>
|
||||
/// Maps the driver-agnostic <see cref="HistoryAggregateType"/> (OPC UA Part 13 aggregate) onto the
|
||||
/// gateway's native <see cref="RetrievalMode"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Average/Minimum/Maximum line up with the legacy Wonderware client's aggregate mapping. The two
|
||||
/// remaining members are now served by <b>native</b> gateway retrieval modes:
|
||||
/// <see cref="HistoryAggregateType.Total"/> → <see cref="RetrievalMode.Integral"/> and
|
||||
/// <see cref="HistoryAggregateType.Count"/> → <see cref="RetrievalMode.Counter"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This replaces the Wonderware-era client-side workarounds (Total derived as Average × interval,
|
||||
/// Count approximated from a value count): no client-side scaling is performed any more, so the
|
||||
/// gateway path is a strict improvement.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
internal static class AggregateModeMapper
|
||||
{
|
||||
/// <summary>Maps an aggregate function to the gateway retrieval mode.</summary>
|
||||
/// <param name="aggregate">The driver-agnostic aggregate function.</param>
|
||||
/// <returns>The matching gateway <see cref="RetrievalMode"/>.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">A future, unmapped enum member (fails the matrix guard).</exception>
|
||||
public static RetrievalMode ToRetrievalMode(HistoryAggregateType aggregate) => aggregate switch
|
||||
{
|
||||
HistoryAggregateType.Average => RetrievalMode.TimeWeightedAverage,
|
||||
HistoryAggregateType.Minimum => RetrievalMode.MinimumWithTime,
|
||||
HistoryAggregateType.Maximum => RetrievalMode.MaximumWithTime,
|
||||
HistoryAggregateType.Total => RetrievalMode.Integral,
|
||||
HistoryAggregateType.Count => RetrievalMode.Counter,
|
||||
_ => throw new ArgumentOutOfRangeException(
|
||||
nameof(aggregate), aggregate, "Unmapped HistoryAggregateType — add a RetrievalMode mapping."),
|
||||
};
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions; // HistoryAggregateType
|
||||
using ZB.MOM.WW.HistorianGateway.Contracts.Grpc; // RetrievalMode
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Mapping;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests.Mapping;
|
||||
|
||||
public sealed class AggregateModeMapperTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(HistoryAggregateType.Average, 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));
|
||||
|
||||
[Fact] // matrix guard: a new HistoryAggregateType member must fail here
|
||||
public void Every_aggregate_member_is_mapped()
|
||||
{
|
||||
foreach (var a in Enum.GetValues<HistoryAggregateType>())
|
||||
_ = AggregateModeMapper.ToRetrievalMode(a); // must not throw for any defined member
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user