diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway/Mapping/HistorianTypeMapper.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway/Mapping/HistorianTypeMapper.cs
new file mode 100644
index 00000000..97631961
--- /dev/null
+++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway/Mapping/HistorianTypeMapper.cs
@@ -0,0 +1,63 @@
+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 onto the gateway's
+/// for tag provisioning + historical writes.
+///
+///
+/// Only the nine numeric types are historizable on the gateway's analog write path. Two of them
+/// fall back to a wider historian type because the narrower one's write path is deferred upstream:
+/// maps to (the historian's
+/// UInt2 write path is not proven). String / DateTime / Reference are not historized in v1
+/// and throw ; callers that want to skip them without catching an
+/// exception should consult first.
+///
+internal static class HistorianTypeMapper
+{
+ /// Maps a driver data type to the historian data type used for provisioning/writes.
+ /// The driver-agnostic data type.
+ /// The matching .
+ ///
+ /// The type is explicitly deferred (string/datetime/reference) or a future, unclassified member.
+ ///
+ public static HistorianDataType ToHistorianDataType(DriverDataType dataType) => dataType switch
+ {
+ DriverDataType.Boolean => HistorianDataType.Int1,
+ DriverDataType.Int16 => HistorianDataType.Int2,
+ DriverDataType.Int32 => HistorianDataType.Int4,
+ DriverDataType.Int64 => HistorianDataType.Int8,
+ DriverDataType.UInt16 => HistorianDataType.Uint4, // UInt2 write path deferred upstream → widen
+ DriverDataType.UInt32 => HistorianDataType.Uint4,
+ DriverDataType.UInt64 => HistorianDataType.Uint8,
+ DriverDataType.Float32 => HistorianDataType.Float,
+ DriverDataType.Float64 => HistorianDataType.Double,
+ DriverDataType.String or DriverDataType.DateTime or DriverDataType.Reference =>
+ throw new NotSupportedException(
+ $"DriverDataType.{dataType} is not historized in v1 " +
+ "(string/datetime/reference writes are deferred — gated on the analog SQL write path)."),
+ _ => throw new NotSupportedException(
+ $"DriverDataType.{dataType} is not classified for historian write mapping — add a HistorianDataType mapping."),
+ };
+
+ ///
+ /// True when is one of the nine historizable numeric types — lets the
+ /// provisioning hook skip deferred types without catching .
+ ///
+ /// The driver-agnostic data type.
+ public static bool IsHistorizable(DriverDataType dataType) => dataType switch
+ {
+ DriverDataType.Boolean
+ or DriverDataType.Int16
+ or DriverDataType.Int32
+ or DriverDataType.Int64
+ or DriverDataType.UInt16
+ or DriverDataType.UInt32
+ or DriverDataType.UInt64
+ or DriverDataType.Float32
+ or DriverDataType.Float64 => true,
+ _ => false,
+ };
+}
diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests/Mapping/HistorianTypeMapperTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests/Mapping/HistorianTypeMapperTests.cs
new file mode 100644
index 00000000..1dde2678
--- /dev/null
+++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests/Mapping/HistorianTypeMapperTests.cs
@@ -0,0 +1,52 @@
+using Xunit;
+using ZB.MOM.WW.OtOpcUa.Core.Abstractions; // DriverDataType
+using ZB.MOM.WW.HistorianGateway.Contracts.Grpc; // HistorianDataType
+using ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Mapping;
+
+namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests.Mapping;
+
+public sealed class HistorianTypeMapperTests
+{
+ [Theory]
+ [InlineData(DriverDataType.Boolean, HistorianDataType.Int1)]
+ [InlineData(DriverDataType.Int16, HistorianDataType.Int2)]
+ [InlineData(DriverDataType.Int32, HistorianDataType.Int4)]
+ [InlineData(DriverDataType.Int64, HistorianDataType.Int8)]
+ [InlineData(DriverDataType.UInt16, HistorianDataType.Uint4)] // fallback: UInt2 write deferred upstream
+ [InlineData(DriverDataType.UInt32, HistorianDataType.Uint4)]
+ [InlineData(DriverDataType.UInt64, HistorianDataType.Uint8)]
+ [InlineData(DriverDataType.Float32, HistorianDataType.Float)]
+ [InlineData(DriverDataType.Float64, HistorianDataType.Double)]
+ public void Maps_writable_numeric_types(DriverDataType d, HistorianDataType expected)
+ => Assert.Equal(expected, HistorianTypeMapper.ToHistorianDataType(d));
+
+ [Theory]
+ [InlineData(DriverDataType.String)]
+ [InlineData(DriverDataType.DateTime)]
+ [InlineData(DriverDataType.Reference)]
+ public void Deferred_types_throw_NotSupported_with_clear_message(DriverDataType d)
+ {
+ var ex = Assert.Throws(() => HistorianTypeMapper.ToHistorianDataType(d));
+ Assert.Contains("not historized in v1", ex.Message); // human-actionable, no tag value leaked
+ }
+
+ [Fact] // matrix guard: a new DriverDataType member must be classified (mapped or explicitly deferred)
+ public void Every_DriverDataType_member_is_classified()
+ {
+ foreach (var d in Enum.GetValues())
+ {
+ try { _ = HistorianTypeMapper.ToHistorianDataType(d); }
+ catch (NotSupportedException) { /* explicitly deferred — acceptable */ }
+ // any OTHER exception (e.g. ArgumentOutOfRangeException from an unhandled new member) fails the test
+ }
+ }
+
+ [Theory]
+ [InlineData(DriverDataType.Boolean, true)]
+ [InlineData(DriverDataType.Float64, true)]
+ [InlineData(DriverDataType.String, false)]
+ [InlineData(DriverDataType.DateTime, false)]
+ [InlineData(DriverDataType.Reference, false)]
+ public void IsHistorizable_matches_writable_set(DriverDataType d, bool expected)
+ => Assert.Equal(expected, HistorianTypeMapper.IsHistorizable(d));
+}