From ce0b2916644be79aea05ef8715aca696deab0ff9 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Thu, 26 Mar 2026 15:33:14 -0400 Subject: [PATCH] Refine XML docs for historian, OPC UA, and tests --- .../Historian/HistorianDataSource.cs | 17 ++++++ .../OpcUa/AddressSpaceDiff.cs | 6 ++ .../OpcUa/LmxNodeManager.cs | 56 ++++++++++++++++++- .../OpcUa/LmxOpcUaServer.cs | 2 + .../OpcUa/OpcUaServerHost.cs | 1 + .../Status/StatusReportService.cs | 1 + .../SecurityClassificationMapperTests.cs | 14 +++++ .../Historian/HistorianQualityMappingTests.cs | 14 +++++ .../Integration/AccessLevelTests.cs | 12 ++++ .../Integration/HistorizingFlagTests.cs | 6 ++ .../Integration/IncrementalSyncTests.cs | 15 +++++ .../OpcUa/AddressSpaceDiffTests.cs | 33 +++++++++++ .../opcuacli-dotnet/Commands/AlarmsCommand.cs | 16 ++++++ .../Commands/HistoryReadCommand.cs | 25 +++++++++ 14 files changed, 215 insertions(+), 3 deletions(-) diff --git a/src/ZB.MOM.WW.LmxOpcUa.Host/Historian/HistorianDataSource.cs b/src/ZB.MOM.WW.LmxOpcUa.Host/Historian/HistorianDataSource.cs index 3591412..5143d41 100644 --- a/src/ZB.MOM.WW.LmxOpcUa.Host/Historian/HistorianDataSource.cs +++ b/src/ZB.MOM.WW.LmxOpcUa.Host/Historian/HistorianDataSource.cs @@ -18,6 +18,10 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Historian private readonly HistorianConfiguration _config; + /// + /// Initializes a Historian reader that translates OPC UA history requests into Wonderware Historian queries. + /// + /// The Historian connection settings and command timeout used for runtime history lookups. public HistorianDataSource(HistorianConfiguration config) { _config = config; @@ -26,6 +30,11 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Historian /// /// Reads raw historical values for a tag from the Historian. /// + /// The Wonderware tag name backing the OPC UA node whose raw history is being requested. + /// The inclusive start of the client-requested history window. + /// The inclusive end of the client-requested history window. + /// The maximum number of samples to return when the OPC UA client limits the result set. + /// The cancellation token that aborts the database call when the OPC UA request is cancelled. public async Task> ReadRawAsync( string tagName, DateTime startTime, DateTime endTime, int maxValues, CancellationToken ct = default) @@ -76,6 +85,12 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Historian /// /// Reads aggregate historical values for a tag from the Historian. /// + /// The Wonderware tag name backing the OPC UA node whose aggregate history is being requested. + /// The inclusive start of the aggregate history window requested by the OPC UA client. + /// The inclusive end of the aggregate history window requested by the OPC UA client. + /// The Wonderware summary resolution, in milliseconds, used to bucket aggregate values. + /// The Historian summary column that matches the OPC UA aggregate function being requested. + /// The cancellation token that aborts the aggregate query when the client request is cancelled. public async Task> ReadAggregateAsync( string tagName, DateTime startTime, DateTime endTime, double intervalMs, string aggregateColumn, @@ -119,6 +134,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Historian /// /// Maps Wonderware Historian quality codes to OPC UA StatusCodes. /// + /// The raw Wonderware Historian quality byte stored with a historical sample. public static StatusCode MapQuality(byte quality) { if (quality == 0) @@ -134,6 +150,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Historian /// Maps an OPC UA aggregate NodeId to the corresponding Historian column name. /// Returns null if the aggregate is not supported. /// + /// The OPC UA aggregate identifier requested by the history client. public static string? MapAggregateToColumn(NodeId aggregateId) { if (aggregateId == ObjectIds.AggregateFunction_Average) diff --git a/src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/AddressSpaceDiff.cs b/src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/AddressSpaceDiff.cs index 68b9054..17307f2 100644 --- a/src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/AddressSpaceDiff.cs +++ b/src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/AddressSpaceDiff.cs @@ -12,6 +12,10 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa /// /// Compares old and new hierarchy+attributes and returns the set of gobject IDs that have any difference. /// + /// The previously published Galaxy object hierarchy snapshot. + /// The previously published Galaxy attribute snapshot keyed to the old hierarchy. + /// The latest Galaxy object hierarchy snapshot pulled from the repository. + /// The latest Galaxy attribute snapshot that should be reflected in the OPC UA namespace. public static HashSet FindChangedGobjectIds( List oldHierarchy, List oldAttributes, List newHierarchy, List newAttributes) @@ -70,6 +74,8 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa /// /// Expands a set of changed gobject IDs to include all descendant gobject IDs in the hierarchy. /// + /// The root Galaxy objects that were detected as changed between snapshots. + /// The hierarchy used to include descendant objects whose OPC UA nodes must also be rebuilt. public static HashSet ExpandToSubtrees(HashSet changed, List hierarchy) { var childrenByParent = hierarchy.GroupBy(h => h.ParentGobjectId) diff --git a/src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/LmxNodeManager.cs b/src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/LmxNodeManager.cs index f22f4ab..d85cd18 100644 --- a/src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/LmxNodeManager.cs +++ b/src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/LmxNodeManager.cs @@ -54,8 +54,19 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa private sealed class TagMetadata { + /// + /// Gets or sets the MXAccess data type code used to map Galaxy values into OPC UA variants. + /// public int MxDataType { get; set; } + + /// + /// Gets or sets a value indicating whether the source Galaxy attribute should be exposed as an array node. + /// public bool IsArray { get; set; } + + /// + /// Gets or sets the declared array length from Galaxy metadata when the attribute is modeled as an array. + /// public int? ArrayDimension { get; set; } } @@ -70,14 +81,49 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa private sealed class AlarmInfo { + /// + /// Gets or sets the full tag reference for the process value whose alarm state is tracked. + /// public string SourceTagReference { get; set; } = ""; + + /// + /// Gets or sets the OPC UA node identifier for the source variable that owns the alarm condition. + /// public NodeId SourceNodeId { get; set; } = NodeId.Null; + + /// + /// Gets or sets the operator-facing source name used in generated alarm events. + /// public string SourceName { get; set; } = ""; + + /// + /// Gets or sets the most recent in-alarm state so duplicate transitions are not reissued. + /// public bool LastInAlarm { get; set; } + + /// + /// Gets or sets the retained OPC UA condition node associated with the source alarm. + /// public AlarmConditionState? ConditionNode { get; set; } + + /// + /// Gets or sets the Galaxy tag reference that supplies runtime alarm priority updates. + /// public string PriorityTagReference { get; set; } = ""; + + /// + /// Gets or sets the Galaxy tag reference or attribute binding used to resolve the alarm message text. + /// public string DescAttrNameTagReference { get; set; } = ""; + + /// + /// Gets or sets the cached OPC UA severity derived from the latest alarm priority value. + /// public ushort CachedSeverity { get; set; } + + /// + /// Gets or sets the cached alarm message used when emitting active and cleared events. + /// public string CachedMessage { get; set; } = ""; } @@ -124,6 +170,8 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa /// The namespace URI that identifies the Galaxy model to clients. /// The runtime client used to service reads, writes, and subscriptions. /// The metrics collector used to track node manager activity. + /// The optional historian adapter used to satisfy OPC UA history read requests. + /// Enables alarm-condition state generation for Galaxy attributes modeled as alarms. public LmxNodeManager( IServerInternal server, ApplicationConfiguration configuration, @@ -444,6 +492,8 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa /// /// Incrementally syncs the address space by detecting changed gobjects and rebuilding only those subtrees. (OPC-010) /// + /// The latest Galaxy object hierarchy snapshot to compare against the currently published model. + /// The latest Galaxy attribute snapshot to compare against the currently published variables. public void SyncAddressSpace(List hierarchy, List attributes) { lock (Lock) @@ -1140,9 +1190,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa #region Condition Refresh - /// - /// Reports all active retained alarm conditions during a condition refresh. - /// + /// + /// The OPC UA request context for the condition refresh operation. + /// The monitored event items that should receive retained alarm conditions. public override ServiceResult ConditionRefresh(OperationContext context, IList monitoredItems) { foreach (var kvp in _alarmInAlarmTags) diff --git a/src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/LmxOpcUaServer.cs b/src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/LmxOpcUaServer.cs index e763836..2575e8f 100644 --- a/src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/LmxOpcUaServer.cs +++ b/src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/LmxOpcUaServer.cs @@ -42,6 +42,8 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa /// The Galaxy name used to construct the namespace URI and product URI. /// The runtime client used by the node manager for live data access. /// The metrics collector shared with the node manager. + /// The optional historian adapter used when clients issue OPC UA history reads. + /// Enables alarm condition tracking for alarm-capable Galaxy attributes. public LmxOpcUaServer(string galaxyName, IMxAccessClient mxAccessClient, PerformanceMetrics metrics, HistorianDataSource? historianDataSource = null, bool alarmTrackingEnabled = false) { diff --git a/src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/OpcUaServerHost.cs b/src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/OpcUaServerHost.cs index 3b1400b..1e630e7 100644 --- a/src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/OpcUaServerHost.cs +++ b/src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/OpcUaServerHost.cs @@ -46,6 +46,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa /// The endpoint and session settings for the OPC UA host. /// The runtime client used by the node manager for live reads, writes, and subscriptions. /// The metrics collector shared with the node manager and runtime bridge. + /// The optional historian adapter that enables OPC UA history read support. public OpcUaServerHost(OpcUaConfiguration config, IMxAccessClient mxAccessClient, PerformanceMetrics metrics, HistorianDataSource? historianDataSource = null) { diff --git a/src/ZB.MOM.WW.LmxOpcUa.Host/Status/StatusReportService.cs b/src/ZB.MOM.WW.LmxOpcUa.Host/Status/StatusReportService.cs index c52941a..cc4219c 100644 --- a/src/ZB.MOM.WW.LmxOpcUa.Host/Status/StatusReportService.cs +++ b/src/ZB.MOM.WW.LmxOpcUa.Host/Status/StatusReportService.cs @@ -40,6 +40,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status /// The performance metrics collector whose operation statistics should be reported. /// The Galaxy repository statistics to surface on the dashboard. /// The OPC UA server host whose active session count should be reported. + /// The node manager whose queue depth and MXAccess event throughput should be surfaced on the dashboard. public void SetComponents(IMxAccessClient? mxAccessClient, PerformanceMetrics? metrics, GalaxyRepositoryStats? galaxyStats, OpcUaServerHost? serverHost, LmxNodeManager? nodeManager = null) diff --git a/tests/ZB.MOM.WW.LmxOpcUa.Tests/Domain/SecurityClassificationMapperTests.cs b/tests/ZB.MOM.WW.LmxOpcUa.Tests/Domain/SecurityClassificationMapperTests.cs index abd2985..e7f7851 100644 --- a/tests/ZB.MOM.WW.LmxOpcUa.Tests/Domain/SecurityClassificationMapperTests.cs +++ b/tests/ZB.MOM.WW.LmxOpcUa.Tests/Domain/SecurityClassificationMapperTests.cs @@ -6,6 +6,11 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Domain { public class SecurityClassificationMapperTests { + /// + /// Verifies that Galaxy classifications intended for operator and engineering writes remain writable through OPC UA. + /// + /// The Galaxy security classification value being evaluated for write access. + /// The expected writable result for the supplied Galaxy classification. [Theory] [InlineData(0, true)] // FreeAccess [InlineData(1, true)] // Operate @@ -16,6 +21,11 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Domain SecurityClassificationMapper.IsWritable(classification).ShouldBe(expected); } + /// + /// Verifies that secured or view-only Galaxy classifications are exposed as read-only attributes. + /// + /// The Galaxy security classification value expected to block writes. + /// The expected writable result for the supplied read-only Galaxy classification. [Theory] [InlineData(2, false)] // SecuredWrite [InlineData(3, false)] // VerifiedWrite @@ -25,6 +35,10 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Domain SecurityClassificationMapper.IsWritable(classification).ShouldBe(expected); } + /// + /// Verifies that unknown security classifications do not accidentally block writes for unmapped Galaxy values. + /// + /// An unmapped Galaxy security classification value that should fall back to writable behavior. [Theory] [InlineData(-1)] [InlineData(7)] diff --git a/tests/ZB.MOM.WW.LmxOpcUa.Tests/Historian/HistorianQualityMappingTests.cs b/tests/ZB.MOM.WW.LmxOpcUa.Tests/Historian/HistorianQualityMappingTests.cs index 9f28ee8..a178405 100644 --- a/tests/ZB.MOM.WW.LmxOpcUa.Tests/Historian/HistorianQualityMappingTests.cs +++ b/tests/ZB.MOM.WW.LmxOpcUa.Tests/Historian/HistorianQualityMappingTests.cs @@ -7,18 +7,28 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Historian { public class HistorianQualityMappingTests { + /// + /// Verifies that the Historian good-quality sentinel is surfaced to OPC UA clients as a good status code. + /// [Fact] public void Quality0_MapsToGood() { HistorianDataSource.MapQuality(0).ShouldBe(StatusCodes.Good); } + /// + /// Verifies that the Historian bad-quality sentinel is surfaced to OPC UA clients as a bad status code. + /// [Fact] public void Quality1_MapsToBad() { HistorianDataSource.MapQuality(1).ShouldBe(StatusCodes.Bad); } + /// + /// Verifies that Historian uncertainty quality bands are translated into OPC UA uncertain results. + /// + /// A Wonderware Historian quality byte in the uncertain range. [Theory] [InlineData(128)] [InlineData(133)] @@ -28,6 +38,10 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Historian HistorianDataSource.MapQuality(quality).ShouldBe(StatusCodes.Uncertain); } + /// + /// Verifies that nonzero non-uncertain Historian quality values are treated as bad historical samples. + /// + /// A Wonderware Historian quality byte that should map to an OPC UA bad status. [Theory] [InlineData(2)] [InlineData(50)] diff --git a/tests/ZB.MOM.WW.LmxOpcUa.Tests/Integration/AccessLevelTests.cs b/tests/ZB.MOM.WW.LmxOpcUa.Tests/Integration/AccessLevelTests.cs index e4c4418..e007dc2 100644 --- a/tests/ZB.MOM.WW.LmxOpcUa.Tests/Integration/AccessLevelTests.cs +++ b/tests/ZB.MOM.WW.LmxOpcUa.Tests/Integration/AccessLevelTests.cs @@ -31,6 +31,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Integration }; } + /// + /// Verifies that writable Galaxy security classifications publish OPC UA variables with read-write access. + /// [Fact] public async Task ReadWriteAttribute_HasCurrentReadOrWrite_AccessLevel() { @@ -52,6 +55,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Integration finally { await fixture.DisposeAsync(); } } + /// + /// Verifies that secured and view-only Galaxy classifications publish OPC UA variables with read-only access. + /// [Fact] public async Task ReadOnlyAttribute_HasCurrentRead_AccessLevel() { @@ -73,6 +79,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Integration finally { await fixture.DisposeAsync(); } } + /// + /// Verifies that the bridge rejects writes against Galaxy attributes whose security classification is read-only. + /// [Fact] public async Task Write_ToReadOnlyAttribute_IsRejected() { @@ -90,6 +99,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Integration finally { await fixture.DisposeAsync(); } } + /// + /// Verifies that writes succeed for Galaxy attributes whose security classification permits operator updates. + /// [Fact] public async Task Write_ToReadWriteAttribute_Succeeds() { diff --git a/tests/ZB.MOM.WW.LmxOpcUa.Tests/Integration/HistorizingFlagTests.cs b/tests/ZB.MOM.WW.LmxOpcUa.Tests/Integration/HistorizingFlagTests.cs index 74e8c5a..5e1cf2b 100644 --- a/tests/ZB.MOM.WW.LmxOpcUa.Tests/Integration/HistorizingFlagTests.cs +++ b/tests/ZB.MOM.WW.LmxOpcUa.Tests/Integration/HistorizingFlagTests.cs @@ -27,6 +27,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Integration }; } + /// + /// Verifies that historized Galaxy attributes advertise OPC UA historizing support and history-read access. + /// [Fact] public async Task HistorizedAttribute_HasHistorizingTrue_AndHistoryReadAccess() { @@ -49,6 +52,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Integration finally { await fixture.DisposeAsync(); } } + /// + /// Verifies that non-historized Galaxy attributes do not claim OPC UA history support. + /// [Fact] public async Task NormalAttribute_HasHistorizingFalse_AndNoHistoryReadAccess() { diff --git a/tests/ZB.MOM.WW.LmxOpcUa.Tests/Integration/IncrementalSyncTests.cs b/tests/ZB.MOM.WW.LmxOpcUa.Tests/Integration/IncrementalSyncTests.cs index 3ff51d9..9df2dc0 100644 --- a/tests/ZB.MOM.WW.LmxOpcUa.Tests/Integration/IncrementalSyncTests.cs +++ b/tests/ZB.MOM.WW.LmxOpcUa.Tests/Integration/IncrementalSyncTests.cs @@ -11,6 +11,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Integration { public class IncrementalSyncTests { + /// + /// Verifies that adding a new Galaxy object and attribute causes the corresponding OPC UA node subtree to appear after sync. + /// [Fact] public async Task Sync_AddObject_NewNodeAppears() { @@ -56,6 +59,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Integration finally { await fixture.DisposeAsync(); } } + /// + /// Verifies that removing a Galaxy object tears down the corresponding OPC UA subtree without affecting siblings. + /// [Fact] public async Task Sync_RemoveObject_NodeDisappears() { @@ -87,6 +93,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Integration finally { await fixture.DisposeAsync(); } } + /// + /// Verifies that adding a Galaxy attribute creates a new OPC UA variable during incremental rebuild. + /// [Fact] public async Task Sync_AddAttribute_NewVariableAppears() { @@ -114,6 +123,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Integration finally { await fixture.DisposeAsync(); } } + /// + /// Verifies that subscriptions on unchanged objects continue receiving data after unrelated subtree rebuilds. + /// [Fact] public async Task Sync_UnchangedObject_SubscriptionSurvives() { @@ -148,6 +160,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Integration finally { await fixture.DisposeAsync(); } } + /// + /// Verifies that a rebuild request with no repository changes leaves the published namespace intact. + /// [Fact] public async Task Sync_NoChanges_NothingHappens() { diff --git a/tests/ZB.MOM.WW.LmxOpcUa.Tests/OpcUa/AddressSpaceDiffTests.cs b/tests/ZB.MOM.WW.LmxOpcUa.Tests/OpcUa/AddressSpaceDiffTests.cs index e6fb98a..a8e24b3 100644 --- a/tests/ZB.MOM.WW.LmxOpcUa.Tests/OpcUa/AddressSpaceDiffTests.cs +++ b/tests/ZB.MOM.WW.LmxOpcUa.Tests/OpcUa/AddressSpaceDiffTests.cs @@ -14,6 +14,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.OpcUa private static GalaxyAttributeInfo Attr(int gobjectId, string name, string tagName = "Obj", int mxDataType = 5) => new GalaxyAttributeInfo { GobjectId = gobjectId, AttributeName = name, FullTagReference = $"{tagName}.{name}", MxDataType = mxDataType, TagName = tagName }; + /// + /// Verifies that identical Galaxy hierarchy and attribute snapshots produce no incremental rebuild work. + /// [Fact] public void NoChanges_ReturnsEmptySet() { @@ -24,6 +27,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.OpcUa changed.ShouldBeEmpty(); } + /// + /// Verifies that newly deployed Galaxy objects are flagged for OPC UA subtree creation. + /// [Fact] public void AddedObject_Detected() { @@ -36,6 +42,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.OpcUa changed.ShouldNotContain(1); } + /// + /// Verifies that removed Galaxy objects are flagged so their OPC UA subtree can be torn down. + /// [Fact] public void RemovedObject_Detected() { @@ -48,6 +57,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.OpcUa changed.ShouldNotContain(1); } + /// + /// Verifies that browse-name changes are treated as address-space changes for the affected Galaxy object. + /// [Fact] public void ModifiedObject_BrowseNameChange_Detected() { @@ -59,6 +71,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.OpcUa changed.ShouldContain(1); } + /// + /// Verifies that parent changes are treated as subtree moves that require rebuilding the affected object. + /// [Fact] public void ModifiedObject_ParentChange_Detected() { @@ -70,6 +85,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.OpcUa changed.ShouldContain(2); } + /// + /// Verifies that adding a Galaxy attribute marks the owning object for OPC UA variable rebuild. + /// [Fact] public void AttributeAdded_Detected() { @@ -81,6 +99,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.OpcUa changed.ShouldContain(1); } + /// + /// Verifies that removing a Galaxy attribute marks the owning object for OPC UA variable rebuild. + /// [Fact] public void AttributeRemoved_Detected() { @@ -92,6 +113,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.OpcUa changed.ShouldContain(1); } + /// + /// Verifies that changes to attribute field metadata such as MX data type trigger rebuild of the owning object. + /// [Fact] public void AttributeFieldChange_Detected() { @@ -103,6 +127,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.OpcUa changed.ShouldContain(1); } + /// + /// Verifies that security-classification changes are treated as address-space changes for the owning attribute. + /// [Fact] public void AttributeSecurityChange_Detected() { @@ -114,6 +141,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.OpcUa changed.ShouldContain(1); } + /// + /// Verifies that subtree expansion includes all descendants of a changed Galaxy object. + /// [Fact] public void ExpandToSubtrees_IncludesChildren() { @@ -136,6 +166,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.OpcUa expanded.ShouldNotContain(5); } + /// + /// Verifies that subtree expansion does not introduce unrelated nodes when the changed object is already a leaf. + /// [Fact] public void ExpandToSubtrees_LeafNode_NoExpansion() { diff --git a/tools/opcuacli-dotnet/Commands/AlarmsCommand.cs b/tools/opcuacli-dotnet/Commands/AlarmsCommand.cs index 0b36558..25b37c4 100644 --- a/tools/opcuacli-dotnet/Commands/AlarmsCommand.cs +++ b/tools/opcuacli-dotnet/Commands/AlarmsCommand.cs @@ -9,18 +9,34 @@ namespace OpcUaCli.Commands; [Command("alarms", Description = "Subscribe to alarm events on a node")] public class AlarmsCommand : ICommand { + /// + /// Gets the OPC UA endpoint URL for the server whose alarm stream should be monitored. + /// [CommandOption("url", 'u', Description = "OPC UA server endpoint URL", IsRequired = true)] public string Url { get; init; } = default!; + /// + /// Gets the node to subscribe to for event notifications, typically a source object or the server node. + /// [CommandOption("node", 'n', Description = "Node ID to monitor for events (default: Server node)")] public string? NodeId { get; init; } + /// + /// Gets the requested publishing and sampling interval for the alarm subscription. + /// [CommandOption("interval", 'i', Description = "Publishing interval in milliseconds")] public int Interval { get; init; } = 1000; + /// + /// Gets a value indicating whether the command should request a retained-condition refresh after subscribing. + /// [CommandOption("refresh", Description = "Request a ConditionRefresh after subscribing")] public bool Refresh { get; init; } + /// + /// Connects to the target server and streams alarm or condition events to the operator console. + /// + /// The CLI console used for cancellation and alarm-event output. public async ValueTask ExecuteAsync(IConsole console) { using var session = await OpcUaHelper.ConnectAsync(Url); diff --git a/tools/opcuacli-dotnet/Commands/HistoryReadCommand.cs b/tools/opcuacli-dotnet/Commands/HistoryReadCommand.cs index 64e3d12..343e199 100644 --- a/tools/opcuacli-dotnet/Commands/HistoryReadCommand.cs +++ b/tools/opcuacli-dotnet/Commands/HistoryReadCommand.cs @@ -9,27 +9,52 @@ namespace OpcUaCli.Commands; [Command("historyread", Description = "Read historical data from a node")] public class HistoryReadCommand : ICommand { + /// + /// Gets the OPC UA endpoint URL for the server that exposes the historized node. + /// [CommandOption("url", 'u', Description = "OPC UA server endpoint URL", IsRequired = true)] public string Url { get; init; } = default!; + /// + /// Gets the node identifier for the historized variable to query. + /// [CommandOption("node", 'n', Description = "Node ID (e.g. ns=1;s=TestMachine_001.TestHistoryValue)", IsRequired = true)] public string NodeId { get; init; } = default!; + /// + /// Gets the requested history start time string supplied by the operator. + /// [CommandOption("start", Description = "Start time (ISO 8601 or date string, default: 24 hours ago)")] public string? StartTime { get; init; } + /// + /// Gets the requested history end time string supplied by the operator. + /// [CommandOption("end", Description = "End time (ISO 8601 or date string, default: now)")] public string? EndTime { get; init; } + /// + /// Gets the maximum number of raw history values that should be returned to the console. + /// [CommandOption("max", Description = "Maximum number of values to return")] public int MaxValues { get; init; } = 1000; + /// + /// Gets the optional aggregate name to request when the operator wants processed history instead of raw values. + /// [CommandOption("aggregate", Description = "Aggregate function: Average, Minimum, Maximum, Count")] public string? Aggregate { get; init; } + /// + /// Gets the aggregate processing interval, in milliseconds, for processed history reads. + /// [CommandOption("interval", Description = "Processing interval in milliseconds for aggregates")] public double IntervalMs { get; init; } = 3600000; + /// + /// Connects to the target server and prints raw or aggregate historical data for the requested node. + /// + /// The CLI console used for output, errors, and cancellation handling. public async ValueTask ExecuteAsync(IConsole console) { using var session = await OpcUaHelper.ConnectAsync(Url);