159 lines
6.2 KiB
C#
159 lines
6.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using MessagePack;
|
|
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Backend;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Backend.Galaxy;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Backend.Historian;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Backend.MxAccess;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Sta;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Shared.Contracts;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests;
|
|
|
|
[Trait("Category", "Unit")]
|
|
public sealed class HistoryReadProcessedTests
|
|
{
|
|
[Fact]
|
|
public async Task ReturnsDisabledError_When_NoHistorianConfigured()
|
|
{
|
|
using var pump = new StaPump("Test.Sta");
|
|
await pump.WaitForStartedAsync();
|
|
var mx = new MxAccessClient(pump, new MxProxyAdapter(), "processed-test");
|
|
using var backend = new MxAccessGalaxyBackend(
|
|
new GalaxyRepository(new GalaxyRepositoryOptions { ConnectionString = "Server=.;Database=ZB;Integrated Security=True;" }),
|
|
mx,
|
|
historian: null);
|
|
|
|
var resp = await backend.HistoryReadProcessedAsync(new HistoryReadProcessedRequest
|
|
{
|
|
TagReference = "T",
|
|
StartUtcUnixMs = 0,
|
|
EndUtcUnixMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
|
IntervalMs = 1000,
|
|
AggregateColumn = "Average",
|
|
}, CancellationToken.None);
|
|
|
|
resp.Success.ShouldBeFalse();
|
|
resp.Error.ShouldContain("Historian disabled");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Rejects_NonPositiveInterval()
|
|
{
|
|
using var pump = new StaPump("Test.Sta");
|
|
await pump.WaitForStartedAsync();
|
|
var mx = new MxAccessClient(pump, new MxProxyAdapter(), "processed-test");
|
|
var fake = new FakeHistorianDataSource();
|
|
using var backend = new MxAccessGalaxyBackend(
|
|
new GalaxyRepository(new GalaxyRepositoryOptions { ConnectionString = "Server=.;Database=ZB;Integrated Security=True;" }),
|
|
mx,
|
|
fake);
|
|
|
|
var resp = await backend.HistoryReadProcessedAsync(new HistoryReadProcessedRequest
|
|
{
|
|
TagReference = "T",
|
|
IntervalMs = 0,
|
|
AggregateColumn = "Average",
|
|
}, CancellationToken.None);
|
|
|
|
resp.Success.ShouldBeFalse();
|
|
resp.Error.ShouldContain("IntervalMs");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Maps_AggregateSample_With_Value_To_Good()
|
|
{
|
|
using var pump = new StaPump("Test.Sta");
|
|
await pump.WaitForStartedAsync();
|
|
var mx = new MxAccessClient(pump, new MxProxyAdapter(), "processed-test");
|
|
var fake = new FakeHistorianDataSource(new HistorianAggregateSample
|
|
{
|
|
Value = 12.34,
|
|
TimestampUtc = new DateTime(2026, 4, 18, 10, 0, 0, DateTimeKind.Utc),
|
|
});
|
|
using var backend = new MxAccessGalaxyBackend(
|
|
new GalaxyRepository(new GalaxyRepositoryOptions { ConnectionString = "Server=.;Database=ZB;Integrated Security=True;" }),
|
|
mx,
|
|
fake);
|
|
|
|
var resp = await backend.HistoryReadProcessedAsync(new HistoryReadProcessedRequest
|
|
{
|
|
TagReference = "T",
|
|
StartUtcUnixMs = 0,
|
|
EndUtcUnixMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
|
IntervalMs = 60_000,
|
|
AggregateColumn = "Average",
|
|
}, CancellationToken.None);
|
|
|
|
resp.Success.ShouldBeTrue();
|
|
resp.Values.Length.ShouldBe(1);
|
|
resp.Values[0].StatusCode.ShouldBe(0u); // Good
|
|
resp.Values[0].ValueBytes.ShouldNotBeNull();
|
|
MessagePackSerializer.Deserialize<double>(resp.Values[0].ValueBytes!).ShouldBe(12.34);
|
|
fake.LastAggregateColumn.ShouldBe("Average");
|
|
fake.LastIntervalMs.ShouldBe(60_000d);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Maps_Null_Bucket_To_BadNoData()
|
|
{
|
|
using var pump = new StaPump("Test.Sta");
|
|
await pump.WaitForStartedAsync();
|
|
var mx = new MxAccessClient(pump, new MxProxyAdapter(), "processed-test");
|
|
var fake = new FakeHistorianDataSource(new HistorianAggregateSample
|
|
{
|
|
Value = null,
|
|
TimestampUtc = DateTime.UtcNow,
|
|
});
|
|
using var backend = new MxAccessGalaxyBackend(
|
|
new GalaxyRepository(new GalaxyRepositoryOptions { ConnectionString = "Server=.;Database=ZB;Integrated Security=True;" }),
|
|
mx,
|
|
fake);
|
|
|
|
var resp = await backend.HistoryReadProcessedAsync(new HistoryReadProcessedRequest
|
|
{
|
|
TagReference = "T",
|
|
IntervalMs = 1000,
|
|
AggregateColumn = "Minimum",
|
|
}, CancellationToken.None);
|
|
|
|
resp.Success.ShouldBeTrue();
|
|
resp.Values.Length.ShouldBe(1);
|
|
resp.Values[0].StatusCode.ShouldBe(0x800E0000u); // BadNoData
|
|
resp.Values[0].ValueBytes.ShouldBeNull();
|
|
}
|
|
|
|
private sealed class FakeHistorianDataSource : IHistorianDataSource
|
|
{
|
|
private readonly HistorianAggregateSample[] _samples;
|
|
public string? LastAggregateColumn { get; private set; }
|
|
public double LastIntervalMs { get; private set; }
|
|
|
|
public FakeHistorianDataSource(params HistorianAggregateSample[] samples) => _samples = samples;
|
|
|
|
public Task<List<HistorianSample>> ReadRawAsync(string tag, DateTime s, DateTime e, int max, CancellationToken ct)
|
|
=> Task.FromResult(new List<HistorianSample>());
|
|
|
|
public Task<List<HistorianAggregateSample>> ReadAggregateAsync(
|
|
string tag, DateTime s, DateTime e, double intervalMs, string col, CancellationToken ct)
|
|
{
|
|
LastAggregateColumn = col;
|
|
LastIntervalMs = intervalMs;
|
|
return Task.FromResult(new List<HistorianAggregateSample>(_samples));
|
|
}
|
|
|
|
public Task<List<HistorianSample>> ReadAtTimeAsync(string tag, DateTime[] ts, CancellationToken ct)
|
|
=> Task.FromResult(new List<HistorianSample>());
|
|
|
|
public Task<List<HistorianEventDto>> ReadEventsAsync(string? src, DateTime s, DateTime e, int max, CancellationToken ct)
|
|
=> Task.FromResult(new List<HistorianEventDto>());
|
|
|
|
public HistorianHealthSnapshot GetHealthSnapshot() => new();
|
|
public void Dispose() { }
|
|
}
|
|
}
|