Files
lmxopcua/tests/ZB.MOM.WW.LmxOpcUa.Tests/Integration/HistorizingFlagTests.cs
Joseph Doherty 415e62c585 Add security classification, alarm detection, historical data access, and primitive grouping
Wire Galaxy security_classification to OPC UA AccessLevel (ReadOnly for SecuredWrite/VerifiedWrite/ViewOnly).
Use deployed package chain for attribute queries to exclude undeployed attributes.
Group primitive attributes under their parent variable node (merged Variable+Object).
Add is_historized and is_alarm detection via HistoryExtension/AlarmExtension primitives.
Implement OPC UA HistoryRead backed by Wonderware Historian Runtime database.
Implement AlarmConditionState nodes driven by InAlarm with condition refresh support.
Add historyread and alarms CLI commands for testing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:32:33 -04:00

75 lines
3.2 KiB
C#

using System.Collections.Generic;
using System.Threading.Tasks;
using Opc.Ua;
using Shouldly;
using Xunit;
using ZB.MOM.WW.LmxOpcUa.Host.Domain;
using ZB.MOM.WW.LmxOpcUa.Tests.Helpers;
namespace ZB.MOM.WW.LmxOpcUa.Tests.Integration
{
public class HistorizingFlagTests
{
private static FakeGalaxyRepository CreateRepo()
{
return new FakeGalaxyRepository
{
Hierarchy = new List<GalaxyObjectInfo>
{
new GalaxyObjectInfo { GobjectId = 1, TagName = "TestObj", BrowseName = "TestObj", ParentGobjectId = 0, IsArea = false }
},
Attributes = new List<GalaxyAttributeInfo>
{
new GalaxyAttributeInfo { GobjectId = 1, TagName = "TestObj", AttributeName = "HistorizedAttr", FullTagReference = "TestObj.HistorizedAttr", MxDataType = 2, IsHistorized = true },
new GalaxyAttributeInfo { GobjectId = 1, TagName = "TestObj", AttributeName = "NormalAttr", FullTagReference = "TestObj.NormalAttr", MxDataType = 5, IsHistorized = false },
new GalaxyAttributeInfo { GobjectId = 1, TagName = "TestObj", AttributeName = "AlarmAttr", FullTagReference = "TestObj.AlarmAttr", MxDataType = 1, IsAlarm = true },
}
};
}
[Fact]
public async Task HistorizedAttribute_HasHistorizingTrue_AndHistoryReadAccess()
{
var fixture = OpcUaServerFixture.WithFakeMxAccessClient(repo: CreateRepo());
await fixture.InitializeAsync();
try
{
using var client = new OpcUaTestClient();
await client.ConnectAsync(fixture.EndpointUrl);
var nodeId = client.MakeNodeId("TestObj.HistorizedAttr");
var historizing = client.ReadAttribute(nodeId, Attributes.Historizing);
((bool)historizing.Value).ShouldBeTrue();
var accessLevel = client.ReadAttribute(nodeId, Attributes.AccessLevel);
var level = (byte)accessLevel.Value;
(level & AccessLevels.HistoryRead).ShouldBe(AccessLevels.HistoryRead,
"HistoryRead bit should be set");
}
finally { await fixture.DisposeAsync(); }
}
[Fact]
public async Task NormalAttribute_HasHistorizingFalse_AndNoHistoryReadAccess()
{
var fixture = OpcUaServerFixture.WithFakeMxAccessClient(repo: CreateRepo());
await fixture.InitializeAsync();
try
{
using var client = new OpcUaTestClient();
await client.ConnectAsync(fixture.EndpointUrl);
var nodeId = client.MakeNodeId("TestObj.NormalAttr");
var historizing = client.ReadAttribute(nodeId, Attributes.Historizing);
((bool)historizing.Value).ShouldBeFalse();
var accessLevel = client.ReadAttribute(nodeId, Attributes.AccessLevel);
var level = (byte)accessLevel.Value;
(level & AccessLevels.HistoryRead).ShouldBe((byte)0,
"HistoryRead bit should not be set");
}
finally { await fixture.DisposeAsync(); }
}
}
}