using Opc.Ua; using Opc.Ua.Server; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Core.Abstractions; using HistorianRead = ZB.MOM.WW.OtOpcUa.Core.Abstractions.HistoryReadResult; namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests; /// /// #736 — assigning re-syncs /// HistoryServerCapabilities/MaxReturnDataValues (i=11273) to the source's /// , read back the way a client would. The Null /// source (the historian-disabled default) advertises Part 11's "no limit". /// public sealed class NodeManagerMaxReturnDataValuesSyncTests(BoundsServerFixture fx) : IClassFixture { [Fact] public async Task Setting_the_historian_source_advertises_its_raw_cap() { await fx.Gate.WaitAsync(TestContext.Current.CancellationToken); try { fx.Nm.HistorianDataSource = new CappedSource(7_000); (await ReadAdvertisedAsync()).ShouldBe(7_000u); fx.Nm.HistorianDataSource = new CappedSource(250); (await ReadAdvertisedAsync()).ShouldBe(250u); fx.Nm.HistorianDataSource = null!; // the Null default: no cap (await ReadAdvertisedAsync()).ShouldBe(0u); } finally { fx.Nm.HistorianDataSource = null!; fx.Gate.Release(); } } private async Task ReadAdvertisedAsync() { var context = new OperationContext(new RequestHeader(), secureChannelContext: null, RequestType.Read, identity: null); var nodesToRead = new ReadValueIdCollection { new ReadValueId { NodeId = VariableIds.HistoryServerCapabilities_MaxReturnDataValues, AttributeId = Attributes.Value }, }; var (values, _) = await fx.Nm.Server.NodeManager.ReadAsync(context, 0, TimestampsToReturn.Neither, nodesToRead, TestContext.Current.CancellationToken); values.Count.ShouldBe(1); StatusCode.IsGood(values[0].StatusCode).ShouldBeTrue(values[0].StatusCode.ToString()); return (uint)values[0].Value; } private sealed class CappedSource(int cap) : IHistorianDataSource { public int MaxRawValuesPerRead => cap; public Task ReadRawAsync(string r, DateTime s, DateTime e, uint m, CancellationToken ct) => NullHistorianDataSource.Instance.ReadRawAsync(r, s, e, m, ct); public Task ReadProcessedAsync(string r, DateTime s, DateTime e, TimeSpan i, HistoryAggregateType a, CancellationToken ct) => NullHistorianDataSource.Instance.ReadProcessedAsync(r, s, e, i, a, ct); public Task ReadAtTimeAsync(string r, IReadOnlyList t, CancellationToken ct) => NullHistorianDataSource.Instance.ReadAtTimeAsync(r, t, ct); public Task ReadEventsAsync(string? n, DateTime s, DateTime e, int m, CancellationToken ct) => NullHistorianDataSource.Instance.ReadEventsAsync(n, s, e, m, ct); public HistorianHealthSnapshot GetHealthSnapshot() => NullHistorianDataSource.Instance.GetHealthSnapshot(); public void Dispose() { } } }