feat(historian-gateway): DriverDataType->HistorianDataType mapper + write-gap fallbacks (matrix-guarded)

Claude-Session: https://claude.ai/code/session_012SDSQ3AcaXqPcBtDESBRii
This commit is contained in:
Joseph Doherty
2026-06-26 16:32:38 -04:00
parent c822a6b196
commit 3226b87818
2 changed files with 115 additions and 0 deletions
@@ -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;
/// <summary>
/// Maps the driver-agnostic <see cref="DriverDataType"/> onto the gateway's
/// <see cref="HistorianDataType"/> for tag provisioning + historical writes.
/// </summary>
/// <remarks>
/// 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:
/// <see cref="DriverDataType.UInt16"/> maps to <see cref="HistorianDataType.Uint4"/> (the historian's
/// <c>UInt2</c> write path is not proven). String / DateTime / Reference are not historized in v1
/// and throw <see cref="NotSupportedException"/>; callers that want to skip them without catching an
/// exception should consult <see cref="IsHistorizable(DriverDataType)"/> first.
/// </remarks>
internal static class HistorianTypeMapper
{
/// <summary>Maps a driver data type to the historian data type used for provisioning/writes.</summary>
/// <param name="dataType">The driver-agnostic data type.</param>
/// <returns>The matching <see cref="HistorianDataType"/>.</returns>
/// <exception cref="NotSupportedException">
/// The type is explicitly deferred (string/datetime/reference) or a future, unclassified member.
/// </exception>
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."),
};
/// <summary>
/// True when <paramref name="dataType"/> is one of the nine historizable numeric types — lets the
/// provisioning hook skip deferred types without catching <see cref="NotSupportedException"/>.
/// </summary>
/// <param name="dataType">The driver-agnostic data type.</param>
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,
};
}