diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway/Mapping/AggregateModeMapper.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway/Mapping/AggregateModeMapper.cs
new file mode 100644
index 00000000..f3d8b3a0
--- /dev/null
+++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway/Mapping/AggregateModeMapper.cs
@@ -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;
+
+///
+/// Maps the driver-agnostic (OPC UA Part 13 aggregate) onto the
+/// gateway's native .
+///
+///
+///
+/// Average/Minimum/Maximum line up with the legacy Wonderware client's aggregate mapping. The two
+/// remaining members are now served by native gateway retrieval modes:
+/// → and
+/// → .
+///
+///
+/// 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.
+///
+///
+internal static class AggregateModeMapper
+{
+ /// Maps an aggregate function to the gateway retrieval mode.
+ /// The driver-agnostic aggregate function.
+ /// The matching gateway .
+ /// A future, unmapped enum member (fails the matrix guard).
+ 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."),
+ };
+}
diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests/Mapping/AggregateModeMapperTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests/Mapping/AggregateModeMapperTests.cs
new file mode 100644
index 00000000..871b513f
--- /dev/null
+++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests/Mapping/AggregateModeMapperTests.cs
@@ -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())
+ _ = AggregateModeMapper.ToRetrievalMode(a); // must not throw for any defined member
+ }
+}