feat(historian-gateway): sample/aggregate->DataValueSnapshot + quality mapper (Wonderware parity)
Claude-Session: https://claude.ai/code/session_012SDSQ3AcaXqPcBtDESBRii
This commit is contained in:
+19
@@ -0,0 +1,19 @@
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Mapping;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests.Mapping;
|
||||
|
||||
public sealed class GatewayQualityMapperTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(192, 0x00000000u)] // Good
|
||||
[InlineData(216, 0x00D80000u)] // Good_LocalOverride
|
||||
[InlineData(64, 0x40000000u)] // Uncertain
|
||||
[InlineData(0, 0x80000000u)] // Bad
|
||||
[InlineData(8, 0x808A0000u)] // Bad_NotConnected
|
||||
[InlineData(255, 0x00000000u)] // >=192 bucket
|
||||
[InlineData(100, 0x40000000u)] // >=64 bucket
|
||||
[InlineData(1, 0x80000000u)] // bad bucket
|
||||
public void Maps_opc_quality_byte(byte q, uint expected)
|
||||
=> Assert.Equal(expected, GatewayQualityMapper.Map(q));
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.HistorianGateway.Contracts.Grpc;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Mapping;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests.Mapping;
|
||||
|
||||
public sealed class SampleMapperTests
|
||||
{
|
||||
[Fact]
|
||||
public void Numeric_sample_maps_value_quality_and_timestamps()
|
||||
{
|
||||
var s = new HistorianSample { Tag = "T", NumericValue = 12.5,
|
||||
Quality = 192, OpcQuality = 192, Timestamp = Ts(2026, 1, 1, 0, 0, 0) };
|
||||
var snap = SampleMapper.ToSnapshot(s);
|
||||
Assert.Equal(12.5, Assert.IsType<double>(snap.Value));
|
||||
Assert.Equal(0x00000000u, snap.StatusCode);
|
||||
Assert.Equal(DateTimeKind.Utc, snap.SourceTimestampUtc!.Value.Kind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void String_sample_carries_string_value()
|
||||
{
|
||||
var s = new HistorianSample { Tag = "T", StringValue = "abc", OpcQuality = 192, Timestamp = Ts(2026, 1, 1, 0, 0, 0) };
|
||||
Assert.Equal("abc", SampleMapper.ToSnapshot(s).Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Bad_quality_sample_maps_to_bad_status()
|
||||
{
|
||||
var s = new HistorianSample { Tag = "T", NumericValue = 1.0, OpcQuality = 0, Timestamp = Ts(2026, 1, 1, 0, 0, 0) };
|
||||
Assert.Equal(0x80000000u, SampleMapper.ToSnapshot(s).StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Aggregate_null_value_is_BadNoData()
|
||||
{
|
||||
var a = new HistorianAggregateSample { Tag = "T", /* Value unset, no Good quality */ EndTime = Ts(2026, 1, 1, 0, 0, 0) };
|
||||
var snap = SampleMapper.ToAggregateSnapshot(a);
|
||||
Assert.Equal(0x800E0000u, snap.StatusCode); // BadNoData
|
||||
Assert.Null(snap.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Aggregate_good_bucket_carries_value()
|
||||
{
|
||||
var a = new HistorianAggregateSample { Tag = "T", Value = 42.0, OpcQuality = 192, EndTime = Ts(2026, 1, 1, 0, 0, 0) };
|
||||
var snap = SampleMapper.ToAggregateSnapshot(a);
|
||||
Assert.Equal(0x00000000u, snap.StatusCode); // Good
|
||||
Assert.Equal(42.0, Assert.IsType<double>(snap.Value));
|
||||
}
|
||||
|
||||
// Ts(...) builds a Google.Protobuf.WellKnownTypes.Timestamp from UTC parts.
|
||||
private static Timestamp Ts(int y, int mo, int d, int h, int mi, int s)
|
||||
=> Timestamp.FromDateTime(new DateTime(y, mo, d, h, mi, s, DateTimeKind.Utc));
|
||||
}
|
||||
Reference in New Issue
Block a user