72 lines
4.0 KiB
C#
72 lines
4.0 KiB
C#
using Opc.Ua;
|
|
using Opc.Ua.Client;
|
|
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests;
|
|
|
|
/// <summary>Preserves baseline alarm discovery through hierarchical folder browses in both application realms.</summary>
|
|
public sealed class Sdk2AlarmBrowseCompatibilityTests
|
|
{
|
|
[Theory]
|
|
[InlineData("OffNormalAlarm", AddressSpaceRealm.Raw)]
|
|
[InlineData("DiscreteAlarm", AddressSpaceRealm.Raw)]
|
|
[InlineData("AlarmCondition", AddressSpaceRealm.Raw)]
|
|
[InlineData("OffNormalAlarm", AddressSpaceRealm.Uns)]
|
|
[InlineData("DiscreteAlarm", AddressSpaceRealm.Uns)]
|
|
[InlineData("AlarmCondition", AddressSpaceRealm.Uns)]
|
|
public async Task Alarm_is_a_hierarchical_component_with_the_same_inverse_parent_and_object_type(
|
|
string type, AddressSpaceRealm realm)
|
|
{
|
|
var ct = TestContext.Current.CancellationToken;
|
|
await using var rig = await ServerEventRig.StartAsync("AlarmBrowseCompatibility", false, ct);
|
|
rig.Enforce(false, [ServerEventRig.Key()]);
|
|
rig.Sink.EnsureFolder("browse-parent", null, "Parent", realm);
|
|
rig.Sink.MaterialiseAlarmCondition("browse-alarm", "browse-parent", "Alarm", type, 500, realm,
|
|
isNative: realm == AddressSpaceRealm.Raw);
|
|
using var session = await rig.ConnectAsync(true, ct);
|
|
var uri = realm == AddressSpaceRealm.Raw ? V3NodeIds.RawNamespaceUri : V3NodeIds.UnsNamespaceUri;
|
|
var ns = session.NamespaceUris.GetIndex(uri);
|
|
ns.ShouldBeGreaterThan(0);
|
|
var parent = new NodeId("browse-parent", (ushort)ns);
|
|
var alarm = new NodeId("browse-alarm", (ushort)ns);
|
|
var expectedType = type switch
|
|
{
|
|
"OffNormalAlarm" => ObjectTypeIds.OffNormalAlarmType,
|
|
"DiscreteAlarm" => ObjectTypeIds.DiscreteAlarmType,
|
|
_ => ObjectTypeIds.AlarmConditionType,
|
|
};
|
|
|
|
var children = await BrowseAsync(session, parent, ReferenceTypeIds.HierarchicalReferences, BrowseDirection.Forward, true, ct);
|
|
var discovered = children.SingleOrDefault(reference => ExpandedNodeId.ToNodeId(reference.NodeId, session.NamespaceUris) == alarm);
|
|
discovered.ShouldNotBeNull("the baseline folder browse exposes every materialised alarm object");
|
|
discovered.ReferenceTypeId.ShouldBe(ReferenceTypeIds.HasComponent);
|
|
discovered.IsForward.ShouldBeTrue();
|
|
discovered.NodeClass.ShouldBe(NodeClass.Object);
|
|
ExpandedNodeId.ToNodeId(discovered.TypeDefinition, session.NamespaceUris).ShouldBe(expectedType);
|
|
|
|
var components = await BrowseAsync(session, parent, ReferenceTypeIds.HasComponent, BrowseDirection.Forward, false, ct);
|
|
components.ShouldContain(reference => ExpandedNodeId.ToNodeId(reference.NodeId, session.NamespaceUris) == alarm);
|
|
var inverse = await BrowseAsync(session, alarm, ReferenceTypeIds.HasComponent, BrowseDirection.Inverse, false, ct);
|
|
inverse.ShouldContain(reference => !reference.IsForward
|
|
&& ExpandedNodeId.ToNodeId(reference.NodeId, session.NamespaceUris) == parent);
|
|
var types = await BrowseAsync(session, alarm, ReferenceTypeIds.HasTypeDefinition, BrowseDirection.Forward, false, ct);
|
|
types.ShouldContain(reference => ExpandedNodeId.ToNodeId(reference.NodeId, session.NamespaceUris) == expectedType);
|
|
await session.CloseAsync(ct);
|
|
}
|
|
|
|
private static async Task<IReadOnlyList<ReferenceDescription>> BrowseAsync(ISession session, NodeId node,
|
|
NodeId reference, BrowseDirection direction, bool subtypes, CancellationToken ct)
|
|
{
|
|
var result = (await session.BrowseAsync(null, null, 0, [new BrowseDescription
|
|
{
|
|
NodeId = node, ReferenceTypeId = reference, BrowseDirection = direction, IncludeSubtypes = subtypes,
|
|
ResultMask = (uint)BrowseResultMask.All,
|
|
}], ct)).Results[0];
|
|
result.StatusCode.ShouldBe(StatusCodes.Good);
|
|
result.ContinuationPoint.IsEmpty.ShouldBeTrue();
|
|
return result.References.ToList();
|
|
}
|
|
}
|