test: preserve historian baseline oracles under SDK 2.0
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
using Opc.Ua;
|
||||
using Opc.Ua.Server;
|
||||
using ZB.MOM.WW.OtOpcUa.OpcUaServer.Compatibility.Aggregates;
|
||||
using Shouldly;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Conformance;
|
||||
|
||||
/// <summary>
|
||||
/// Drives the pinned OPC UA SDK's own Part 13 aggregate calculators (<c>Opc.Ua.Server</c> — the classes the SDK's
|
||||
/// <c>AggregateManager</c> registers) in-process over a sample set, so our processed reads can be compared with the
|
||||
/// Drives the application's versioned 1.5.378.182 Part 13 compatibility calculators — the same factories
|
||||
/// the upgraded server registers for live aggregates in-process over a sample set, so our processed reads can be compared with the
|
||||
/// reference implementation bucket by bucket (#677). The calculators run under the server's default
|
||||
/// <see cref="AggregateConfiguration"/> (TreatUncertainAsBad = false, PercentDataBad = PercentDataGood = 100,
|
||||
/// UseSlopedExtrapolation = false), sloped interpolation, source timestamps.
|
||||
@@ -17,6 +18,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Conformance;
|
||||
/// </remarks>
|
||||
public static class SdkAggregateOracle
|
||||
{
|
||||
private static readonly ITelemetryContext Telemetry = DefaultTelemetry.Create(_ => { });
|
||||
/// <summary>One processed value: value (double / int / null), full status code, timestamp.</summary>
|
||||
public readonly record struct Result(object? Value, uint StatusCode, DateTime Timestamp);
|
||||
|
||||
@@ -43,22 +45,7 @@ public static class SdkAggregateOracle
|
||||
var id = AggregateId(aggregate);
|
||||
var ms = interval.TotalMilliseconds;
|
||||
var cfg = DefaultConfiguration;
|
||||
IAggregateCalculator calc = aggregate switch
|
||||
{
|
||||
HistoryAggregateType.Average or HistoryAggregateType.TimeAverage or HistoryAggregateType.TimeAverage2
|
||||
or HistoryAggregateType.Total or HistoryAggregateType.Total2 =>
|
||||
new AverageAggregateCalculator(id, start, end, ms, false, cfg, null),
|
||||
HistoryAggregateType.Minimum or HistoryAggregateType.Maximum or HistoryAggregateType.Range
|
||||
or HistoryAggregateType.MinimumActualTime or HistoryAggregateType.MaximumActualTime =>
|
||||
new MinMaxAggregateCalculator(id, start, end, ms, false, cfg, null),
|
||||
HistoryAggregateType.Count => new CountAggregateCalculator(id, start, end, ms, false, cfg, null),
|
||||
HistoryAggregateType.PercentGood or HistoryAggregateType.PercentBad =>
|
||||
new StatusAggregateCalculator(id, start, end, ms, false, cfg, null),
|
||||
HistoryAggregateType.Start or HistoryAggregateType.End or HistoryAggregateType.Delta =>
|
||||
new StartEndAggregateCalculator(id, start, end, ms, false, cfg, null),
|
||||
HistoryAggregateType.Interpolative => new AggregateCalculator(id, start, end, ms, false, cfg, null),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(aggregate), aggregate, "no SDK calculator mapped"),
|
||||
};
|
||||
IAggregateCalculator calc = CompatibilityAggregateFactory.Create(id, start, end, ms, false, cfg, Telemetry);
|
||||
|
||||
// The SDK expects values in the direction of the read: ascending forward, descending reverse.
|
||||
var ordered = samples.OrderBy(s => s.Time).ToList();
|
||||
@@ -68,9 +55,8 @@ public static class SdkAggregateOracle
|
||||
var results = new List<Result>();
|
||||
for (var guard = 0; guard < 100_000; guard++)
|
||||
{
|
||||
var dv = calc.GetProcessedValue(true);
|
||||
if (dv is null) break;
|
||||
results.Add(new Result(dv.Value, dv.StatusCode.Code, DateTime.SpecifyKind(dv.SourceTimestamp, DateTimeKind.Utc)));
|
||||
if (!calc.TryGetProcessedValue(true, out var dv)) break;
|
||||
results.Add(new Result(dv.WrappedValue.AsBoxedObject(Variant.BoxingBehavior.Legacy), dv.StatusCode.Code, (DateTime)dv.SourceTimestamp));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
@@ -123,20 +109,16 @@ public static class SdkAggregateOracle
|
||||
problems.ShouldBeEmpty($"{label} differs from the SDK:\n" + string.Join("\n", problems));
|
||||
}
|
||||
|
||||
private static DataValue ToDataValue(DateTime t, HistorianValue v, uint status) => new()
|
||||
{
|
||||
WrappedValue = v.Kind switch
|
||||
private static DataValue ToDataValue(DateTime t, HistorianValue v, uint status) => new(
|
||||
v.Kind switch
|
||||
{
|
||||
HistorianValueKind.Empty => Variant.Null,
|
||||
HistorianValueKind.Int64 => new Variant(v.Integer),
|
||||
HistorianValueKind.Boolean => new Variant(v.Number != 0),
|
||||
HistorianValueKind.String => new Variant(v.Text),
|
||||
_ => new Variant(v.Number),
|
||||
HistorianValueKind.Int64 => Variant.From(v.Integer),
|
||||
HistorianValueKind.Boolean => Variant.From(v.Number != 0),
|
||||
HistorianValueKind.String => v.Text is { } text ? Variant.From(text) : Variant.Null,
|
||||
_ => Variant.From(v.Number),
|
||||
},
|
||||
StatusCode = status,
|
||||
SourceTimestamp = t,
|
||||
ServerTimestamp = t,
|
||||
};
|
||||
status, t, t);
|
||||
|
||||
private static NodeId AggregateId(HistoryAggregateType a) => a switch
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user