From bd6c0b4d3d024795a243fc9dc62a1992acb0a0c5 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Wed, 3 Jun 2026 12:34:34 -0400 Subject: [PATCH] docs: complete XML doc comments via fixdocs (2757 to 131 findings) Add missing /// tags and clean up misused inheritdoc across 481 files so the documented API surface is complete. Documentation-only (zero code lines changed). The 131 remaining findings are inheritdoc-style warnings deliberately left to preserve hand-written implementation rationale (plan-decision notes, race-condition explanations). --- .../CommandBase.cs | 3 + .../DefaultApplicationConfigurationFactory.cs | 4 +- .../Adapters/DefaultEndpointDiscovery.cs | 6 +- .../IApplicationConfigurationFactory.cs | 1 + .../Adapters/IEndpointDiscovery.cs | 1 + .../Adapters/ISessionAdapter.cs | 10 ++ .../Adapters/ISubscriptionAdapter.cs | 3 + .../ClientStoragePaths.cs | 3 + .../IOpcUaClientService.cs | 13 +++ .../OpcUaClientService.cs | 6 +- .../Services/AvaloniaUiDispatcher.cs | 3 +- .../Services/ISettingsService.cs | 1 + .../Services/JsonSettingsService.cs | 6 +- .../Services/SynchronousUiDispatcher.cs | 3 +- .../ViewModels/AlarmsViewModel.cs | 1 + .../ViewModels/BrowseTreeViewModel.cs | 1 + .../ViewModels/SubscriptionsViewModel.cs | 5 + .../ClusterRoleInfo.cs | 16 +--- .../ZB.MOM.WW.OtOpcUa.Cluster/RoleParser.cs | 1 + .../ServiceCollectionExtensions.cs | 2 + .../Browsing/IBrowseSession.cs | 8 ++ .../Browsing/IDriverBrowser.cs | 1 + .../Engines/IAlarmActorStateStore.cs | 9 +- .../Engines/IVirtualTagEvaluator.cs | 6 +- .../Interfaces/IAdminOperationsClient.cs | 1 + .../Interfaces/IFleetDiagnosticsClient.cs | 1 + .../Observability/OtOpcUaTelemetry.cs | 2 + .../OpcUa/DeferredAddressSpaceSink.cs | 25 +---- .../OpcUa/DeferredServiceLevelPublisher.cs | 3 +- .../Types/CorrelationId.cs | 3 + .../LocalCache/ILocalConfigCache.cs | 2 + .../LocalCache/LiteDbConfigCache.cs | 13 +-- .../Services/ILdapGroupRoleMappingService.cs | 4 + .../Services/LdapGroupRoleMappingService.cs | 5 +- .../Validation/DraftValidator.cs | 3 + .../DriverTypeRegistry.cs | 3 + .../Historian/IHistorianDataSource.cs | 5 + .../IAddressSpaceBuilder.cs | 3 + .../IAlarmSource.cs | 3 + .../IDriver.cs | 6 ++ .../IDriverFactory.cs | 8 +- .../IDriverHealthPublisher.cs | 4 + .../IDriverProbe.cs | 4 + .../IDriverSupervisor.cs | 1 + .../IHistoryProvider.cs | 1 + .../IHostConnectivityProbe.cs | 1 + .../ITagDiscovery.cs | 1 + .../IWritable.cs | 1 + .../PollGroupEngine.cs | 5 +- .../IAlarmHistorianSink.cs | 3 + .../SqliteStoreAndForwardSink.cs | 5 +- .../ScriptedAlarmEngine.cs | 13 +++ .../DependencyExtractor.cs | 1 + .../ScriptContext.cs | 2 + .../ScriptEvaluator.cs | 2 + .../ScriptSandbox.cs | 1 + .../DependencyGraph.cs | 2 + .../ITagUpstreamSource.cs | 2 + .../VirtualTagEngine.cs | 4 + .../Authorization/AuthorizationDecision.cs | 1 + .../Authorization/IPermissionEvaluator.cs | 1 + .../Authorization/PermissionTrie.cs | 1 + .../Authorization/PermissionTrieBuilder.cs | 1 + .../Authorization/PermissionTrieCache.cs | 5 + .../Authorization/TriePermissionEvaluator.cs | 6 +- .../Authorization/UserAuthorizationState.cs | 2 + .../Hosting/DriverFactoryRegistry.cs | 2 + .../Hosting/DriverFactoryRegistryAdapter.cs | 7 +- .../Hosting/DriverHost.cs | 5 + .../Observability/DriverHealthReport.cs | 2 + .../Observability/LogContextEnricher.cs | 2 + .../OpcUa/EquipmentNodeWalker.cs | 1 + .../OpcUa/GenericDriverNodeManager.cs | 20 ++-- .../Resilience/AlarmSurfaceInvoker.cs | 5 +- .../Resilience/CapabilityInvoker.cs | 3 + .../DriverResilienceOptionsParser.cs | 1 + .../DriverResiliencePipelineBuilder.cs | 1 + .../DriverResilienceStatusTracker.cs | 2 + .../Stability/MemoryTracking.cs | 2 + .../AbCipCommandBase.cs | 1 + .../Commands/ReadCommand.cs | 1 + .../Commands/ReadCommand.cs | 1 + .../SnapshotFormatter.cs | 3 + .../Commands/ReadCommand.cs | 1 + .../FocasCommandBase.cs | 1 + .../ModbusCommandBase.cs | 1 + .../Commands/ReadCommand.cs | 1 + .../S7CommandBase.cs | 1 + .../Commands/BrowseCommand.cs | 3 + .../Commands/WriteCommand.cs | 1 + .../TwinCATTagCommandBase.cs | 2 + .../AbCipDriver.cs | 40 ++------ .../AbCipStatusMapper.cs | 3 + .../AbCipSystemTagFilter.cs | 1 + .../AbCipTagPath.cs | 1 + .../AbCipTemplateCache.cs | 1 + .../AbCipUdtMemberLayout.cs | 1 + .../AbCipUdtReadPlanner.cs | 1 + .../IAbCipTagEnumerator.cs | 4 + .../IAbCipTagRuntime.cs | 7 ++ .../IAbCipTemplateReader.cs | 2 + .../LibplctagTagEnumerator.cs | 8 +- .../LibplctagTagRuntime.cs | 35 ++----- .../LibplctagTemplateReader.cs | 11 +-- .../PlcFamilies/AbCipPlcFamilyProfile.cs | 1 + .../AbLegacyPlcFamilyProfile.cs | 1 + .../AbLegacyAddress.cs | 2 + .../IAbLegacyTagRuntime.cs | 6 ++ .../LibplctagLegacyTagRuntime.cs | 6 +- .../FocasDriver.cs | 74 ++++---------- .../IFocasClient.cs | 18 +++- .../Wire/FocasWireClient.cs | 27 ++++++ .../Wire/WireFocasClient.cs | 69 ++++--------- .../GalaxyBrowseSession.cs | 13 ++- .../GalaxyDriverBrowser.cs | 3 +- .../Browse/AlarmRefBuilder.cs | 1 + .../Browse/DataTypeMap.cs | 1 + .../Browse/DeployWatcher.cs | 1 + .../Browse/GalaxyDiscoverer.cs | 1 + .../Browse/GatewayGalaxyDeployWatchSource.cs | 5 +- .../Browse/GatewayGalaxyHierarchySource.cs | 1 + .../Browse/IGalaxyDeployWatchSource.cs | 1 + .../Browse/IGalaxyHierarchySource.cs | 1 + .../GalaxyDriver.cs | 20 ++-- .../GalaxyDriverFactoryExtensions.cs | 2 + .../Health/HostStatusAggregator.cs | 1 + .../Health/PerPlatformProbeWatcher.cs | 1 + .../Runtime/GalaxyMxSession.cs | 2 + .../Runtime/GalaxySubscriptionHandle.cs | 2 +- .../Runtime/GatewayGalaxyAlarmAcknowledger.cs | 1 + .../Runtime/GatewayGalaxyAlarmFeed.cs | 2 + .../Runtime/GatewayGalaxySubscriber.cs | 15 +-- .../Runtime/IGalaxyAlarmAcknowledger.cs | 1 + .../Runtime/IGalaxyDataWriter.cs | 1 + .../Runtime/IGalaxySubscriber.cs | 3 + .../Runtime/MxAccessSeverityMapper.cs | 1 + .../Runtime/MxValueDecoder.cs | 1 + .../Runtime/ReconnectSupervisor.cs | 1 + .../Runtime/SubscriptionRegistry.cs | 3 + .../Runtime/TracedGalaxyDataWriter.cs | 1 + .../Runtime/TracedGalaxySubscriber.cs | 10 +- .../Ipc/FrameWriter.cs | 1 + .../WonderwareHistorianClient.cs | 32 +------ .../AahClientManagedAlarmEventWriter.cs | 2 + .../Backend/HistorianClusterEndpointPicker.cs | 2 + .../Backend/HistorianDataSource.cs | 36 +++---- .../Backend/IAlarmHistorianWriteBackend.cs | 1 + .../Backend/SdkAlarmHistorianWriteBackend.cs | 7 +- .../Ipc/FrameReader.cs | 3 +- .../Ipc/FrameWriter.cs | 3 +- .../Ipc/HistorianFrameHandler.cs | 7 +- .../Ipc/PipeServer.cs | 6 +- .../Program.cs | 1 + .../DirectLogicAddress.cs | 11 +++ .../MelsecAddress.cs | 4 + .../ModbusModiconAddress.cs | 1 + .../ModbusDriver.cs | 55 +++++------ .../ModbusDriverFactoryExtensions.cs | 2 + .../ModbusTcpTransport.cs | 10 +- .../OpcUaClientBrowseSession.cs | 22 ++--- .../OpcUaClientDriverBrowser.cs | 7 +- .../NamespaceMap.cs | 6 ++ .../OpcUaClientDriver.cs | 89 +++++++---------- .../S7CpuTypeMap.cs | 3 + .../ZB.MOM.WW.OtOpcUa.Driver.S7/S7Driver.cs | 57 +++-------- .../ITwinCATClient.cs | 7 ++ .../TwinCATDriver.cs | 51 +++------- .../TwinCATDriverFactoryExtensions.cs | 3 + .../TwinCATSystemSymbolFilter.cs | 1 + .../Browsing/BrowseSessionReaper.cs | 2 + .../Browsing/BrowseSessionRegistry.cs | 8 ++ .../Browsing/IBrowserSessionService.cs | 17 ++++ .../Clients/AdminOperationsClient.cs | 9 +- .../Clients/AdminProbeService.cs | 1 + .../Clients/FleetDiagnosticsClient.cs | 5 +- .../Clients/ServiceCollectionExtensions.cs | 1 + .../Drivers/Pickers/AbLegacyAddressBuilder.cs | 5 + .../Drivers/Pickers/FocasAddressBuilder.cs | 4 + .../HistorianWonderwareAddressBuilder.cs | 5 + .../Drivers/Pickers/ModbusAddressBuilder.cs | 5 + .../Drivers/Pickers/S7AddressBuilder.cs | 2 + .../Shared/Drivers/ResilienceFormModel.cs | 19 ++++ .../EndpointRouteBuilderExtensions.cs | 2 + .../Hubs/AlertSignalRBridge.cs | 1 + .../Hubs/DriverStatusHub.cs | 4 + .../Hubs/DriverStatusSignalRBridge.cs | 1 + .../Hubs/HubRouteBuilderExtensions.cs | 1 + .../Hubs/HubServiceCollectionExtensions.cs | 2 + .../Hubs/IDriverStatusSnapshotStore.cs | 7 ++ .../Hubs/IInProcessBroadcaster.cs | 3 +- .../Hubs/InMemoryDriverStatusSnapshotStore.cs | 2 +- .../Hubs/ScriptLogSignalRBridge.cs | 1 + .../AdminOperations/ConfigComposer.cs | 2 + .../Audit/AuditWriterActor.cs | 1 + .../Coordinators/ConfigPublishCoordinator.cs | 1 + .../Drivers/DriverFactoryBootstrap.cs | 2 + .../Health/HealthEndpoints.cs | 3 + .../Observability/ObservabilityExtensions.cs | 2 + .../OpcUa/LdapOpcUaUserAuthenticator.cs | 1 + .../OpcUa/OtOpcUaServerHostedService.cs | 3 + .../OpcUaApplicationHost.cs | 4 + .../Phase7Plan.cs | 1 + .../SdkAddressSpaceSink.cs | 25 +---- .../SdkServiceLevelPublisher.cs | 3 +- .../Drivers/DeploymentArtifact.cs | 2 + .../Drivers/DriverHostActor.cs | 5 +- .../Drivers/DriverInstanceActor.cs | 2 + .../Drivers/DriverSpawnPlan.cs | 1 + .../Health/DbHealthProbeActor.cs | 1 + .../OpcUa/OpcUaPublishActor.cs | 2 + .../ScriptedAlarms/EfAlarmActorStateStore.cs | 9 +- .../ServiceCollectionExtensions.cs | 2 + .../VirtualTags/VirtualTagActor.cs | 1 + .../CookieAuthenticationStateProvider.cs | 3 +- .../Jwt/JwtTokenService.cs | 1 + .../Ldap/LdapOptions.cs | 1 + .../Ldap/OtOpcUaGroupRoleMapper.cs | 5 +- .../Ldap/RoleMapper.cs | 1 + .../ServiceCollectionExtensions.cs | 1 + .../AlarmsCommandTests.cs | 6 ++ .../BrowseCommandTests.cs | 6 ++ .../CommandBaseTests.cs | 4 + .../CommandRangeValidationTests.cs | 9 ++ .../ConnectCommandTests.cs | 3 + .../EventHandlerLifecycleTests.cs | 2 + .../Fakes/FakeOpcUaClientService.cs | 6 +- .../Fakes/FakeOpcUaClientServiceFactory.cs | 1 + .../HistoryReadCommandTests.cs | 6 ++ .../InputValidationErrorsTests.cs | 5 + .../LoggerLifecycleTests.cs | 1 + .../ReadCommandTests.cs | 4 + .../RedundancyCommandTests.cs | 4 + .../SubscribeCommandSummaryTests.cs | 8 ++ .../SubscribeCommandTests.cs | 4 + .../TestConsoleHelper.cs | 1 + .../WriteCommandTests.cs | 4 + .../Fakes/FakeEndpointDiscovery.cs | 6 +- .../OpcUaClientServiceTests.cs | 63 ++++++++++++ .../AlarmsViewModelTests.cs | 4 + .../BrowseTreeViewModelTests.cs | 7 ++ .../Fakes/FakeOpcUaClientService.cs | 6 +- .../Fakes/FakeSettingsService.cs | 5 +- .../HistoryViewModelTests.cs | 5 + .../MainWindowViewModelTests.cs | 27 ++++++ .../ReadWriteViewModelTests.cs | 3 + .../SubscriptionsViewModelTests.cs | 18 ++++ .../ClusterAuditQueryTests.cs | 3 + .../DriverHostStatusTests.cs | 3 + .../GenerationSealedCacheTests.cs | 10 ++ .../LdapGroupRoleMappingServiceTests.cs | 10 ++ .../LiteDbConfigCacheTests.cs | 6 ++ .../ResilientConfigReaderTests.cs | 8 ++ .../PollGroupEngineTests.cs | 18 +++- .../SqliteStoreAndForwardSinkTests.cs | 39 +++++--- .../FakeUpstream.cs | 7 +- .../ScriptedAlarmEngineTests.cs | 59 +++++++----- .../ScriptedAlarmSourceTests.cs | 5 + .../CompiledScriptCacheTests.cs | 1 + .../ScriptSandboxTests.cs | 7 ++ .../TimedScriptEvaluatorTests.cs | 6 ++ .../DriverHostTests.cs | 53 +++++----- .../GenericDriverNodeManagerTests.cs | 96 +++++++------------ .../CapabilityInvokerEnrichmentTests.cs | 2 + .../OpcUa/EquipmentNodeWalkerTests.cs | 19 +--- .../OpcUa/IdentificationFolderBuilderTests.cs | 14 +-- .../Resilience/AlarmSurfaceInvokerTests.cs | 28 +++--- .../Resilience/CapabilityInvokerTests.cs | 8 ++ .../DriverResiliencePipelineBuilderTests.cs | 10 ++ .../FlakeyDriverIntegrationTests.cs | 14 ++- .../Resilience/InFlightCounterTests.cs | 3 + .../PerCallHostResolverDispatchTests.cs | 6 +- .../Stability/MemoryRecycleTests.cs | 11 ++- .../ScheduledRecycleSchedulerTests.cs | 11 ++- .../FakeUpstream.cs | 7 +- .../TimerTriggerSchedulerTests.cs | 3 + .../VirtualTagEngineTests.cs | 29 +++++- .../VirtualTagSourceTests.cs | 6 ++ .../CommandCancellationTests.cs | 3 + .../ModbusCommandBaseTests.cs | 1 + .../WriteCommandRegionValidationTests.cs | 2 + .../AbCipReadSmokeTests.cs | 1 + .../AbServerFixture.cs | 7 +- .../Emulate/AbCipEmulateAlmdTests.cs | 1 + .../Emulate/AbCipEmulateUdtReadTests.cs | 1 + .../AbCipAlarmProjectionTests.cs | 4 + .../AbCipBoolInDIntRmwTests.cs | 5 + .../AbCipDriverCodeReviewRegressionTests.cs | 10 ++ .../AbCipDriverDiscoveryTests.cs | 37 ++++--- .../AbCipDriverReadTests.cs | 12 +++ .../AbCipDriverTests.cs | 5 + .../AbCipDriverWholeUdtReadTests.cs | 5 + .../AbCipDriverWriteTests.cs | 10 ++ .../AbCipFetchUdtShapeTests.cs | 21 ++-- .../AbCipHostProbeTests.cs | 10 ++ .../AbCipLoggingTests.cs | 7 ++ .../AbCipPerDeviceConnectionOptionsTests.cs | 5 + .../AbCipPlcFamilyTests.cs | 28 +++--- .../AbCipSubscriptionTests.cs | 7 ++ .../AbCipUdtMemberTests.cs | 29 +++--- .../FakeAbCipTag.cs | 28 ++---- .../AbLegacyReadSmokeTests.cs | 2 + .../AbLegacyServerFixture.cs | 4 + .../AbLegacyBitIndexRangeTests.cs | 2 + .../AbLegacyBitRmwTests.cs | 4 + .../AbLegacyCapabilityTests.cs | 29 +++--- .../AbLegacyDisposeAndResolveHostTests.cs | 3 + .../AbLegacyDriverTests.cs | 8 ++ .../AbLegacyLoggerInjectionTests.cs | 21 +++- .../AbLegacyReadWriteTests.cs | 14 +++ .../AbLegacyRuntimeConcurrencyTests.cs | 10 +- .../FakeAbLegacyTag.cs | 29 ++---- .../FocasSimFixture.cs | 6 ++ .../Series/WireBackendCoverageTests.cs | 27 +++--- .../Series/WireBackendTests.cs | 4 + .../FakeFocasClient.cs | 55 ++++------- .../FocasAlarmProjectionTests.cs | 3 + .../FocasCapabilityTests.cs | 34 +++---- .../FocasDriverMediumFindingsTests.cs | 29 +++--- .../FocasFactoryConfigTests.cs | 2 + .../FocasHandleRecycleTests.cs | 2 + .../FocasLoggingTests.cs | 3 + .../FocasLowFindingsTests.cs | 3 + .../FocasPmcBitRmwTests.cs | 4 + .../FocasReadWriteTests.cs | 15 +++ .../FocasScaffoldingTests.cs | 3 + .../GalaxyBrowseSessionTests.cs | 5 + .../GalaxyDriverBrowserTests.cs | 3 + .../Browse/DeployWatcherTests.cs | 7 ++ .../Browse/GalaxyDiscovererTests.cs | 55 ++++------- ...alaxyDriverAlarmEventArgsExtensionTests.cs | 7 +- .../GalaxyDriverAlarmSourceTests.cs | 17 ++-- .../GalaxyDriverApiKeyResolverTests.cs | 17 +++- .../GalaxyDriverFactoryTests.cs | 4 + .../GalaxyDriverInfrastructureTests.cs | 32 +++---- .../Health/PerPlatformProbeWatcherTests.cs | 21 ++-- .../Runtime/EventPumpBoundedChannelTests.cs | 18 ++-- .../Runtime/EventPumpStreamFaultTests.cs | 29 +++--- .../Runtime/GalaxyDriverReadTests.cs | 7 ++ .../Runtime/GalaxyDriverSubscribeTests.cs | 26 ++--- .../Runtime/GalaxyDriverWriteTests.cs | 36 +++---- .../Runtime/GalaxyTelemetryTests.cs | 8 +- .../GatewayGalaxyAlarmFeedLiveTests.cs | 2 + .../Runtime/GatewayGalaxyAlarmFeedTests.cs | 3 + .../Runtime/ReconnectSupervisorTests.cs | 7 ++ .../Runtime/SubscriptionRegistryTests.cs | 1 + .../FakeSidecarServer.cs | 2 + .../WonderwareHistorianClientTests.cs | 18 ++++ .../AahClientManagedAlarmEventWriterTests.cs | 14 ++- ...HistorianDataSourceConnectFailoverTests.cs | 6 ++ .../HistorianDataSourceRequestTimeoutTests.cs | 1 + .../SdkAlarmHistorianWriteBackendTests.cs | 11 ++- .../Ipc/PipeRoundTripTests.cs | 13 ++- .../Ipc/PipeServerSidRejectTests.cs | 1 + .../AddressingGrammarTests.cs | 4 + .../DL205/DL205BcdQuirkTests.cs | 1 + .../DL205/DL205CoilMappingTests.cs | 3 + .../DL205/DL205ExceptionCodeTests.cs | 1 + .../DL205/DL205FloatCdabQuirkTests.cs | 1 + .../DL205/DL205SmokeTests.cs | 1 + .../DL205/DL205StringQuirkTests.cs | 1 + .../DL205/DL205VMemoryQuirkTests.cs | 2 + .../DL205/DL205XInputTests.cs | 1 + .../ExceptionInjectionTests.cs | 3 + .../Mitsubishi/MitsubishiProfile.cs | 1 + .../Mitsubishi/MitsubishiQuirkTests.cs | 6 ++ .../Mitsubishi/MitsubishiSmokeTests.cs | 1 + .../ModbusSimulatorFixture.cs | 3 +- .../S7/S7_1500SmokeTests.cs | 1 + .../S7/S7_ByteOrderTests.cs | 3 + .../MelsecAddressTests.cs | 14 +++ .../ModbusArrayTests.cs | 30 +++--- .../ModbusBitRmwTests.cs | 15 ++- .../ModbusCapTests.cs | 8 +- .../ModbusCoalescingAutoRecoveryTests.cs | 9 +- .../ModbusCoalescingBisectionTests.cs | 23 ++--- .../ModbusCoalescingTests.cs | 18 ++-- .../ModbusDriverTests.cs | 41 ++++---- .../ModbusExceptionMapperTests.cs | 21 ++-- .../ModbusLifecycleHygieneTests.cs | 40 ++++---- .../ModbusLoggerInjectionTests.cs | 16 ++-- .../ModbusMultiUnitTests.cs | 15 +-- .../ModbusProbeTests.cs | 17 ++-- .../ModbusProtocolOptionsTests.cs | 15 ++- .../ModbusSubscribeOptionsTests.cs | 14 +-- .../ModbusSubscriptionTests.cs | 18 ++-- .../ModbusTcpReconnectTests.cs | 3 + .../BrowseRoundTripTests.cs | 2 + .../OpcUaClientBrowseSessionTests.cs | 3 + .../OpcUaClientDriverBrowserTests.cs | 3 + .../OpcPlcFixture.cs | 1 + .../OpcUaClientSmokeTests.cs | 3 + .../OpcUaClientAlarmTests.cs | 6 +- .../OpcUaClientDiscoveryTests.cs | 20 ++-- .../OpcUaClientDriverScaffoldTests.cs | 2 + .../OpcUaClientFailoverTests.cs | 1 + .../OpcUaClientHistoryTests.cs | 5 + .../OpcUaClientLowFindingsRegressionTests.cs | 6 +- ...pcUaClientMediumFindingsRegressionTests.cs | 4 + .../OpcUaClientReadWriteTests.cs | 2 + .../OpcUaClientSubscribeAndProbeTests.cs | 2 + .../S7_1500/S7_1500Profile.cs | 1 + .../S7_1500/S7_1500SmokeTests.cs | 3 + .../Snap7ServerFixture.cs | 1 + .../S7DiscoveryAndSubscribeTests.cs | 26 ++--- .../S7DriverCodeReviewFixTests.cs | 7 ++ .../S7DriverCodeReviewFixTests2.cs | 6 ++ .../S7DriverReadWriteTests.cs | 3 + .../S7DriverScaffoldTests.cs | 1 + .../TwinCAT3SmokeTests.cs | 38 ++++---- .../TwinCATXarFixture.cs | 7 +- .../FakeTwinCATClient.cs | 44 ++------- .../TwinCATCapabilityTests.cs | 35 ++++--- .../TwinCATDriverTests.cs | 4 + .../TwinCATHighFindingsRegressionTests.cs | 6 ++ .../TwinCATLowFindingsRegressionTests.cs | 4 + .../TwinCATNativeNotificationTests.cs | 8 ++ .../TwinCATReadWriteTests.cs | 14 +++ .../TwinCATSymbolBrowserTests.cs | 10 +- .../AbCipDriverPageFormSerializationTests.cs | 9 ++ ...bLegacyDriverPageFormSerializationTests.cs | 9 ++ .../Browsing/BrowseSessionReaperTests.cs | 8 ++ .../Browsing/BrowseSessionRegistryTests.cs | 5 + .../Browsing/BrowserSessionServiceTests.cs | 14 +++ .../Browsing/FakeBrowseSession.cs | 5 +- .../DriverStatusSnapshotStoreTests.cs | 3 + .../FocasDriverPageFormSerializationTests.cs | 10 ++ .../GalaxyDriverPageFormSerializationTests.cs | 2 + ...derwareDriverPageFormSerializationTests.cs | 4 + .../InProcessBroadcasterTests.cs | 3 + .../ModbusDriverPageFormSerializationTests.cs | 6 ++ ...aClientDriverPageFormSerializationTests.cs | 9 ++ .../Pickers/AbLegacyAddressBuilderTests.cs | 5 + .../Pickers/FocasAddressBuilderTests.cs | 4 + .../HistorianWonderwareAddressBuilderTests.cs | 5 + .../Pickers/ModbusAddressBuilderTests.cs | 6 ++ .../Pickers/S7AddressBuilderTests.cs | 6 ++ .../ResilienceFormModelTests.cs | 4 + .../S7DriverPageFormSerializationTests.cs | 7 ++ ...TwinCATDriverPageFormSerializationTests.cs | 10 ++ .../_PlaceholderTests.cs | 1 + .../AuditWriterActorTests.cs | 3 + .../ConfigComposerTests.cs | 4 + .../ClusterFormationTests.cs | 2 + .../DeployHappyPathTests.cs | 2 + .../DockerFixtureAvailability.cs | 2 + .../DriverProbeRegistrationTests.cs | 2 + .../DriverReconnectE2eTests.cs | 2 + .../DriverStatusHubE2eTests.cs | 2 + .../DriverTestConnectE2eTests.cs | 3 + .../FailoverDuringDeployTests.cs | 3 + .../FleetDiagnosticsRoundTripTests.cs | 2 + .../LdapOpcUaUserAuthenticatorTests.cs | 12 ++- .../LdapOptionsBindingTests.cs | 5 + .../TwoNodeClusterHarness.cs | 11 ++- .../DualEndpointTests.cs | 1 + .../OpcUaApplicationHostImpersonationTests.cs | 1 + .../OpcUaApplicationHostSecurityTests.cs | 2 + .../OpcUaApplicationHostServerArrayTests.cs | 1 + .../OpcUaApplicationHostTests.cs | 2 + .../Phase7ApplierHierarchyTests.cs | 26 ++--- .../Phase7ApplierTests.cs | 51 ++-------- .../SdkAddressSpaceSinkTests.cs | 4 + .../SdkServiceLevelPublisherTests.cs | 2 + .../Drivers/DriverHostActorReconcileTests.cs | 5 +- .../Drivers/DriverInstanceActorTests.cs | 45 ++++----- .../Health/HealthProbeActorTests.cs | 8 +- .../OtOpcUaTelemetryHookTests.cs | 31 ++---- .../OpcUa/OpcUaPublishActorTests.cs | 28 ++---- .../OpcUa/ServiceLevelEndToEndTests.cs | 2 + .../ScriptedAlarmStatePersistenceTests.cs | 15 ++- .../ServiceCollectionExtensionsTests.cs | 17 ++-- .../VirtualTags/DependencyMuxActorTests.cs | 1 + .../VirtualTags/VirtualTagActorTests.cs | 6 +- .../Audit/AuditActorTests.cs | 1 + .../Audit/HttpAuditActorAccessorTests.cs | 1 + .../AuthEndpointsIntegrationTests.cs | 31 ++++-- .../CanonicalAdminRolesTests.cs | 28 ++++++ .../OtOpcUaGroupRoleMapperTests.cs | 23 +++++ .../OtOpcUaLdapAuthServiceTests.cs | 14 +++ .../RoleMapperTests.cs | 2 + .../UnwrappedCapabilityCallAnalyzerTests.cs | 26 +++++ 481 files changed, 2550 insertions(+), 1668 deletions(-) diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI/CommandBase.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI/CommandBase.cs index 7aca25ff..e2f3f788 100644 --- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI/CommandBase.cs +++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI/CommandBase.cs @@ -67,11 +67,13 @@ public abstract class CommandBase : ICommand /// Executes the command-specific workflow against the configured OPC UA endpoint. /// /// The CLI console used for output and cancellation handling. + /// A value task that represents the asynchronous command execution. public abstract ValueTask ExecuteAsync(IConsole console); /// /// Creates a from the common command options. /// + /// A populated from the current command option values. protected ConnectionSettings CreateConnectionSettings() { var securityMode = SecurityModeMapper.FromString(Security); @@ -97,6 +99,7 @@ public abstract class CommandBase : ICommand /// and returns both the service and the connection info. /// /// The cancellation token that aborts connection setup for the command. + /// A tuple of the connected and the resulting . protected async Task<(IOpcUaClientService Service, ConnectionInfo Info)> CreateServiceAndConnectAsync( CancellationToken ct) { diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/DefaultApplicationConfigurationFactory.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/DefaultApplicationConfigurationFactory.cs index 18e683a7..3165f322 100644 --- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/DefaultApplicationConfigurationFactory.cs +++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/DefaultApplicationConfigurationFactory.cs @@ -12,9 +12,7 @@ internal sealed class DefaultApplicationConfigurationFactory : IApplicationConfi { private static readonly ILogger Logger = Log.ForContext(); - /// Creates an OPC UA application configuration from the provided connection settings. - /// The connection settings to use. - /// Token to cancel the operation. + /// public async Task CreateAsync(ConnectionSettings settings, CancellationToken ct) { // Resolve the canonical PKI path lazily on first use so constructing a diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/DefaultEndpointDiscovery.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/DefaultEndpointDiscovery.cs index a8719460..687fbd63 100644 --- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/DefaultEndpointDiscovery.cs +++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/DefaultEndpointDiscovery.cs @@ -11,10 +11,7 @@ internal sealed class DefaultEndpointDiscovery : IEndpointDiscovery { private static readonly ILogger Logger = Log.ForContext(); - /// Selects an OPC UA endpoint matching the requested security mode. - /// The application configuration. - /// The endpoint URL to query. - /// The requested message security mode. + /// public EndpointDescription SelectEndpoint(ApplicationConfiguration config, string endpointUrl, MessageSecurityMode requestedMode) { @@ -53,6 +50,7 @@ internal static class EndpointSelector /// Thrown when no endpoint matches ; the message lists the /// security mode + policy combinations the server returned so operators can diagnose mismatches. /// + /// The best matching with its URL rewritten to the requested host. public static EndpointDescription SelectBest( IEnumerable allEndpoints, string endpointUrl, diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/IApplicationConfigurationFactory.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/IApplicationConfigurationFactory.cs index 29d8ae30..0a350cda 100644 --- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/IApplicationConfigurationFactory.cs +++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/IApplicationConfigurationFactory.cs @@ -13,5 +13,6 @@ internal interface IApplicationConfigurationFactory /// /// The connection settings to configure. /// Cancellation token for the operation. + /// A task that resolves to the validated . Task CreateAsync(ConnectionSettings settings, CancellationToken ct = default); } \ No newline at end of file diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/IEndpointDiscovery.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/IEndpointDiscovery.cs index 4f802729..816c3c18 100644 --- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/IEndpointDiscovery.cs +++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/IEndpointDiscovery.cs @@ -14,6 +14,7 @@ internal interface IEndpointDiscovery /// The OPC UA application configuration. /// The endpoint URL to discover. /// The requested message security mode. + /// The best matching endpoint description for the requested security mode. EndpointDescription SelectEndpoint(ApplicationConfiguration config, string endpointUrl, MessageSecurityMode requestedMode); } \ No newline at end of file diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/ISessionAdapter.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/ISessionAdapter.cs index d19a285c..1f9a84b7 100644 --- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/ISessionAdapter.cs +++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/ISessionAdapter.cs @@ -58,6 +58,7 @@ internal interface ISessionAdapter : IDisposable /// /// The node whose current runtime value should be read. /// The cancellation token that aborts the server read if the client cancels the request. + /// A task that resolves to the current for the node. Task ReadValueAsync(NodeId nodeId, CancellationToken ct = default); /// @@ -66,6 +67,7 @@ internal interface ISessionAdapter : IDisposable /// The node whose value should be updated. /// The typed OPC UA data value to write to the server. /// The cancellation token that aborts the write if the client cancels the request. + /// A task that resolves to the OPC UA for the write operation. Task WriteValueAsync(NodeId nodeId, DataValue value, CancellationToken ct = default); /// @@ -75,6 +77,7 @@ internal interface ISessionAdapter : IDisposable /// The starting node for the hierarchical browse. /// The node classes that should be returned to the caller. /// The cancellation token that aborts the browse request. + /// A task that resolves to a tuple of an optional continuation point and the returned references. Task<(byte[]? ContinuationPoint, ReferenceDescriptionCollection References)> BrowseAsync( NodeId nodeId, uint nodeClassMask = 0, CancellationToken ct = default); @@ -83,6 +86,7 @@ internal interface ISessionAdapter : IDisposable /// /// The continuation token returned by a prior browse result page. /// The cancellation token that aborts the browse-next request. + /// A task that resolves to a tuple of an optional next continuation point and the returned references. Task<(byte[]? ContinuationPoint, ReferenceDescriptionCollection References)> BrowseNextAsync( byte[] continuationPoint, CancellationToken ct = default); @@ -91,6 +95,7 @@ internal interface ISessionAdapter : IDisposable /// /// The node to inspect for child objects or variables. /// The cancellation token that aborts the child lookup. + /// A task that resolves to if the node has at least one child; otherwise . Task HasChildrenAsync(NodeId nodeId, CancellationToken ct = default); /// @@ -101,6 +106,7 @@ internal interface ISessionAdapter : IDisposable /// The inclusive end of the requested history window. /// The maximum number of raw samples to return to the client. /// The cancellation token that aborts the history read. + /// A task that resolves to the ordered list of raw historical data values. Task> HistoryReadRawAsync(NodeId nodeId, DateTime startTime, DateTime endTime, int maxValues, CancellationToken ct = default); @@ -113,6 +119,7 @@ internal interface ISessionAdapter : IDisposable /// The OPC UA aggregate function to evaluate over the history window. /// The processing interval, in milliseconds, for each aggregate bucket. /// The cancellation token that aborts the aggregate history read. + /// A task that resolves to the ordered list of processed aggregate data values. Task> HistoryReadAggregateAsync(NodeId nodeId, DateTime startTime, DateTime endTime, NodeId aggregateId, double intervalMs, CancellationToken ct = default); @@ -121,6 +128,7 @@ internal interface ISessionAdapter : IDisposable /// /// The requested publishing interval for monitored items on the new subscription. /// The cancellation token that aborts subscription creation. + /// A task that resolves to the newly created . Task CreateSubscriptionAsync(int publishingIntervalMs, CancellationToken ct = default); /// @@ -130,11 +138,13 @@ internal interface ISessionAdapter : IDisposable /// The method node to invoke. /// The ordered input arguments supplied to the server method call. /// The cancellation token that aborts the method invocation. + /// A task that resolves to the list of output arguments returned by the method, or if none. Task?> CallMethodAsync(NodeId objectId, NodeId methodId, object[] inputArguments, CancellationToken ct = default); /// /// Closes the underlying session gracefully before the adapter is disposed or replaced during failover. /// /// The cancellation token that aborts the close request. + /// A task that represents the asynchronous operation. Task CloseAsync(CancellationToken ct = default); } diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/ISubscriptionAdapter.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/ISubscriptionAdapter.cs index 21aeeb0c..afedf4c7 100644 --- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/ISubscriptionAdapter.cs +++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/Adapters/ISubscriptionAdapter.cs @@ -28,6 +28,7 @@ internal interface ISubscriptionAdapter : IDisposable /// /// The client handle returned when the monitored item was created. /// The cancellation token that aborts the monitored-item removal. + /// A task that represents the asynchronous operation. Task RemoveMonitoredItemAsync(uint clientHandle, CancellationToken ct = default); /// @@ -46,11 +47,13 @@ internal interface ISubscriptionAdapter : IDisposable /// Requests a condition refresh for this subscription. /// /// The cancellation token that aborts the condition refresh request. + /// A task that represents the asynchronous operation. Task ConditionRefreshAsync(CancellationToken ct = default); /// /// Removes all monitored items and deletes the subscription. /// /// The cancellation token that aborts subscription deletion. + /// A task that represents the asynchronous operation. Task DeleteAsync(CancellationToken ct = default); } diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/ClientStoragePaths.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/ClientStoragePaths.cs index d1309527..9efc28f8 100644 --- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/ClientStoragePaths.cs +++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/ClientStoragePaths.cs @@ -28,6 +28,7 @@ public static class ClientStoragePaths /// one-shot legacy-folder migration before returning so callers that depend on this /// path (PKI store, settings file) find their existing state at the canonical name. /// + /// The absolute path to the client's top-level folder under LocalApplicationData. public static string GetRoot() { var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); @@ -37,6 +38,7 @@ public static class ClientStoragePaths } /// Subfolder for the application's PKI store — used by both CLI + UI. + /// The absolute path to the PKI store subfolder. public static string GetPkiPath() => Path.Combine(GetRoot(), "pki"); /// @@ -45,6 +47,7 @@ public static class ClientStoragePaths /// folder existed + was moved to canonical, false when no migration was needed or /// canonical was already present. /// + /// when the legacy folder was found and moved; when no migration was needed. public static bool TryRunLegacyMigration() { var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/IOpcUaClientService.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/IOpcUaClientService.cs index 6990dde4..7659d27c 100644 --- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/IOpcUaClientService.cs +++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/IOpcUaClientService.cs @@ -24,12 +24,14 @@ public interface IOpcUaClientService : IDisposable /// /// The endpoint, security, and authentication settings used to establish the session. /// The cancellation token that aborts the connect workflow. + /// A describing the active session after a successful connect. Task ConnectAsync(ConnectionSettings settings, CancellationToken ct = default); /// /// Disconnects from the active OPC UA endpoint and tears down subscriptions owned by the client. /// /// The cancellation token that aborts disconnect cleanup. + /// A task that represents the asynchronous operation. Task DisconnectAsync(CancellationToken ct = default); /// @@ -37,6 +39,7 @@ public interface IOpcUaClientService : IDisposable /// /// The node whose value should be retrieved. /// The cancellation token that aborts the read request. + /// The current including value, status code, and timestamps. Task ReadValueAsync(NodeId nodeId, CancellationToken ct = default); /// @@ -45,6 +48,7 @@ public interface IOpcUaClientService : IDisposable /// The node whose value should be updated. /// The raw value supplied by the CLI or UI workflow. /// The cancellation token that aborts the write request. + /// The OPC UA returned by the server for the write operation. Task WriteValueAsync(NodeId nodeId, object value, CancellationToken ct = default); /// @@ -52,6 +56,7 @@ public interface IOpcUaClientService : IDisposable /// /// The node to browse, or when omitted. /// The cancellation token that aborts the browse request. + /// The list of child nodes discovered under the specified parent. Task> BrowseAsync(NodeId? parentNodeId = null, CancellationToken ct = default); /// @@ -60,6 +65,7 @@ public interface IOpcUaClientService : IDisposable /// The node whose value changes should be monitored. /// The monitored-item sampling and publishing interval in milliseconds. /// The cancellation token that aborts subscription creation. + /// A task that represents the asynchronous operation. Task SubscribeAsync(NodeId nodeId, int intervalMs = 1000, CancellationToken ct = default); /// @@ -67,6 +73,7 @@ public interface IOpcUaClientService : IDisposable /// /// The node whose live-data subscription should be removed. /// The cancellation token that aborts the unsubscribe request. + /// A task that represents the asynchronous operation. Task UnsubscribeAsync(NodeId nodeId, CancellationToken ct = default); /// @@ -75,18 +82,21 @@ public interface IOpcUaClientService : IDisposable /// The event source to monitor, or the server object when omitted. /// The publishing interval in milliseconds for the alarm subscription. /// The cancellation token that aborts alarm subscription creation. + /// A task that represents the asynchronous operation. Task SubscribeAlarmsAsync(NodeId? sourceNodeId = null, int intervalMs = 1000, CancellationToken ct = default); /// /// Removes the active alarm subscription. /// /// The cancellation token that aborts alarm subscription cleanup. + /// A task that represents the asynchronous operation. Task UnsubscribeAlarmsAsync(CancellationToken ct = default); /// /// Requests retained alarm conditions again so a client can repopulate its alarm list after reconnecting. /// /// The cancellation token that aborts the condition refresh request. + /// A task that represents the asynchronous operation. Task RequestConditionRefreshAsync(CancellationToken ct = default); /// @@ -111,6 +121,7 @@ public interface IOpcUaClientService : IDisposable /// The inclusive end of the requested history range. /// The maximum number of raw values to return. /// The cancellation token that aborts the history read. + /// The raw historical samples in the requested range. Task> HistoryReadRawAsync(NodeId nodeId, DateTime startTime, DateTime endTime, int maxValues = 1000, CancellationToken ct = default); @@ -123,6 +134,7 @@ public interface IOpcUaClientService : IDisposable /// The aggregate function the operator selected for processed history. /// The processing interval, in milliseconds, for each aggregate bucket. /// The cancellation token that aborts the processed history request. + /// The processed historical samples computed by the requested aggregate. Task> HistoryReadAggregateAsync(NodeId nodeId, DateTime startTime, DateTime endTime, AggregateType aggregate, double intervalMs = 3600000, CancellationToken ct = default); @@ -130,6 +142,7 @@ public interface IOpcUaClientService : IDisposable /// Reads redundancy status data such as redundancy mode, service level, and partner endpoint URIs. /// /// The cancellation token that aborts redundancy inspection. + /// A snapshot containing redundancy mode, service level, and partner endpoint URIs. Task GetRedundancyInfoAsync(CancellationToken ct = default); /// diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/OpcUaClientService.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/OpcUaClientService.cs index 57a6ac70..6df7eb85 100644 --- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/OpcUaClientService.cs +++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/OpcUaClientService.cs @@ -73,13 +73,13 @@ public sealed class OpcUaClientService : IOpcUaClientService { } - /// + /// Raised when subscribed node values change. public event EventHandler? DataChanged; - /// + /// Raised when an alarm event is received from the server. public event EventHandler? AlarmEvent; - /// + /// Raised when the connection state changes. public event EventHandler? ConnectionStateChanged; /// diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/Services/AvaloniaUiDispatcher.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/Services/AvaloniaUiDispatcher.cs index 13275830..e74c0cb2 100644 --- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/Services/AvaloniaUiDispatcher.cs +++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/Services/AvaloniaUiDispatcher.cs @@ -7,8 +7,7 @@ namespace ZB.MOM.WW.OtOpcUa.Client.UI.Services; /// public sealed class AvaloniaUiDispatcher : IUiDispatcher { - /// Posts an action to the Avalonia UI thread for execution. - /// The action to execute on the UI thread. + /// public void Post(Action action) { Dispatcher.UIThread.Post(action); diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/Services/ISettingsService.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/Services/ISettingsService.cs index f95c56c0..90cb9516 100644 --- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/Services/ISettingsService.cs +++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/Services/ISettingsService.cs @@ -6,6 +6,7 @@ namespace ZB.MOM.WW.OtOpcUa.Client.UI.Services; public interface ISettingsService { /// Loads user settings from persistent storage. + /// The persisted , or a default instance if none are saved. UserSettings Load(); /// Saves user settings to persistent storage. /// The settings to save. diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/Services/JsonSettingsService.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/Services/JsonSettingsService.cs index 2f793cad..97764de9 100644 --- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/Services/JsonSettingsService.cs +++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/Services/JsonSettingsService.cs @@ -19,8 +19,7 @@ public sealed class JsonSettingsService : ISettingsService WriteIndented = true }; - /// Loads user settings from the settings file. - /// The loaded user settings, or a new default instance if load fails. + /// public UserSettings Load() { try @@ -37,8 +36,7 @@ public sealed class JsonSettingsService : ISettingsService } } - /// Saves user settings to the settings file. - /// The user settings to save. + /// public void Save(UserSettings settings) { try diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/Services/SynchronousUiDispatcher.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/Services/SynchronousUiDispatcher.cs index 06c5f6bc..7744cc3e 100644 --- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/Services/SynchronousUiDispatcher.cs +++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/Services/SynchronousUiDispatcher.cs @@ -6,8 +6,7 @@ namespace ZB.MOM.WW.OtOpcUa.Client.UI.Services; /// public sealed class SynchronousUiDispatcher : IUiDispatcher { - /// Executes the action synchronously on the calling thread. - /// The action to execute. + /// public void Post(Action action) { action(); diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/ViewModels/AlarmsViewModel.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/ViewModels/AlarmsViewModel.cs index f7033901..3589793e 100644 --- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/ViewModels/AlarmsViewModel.cs +++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/ViewModels/AlarmsViewModel.cs @@ -195,6 +195,7 @@ public partial class AlarmsViewModel : ObservableObject /// /// Returns the monitored node ID for persistence, or null if not subscribed. /// + /// The monitored node ID string, or null if not currently subscribed. public string? GetAlarmSourceNodeId() { return IsSubscribed ? MonitoredNodeIdText : null; diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/ViewModels/BrowseTreeViewModel.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/ViewModels/BrowseTreeViewModel.cs index 6ca20cbf..c3c60b8e 100644 --- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/ViewModels/BrowseTreeViewModel.cs +++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/ViewModels/BrowseTreeViewModel.cs @@ -30,6 +30,7 @@ public class BrowseTreeViewModel : ObservableObject /// /// Loads root nodes by browsing with a null parent. /// + /// A task that represents the asynchronous operation. public async Task LoadRootsAsync() { var results = await _service.BrowseAsync(); diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/ViewModels/SubscriptionsViewModel.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/ViewModels/SubscriptionsViewModel.cs index 612ed752..c21bfd8f 100644 --- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/ViewModels/SubscriptionsViewModel.cs +++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/ViewModels/SubscriptionsViewModel.cs @@ -143,6 +143,7 @@ public partial class SubscriptionsViewModel : ObservableObject /// /// The node ID to subscribe to from the browse tree or persisted settings. /// The monitored-item interval, in milliseconds, for the subscription. + /// A task that represents the asynchronous operation. public async Task AddSubscriptionForNodeAsync(string nodeIdStr, int intervalMs = 1000) { if (!IsConnected || string.IsNullOrWhiteSpace(nodeIdStr)) return; @@ -176,6 +177,7 @@ public partial class SubscriptionsViewModel : ObservableObject /// The root node whose variables should be subscribed recursively. /// The node class of the starting node so variables can be subscribed immediately. /// The monitored-item interval, in milliseconds, used for created subscriptions. + /// A task that represents the asynchronous operation. public Task AddSubscriptionRecursiveAsync(string nodeIdStr, string nodeClass, int intervalMs = 1000) { return AddSubscriptionRecursiveAsync(nodeIdStr, nodeClass, intervalMs, maxDepth: 10, currentDepth: 0); @@ -211,6 +213,7 @@ public partial class SubscriptionsViewModel : ObservableObject /// /// Returns the node IDs of all active subscriptions for persistence. /// + /// The list of node ID strings for all currently active subscriptions. public List GetSubscribedNodeIds() { return ActiveSubscriptions.Select(s => s.NodeId).ToList(); @@ -220,6 +223,7 @@ public partial class SubscriptionsViewModel : ObservableObject /// Restores subscriptions from a saved list of node IDs. /// /// The node IDs persisted from a prior UI session. + /// A task that represents the asynchronous operation. public async Task RestoreSubscriptionsAsync(IEnumerable nodeIds) { foreach (var nodeId in nodeIds) @@ -232,6 +236,7 @@ public partial class SubscriptionsViewModel : ObservableObject /// /// The node ID the operator wants to write. /// The raw text value entered by the operator. + /// A tuple of (success flag, operator-readable message) describing the outcome of the write. public async Task<(bool Success, string Message)> ValidateAndWriteAsync(string nodeIdStr, string rawValue) { try diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ClusterRoleInfo.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ClusterRoleInfo.cs index 512be105..0861220d 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ClusterRoleInfo.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ClusterRoleInfo.cs @@ -43,20 +43,16 @@ public sealed class ClusterRoleInfo : IClusterRoleInfo, IDisposable _subscriber = system.ActorOf(Props.Create(() => new SubscriberActor(this)), "clusterroleinfo-subscriber"); } - /// Gets the local cluster node identifier. + /// public CommonsNodeId LocalNode => _localNode; - /// Gets the set of roles assigned to the local node. + /// public IReadOnlySet LocalRoles => _localRoles; - /// Checks if the local node has a specific role. - /// The role name to check. - /// True if the local node has the specified role; otherwise false. + /// public bool HasRole(string role) => _localRoles.Contains(role); - /// Gets all cluster members that have a specific role. - /// The role name. - /// A read-only list of node IDs with the specified role. + /// public IReadOnlyList MembersWithRole(string role) { lock (_lock) @@ -68,9 +64,7 @@ public sealed class ClusterRoleInfo : IClusterRoleInfo, IDisposable } } - /// Gets the current leader node for a specific role. - /// The role name. - /// The node ID of the current role leader, or null if no leader is elected. + /// public CommonsNodeId? RoleLeader(string role) { lock (_lock) diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/RoleParser.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/RoleParser.cs index a2de2290..ff6a52a8 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/RoleParser.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/RoleParser.cs @@ -9,6 +9,7 @@ public static class RoleParser /// Parses a comma-separated string of role names into a validated array. /// The raw role string to parse. + /// An array of validated, distinct, lower-cased role names; empty array when the input is null or whitespace. public static string[] Parse(string? raw) { if (string.IsNullOrWhiteSpace(raw)) return Array.Empty(); diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ServiceCollectionExtensions.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ServiceCollectionExtensions.cs index e2bab992..c4c6b348 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ServiceCollectionExtensions.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/ServiceCollectionExtensions.cs @@ -18,6 +18,7 @@ public static class ServiceCollectionExtensions /// /// The service collection to configure. /// The application configuration containing cluster options. + /// The same for chaining. public static IServiceCollection AddOtOpcUaCluster(this IServiceCollection services, IConfiguration configuration) { services.AddOptions() @@ -45,6 +46,7 @@ public static class ServiceCollectionExtensions /// /// The Akka configuration builder to configure. /// The service provider for resolving cluster options. + /// The same for chaining. public static AkkaConfigurationBuilder WithOtOpcUaClusterBootstrap( this AkkaConfigurationBuilder builder, IServiceProvider serviceProvider) diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Browsing/IBrowseSession.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Browsing/IBrowseSession.cs index a470b7e2..38326b0f 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Browsing/IBrowseSession.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Browsing/IBrowseSession.cs @@ -16,14 +16,22 @@ public interface IBrowseSession : IAsyncDisposable DateTime LastUsedUtc { get; } /// Returns the top-level browse nodes. + /// Cancellation token for the operation. + /// A task that resolves to the list of top-level browse nodes. Task> RootAsync(CancellationToken cancellationToken); /// Returns the direct children of the node identified by /// . + /// The identifier of the node whose children to expand. + /// Cancellation token for the operation. + /// A task that resolves to the list of direct child nodes. Task> ExpandAsync(string nodeId, CancellationToken cancellationToken); /// Returns the attributes of the node identified by . /// Empty for drivers whose tree is uniform (OPC UA Client). Galaxy uses this to populate /// the attribute side-panel after the user selects an object. + /// The identifier of the node whose attributes to retrieve. + /// Cancellation token for the operation. + /// A task that resolves to the list of attribute descriptors for the node. Task> AttributesAsync(string nodeId, CancellationToken cancellationToken); } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Browsing/IDriverBrowser.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Browsing/IDriverBrowser.cs index 80f80375..44a396f1 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Browsing/IDriverBrowser.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Browsing/IDriverBrowser.cs @@ -15,5 +15,6 @@ public interface IDriverBrowser /// Driver options serialized as JSON; same shape the runtime /// driver would consume. /// Cancellation for the connect phase only. + /// A task containing the opened browse session. Task OpenAsync(string configJson, CancellationToken cancellationToken); } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Engines/IAlarmActorStateStore.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Engines/IAlarmActorStateStore.cs index 56b195a4..3528d989 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Engines/IAlarmActorStateStore.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Engines/IAlarmActorStateStore.cs @@ -18,6 +18,7 @@ public interface IAlarmActorStateStore /// Saves the alarm actor state snapshot. /// The state snapshot to persist. /// Cancellation token. + /// A task that represents the asynchronous operation. Task SaveAsync(AlarmActorStateSnapshot snapshot, CancellationToken ct); } @@ -41,14 +42,10 @@ public sealed class NullAlarmActorStateStore : IAlarmActorStateStore { public static readonly NullAlarmActorStateStore Instance = new(); private NullAlarmActorStateStore() { } - /// Always returns null, indicating no persisted state. - /// The alarm identifier (unused). - /// Cancellation token (unused). + /// public Task LoadAsync(string alarmId, CancellationToken ct) => Task.FromResult(null); - /// Completes immediately without persisting anything. - /// The state snapshot (ignored). - /// Cancellation token (unused). + /// public Task SaveAsync(AlarmActorStateSnapshot snapshot, CancellationToken ct) => Task.CompletedTask; } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Engines/IVirtualTagEvaluator.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Engines/IVirtualTagEvaluator.cs index 023b48cf..78d5ff4a 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Engines/IVirtualTagEvaluator.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Engines/IVirtualTagEvaluator.cs @@ -43,11 +43,7 @@ public sealed class NullVirtualTagEvaluator : IVirtualTagEvaluator { public static readonly NullVirtualTagEvaluator Instance = new(); private NullVirtualTagEvaluator() { } - /// Returns for every evaluation. - /// The virtual tag identifier (ignored). - /// The expression string (ignored). - /// The variable dependencies (ignored). - /// Always returns . + /// public VirtualTagEvalResult Evaluate(string virtualTagId, string expression, IReadOnlyDictionary dependencies) => VirtualTagEvalResult.NoChange; } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Interfaces/IAdminOperationsClient.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Interfaces/IAdminOperationsClient.cs index be992f84..5ce41bb8 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Interfaces/IAdminOperationsClient.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Interfaces/IAdminOperationsClient.cs @@ -23,5 +23,6 @@ public interface IAdminOperationsClient /// Expected reply type. /// The message to send. /// Cancellation token (caller-controlled timeout). + /// A task that resolves to the reply of type . Task AskAsync(object message, CancellationToken ct); } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Interfaces/IFleetDiagnosticsClient.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Interfaces/IFleetDiagnosticsClient.cs index 262cf795..db6fda4e 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Interfaces/IFleetDiagnosticsClient.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Interfaces/IFleetDiagnosticsClient.cs @@ -11,5 +11,6 @@ public interface IFleetDiagnosticsClient /// Gets diagnostics for the specified node. /// The node ID to retrieve diagnostics for. /// The cancellation token. + /// A task that resolves to the diagnostics snapshot for the specified node. Task GetDiagnosticsAsync(NodeId nodeId, CancellationToken ct); } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Observability/OtOpcUaTelemetry.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Observability/OtOpcUaTelemetry.cs index 1a284860..fa5a2552 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Observability/OtOpcUaTelemetry.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Observability/OtOpcUaTelemetry.cs @@ -69,6 +69,7 @@ public static class OtOpcUaTelemetry /// null when no listener is attached so the call site stays cheap on undecorated builds. /// /// The deployment identifier to tag the span with. + /// The started , or null when no listener is attached. public static Activity? StartDeployApplySpan(string deploymentId) { var activity = ActivitySource.StartActivity("otopcua.deploy.apply", ActivityKind.Internal); @@ -77,6 +78,7 @@ public static class OtOpcUaTelemetry } /// Span wrapping a full OPC UA address-space rebuild (Phase7 plan → apply). + /// The started , or null when no listener is attached. public static Activity? StartAddressSpaceRebuildSpan() => ActivitySource.StartActivity("otopcua.opcua.address_space_rebuild", ActivityKind.Internal); } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/DeferredAddressSpaceSink.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/DeferredAddressSpaceSink.cs index 3bb7e934..6f3ecea8 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/DeferredAddressSpaceSink.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/DeferredAddressSpaceSink.cs @@ -22,37 +22,22 @@ public sealed class DeferredAddressSpaceSink : IOpcUaAddressSpaceSink public void SetSink(IOpcUaAddressSpaceSink? sink) => _inner = sink ?? NullOpcUaAddressSpaceSink.Instance; - /// Writes a value to the OPC UA address space through the inner sink. - /// The node ID of the variable. - /// The value to write. - /// The OPC UA quality value. - /// The source timestamp in UTC. + /// public void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc) => _inner.WriteValue(nodeId, value, quality, sourceTimestampUtc); - /// Writes an alarm state through the inner sink. - /// The node ID of the alarm condition. - /// Whether the alarm is active. - /// Whether the alarm has been acknowledged. - /// The source timestamp in UTC. + /// public void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime sourceTimestampUtc) => _inner.WriteAlarmState(alarmNodeId, active, acknowledged, sourceTimestampUtc); - /// Ensures a folder exists in the address space through the inner sink. - /// The node ID of the folder. - /// The node ID of the parent folder, or null for root. - /// The display name of the folder. + /// public void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName) => _inner.EnsureFolder(folderNodeId, parentNodeId, displayName); - /// Ensures a variable exists in the address space through the inner sink. - /// The node ID of the variable. - /// The node ID of the parent folder, or null for root. - /// The display name of the variable. - /// The OPC UA data type of the variable. + /// public void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType) => _inner.EnsureVariable(variableNodeId, parentFolderNodeId, displayName, dataType); - /// Rebuilds the address space through the inner sink. + /// public void RebuildAddressSpace() => _inner.RebuildAddressSpace(); } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/DeferredServiceLevelPublisher.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/DeferredServiceLevelPublisher.cs index eb227979..f567d9ec 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/DeferredServiceLevelPublisher.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/DeferredServiceLevelPublisher.cs @@ -16,7 +16,6 @@ public sealed class DeferredServiceLevelPublisher : IServiceLevelPublisher public void SetInner(IServiceLevelPublisher? inner) => _inner = inner ?? NullServiceLevelPublisher.Instance; - /// Publishes a service level value to the inner publisher. - /// The service level to publish. + /// public void Publish(byte serviceLevel) => _inner.Publish(serviceLevel); } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Types/CorrelationId.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Types/CorrelationId.cs index fbf9dc06..6cc827a1 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Types/CorrelationId.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Types/CorrelationId.cs @@ -3,15 +3,18 @@ namespace ZB.MOM.WW.OtOpcUa.Commons.Types; public readonly record struct CorrelationId(Guid Value) { /// Creates a new CorrelationId with a randomly generated GUID. + /// A new backed by a random GUID. public static CorrelationId NewId() => new(Guid.NewGuid()); /// public override string ToString() => Value.ToString("N"); /// Parses a lowercase hex string without hyphens into a CorrelationId. /// The string to parse. + /// A parsed from the supplied string. public static CorrelationId Parse(string s) => new(Guid.ParseExact(s, "N")); /// Attempts to parse a lowercase hex string without hyphens into a CorrelationId. /// The string to parse, or null. /// The resulting CorrelationId if parsing succeeds. + /// if parsing succeeded; otherwise . public static bool TryParse(string? s, out CorrelationId id) { if (Guid.TryParseExact(s, "N", out var g)) { id = new CorrelationId(g); return true; } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/LocalCache/ILocalConfigCache.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/LocalCache/ILocalConfigCache.cs index fc92d9ec..c1618321 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/LocalCache/ILocalConfigCache.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/LocalCache/ILocalConfigCache.cs @@ -21,10 +21,12 @@ public interface ILocalConfigCache /// Stores a generation snapshot in the local cache. /// The generation snapshot to store. /// The cancellation token. + /// A task that represents the asynchronous operation. Task PutAsync(GenerationSnapshot snapshot, CancellationToken ct = default); /// Removes old generations, keeping only the most recent N. /// The cluster identifier. /// The number of latest generations to keep. /// The cancellation token. + /// A task that represents the asynchronous operation. Task PruneOldGenerationsAsync(string clusterId, int keepLatest = 10, CancellationToken ct = default); } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/LocalCache/LiteDbConfigCache.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/LocalCache/LiteDbConfigCache.cs index 39e5dfb4..12deb1eb 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/LocalCache/LiteDbConfigCache.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/LocalCache/LiteDbConfigCache.cs @@ -45,9 +45,7 @@ public sealed class LiteDbConfigCache : ILocalConfigCache, IDisposable } } - /// Gets the most recent snapshot for the specified cluster. - /// The cluster ID. - /// Cancellation token. + /// public Task GetMostRecentAsync(string clusterId, CancellationToken ct = default) { ct.ThrowIfCancellationRequested(); @@ -58,9 +56,7 @@ public sealed class LiteDbConfigCache : ILocalConfigCache, IDisposable return Task.FromResult(snapshot); } - /// Stores a snapshot in the cache. - /// The snapshot to store. - /// Cancellation token. + /// public async Task PutAsync(GenerationSnapshot snapshot, CancellationToken ct = default) { ct.ThrowIfCancellationRequested(); @@ -89,10 +85,7 @@ public sealed class LiteDbConfigCache : ILocalConfigCache, IDisposable } } - /// Removes old generation snapshots, keeping only the latest ones. - /// The cluster ID. - /// Number of latest generations to keep. - /// Cancellation token. + /// public Task PruneOldGenerationsAsync(string clusterId, int keepLatest = 10, CancellationToken ct = default) { ct.ThrowIfCancellationRequested(); diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Services/ILdapGroupRoleMappingService.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Services/ILdapGroupRoleMappingService.cs index a34f15fd..308f289f 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Services/ILdapGroupRoleMappingService.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Services/ILdapGroupRoleMappingService.cs @@ -24,11 +24,13 @@ public interface ILdapGroupRoleMappingService /// /// The LDAP groups to search for. /// The cancellation token. + /// A task resolving to the list of mappings whose LDAP group matches any of the provided groups. Task> GetByGroupsAsync( IEnumerable ldapGroups, CancellationToken cancellationToken); /// Enumerate every mapping; Admin UI listing only. /// The cancellation token. + /// A task resolving to all LDAP group role mappings. Task> ListAllAsync(CancellationToken cancellationToken); /// Create a new grant. @@ -39,11 +41,13 @@ public interface ILdapGroupRoleMappingService /// /// The LDAP group role mapping to create. /// The cancellation token. + /// A task resolving to the newly created with any DB-assigned values populated. Task CreateAsync(LdapGroupRoleMapping row, CancellationToken cancellationToken); /// Delete a mapping by its surrogate key. /// The unique identifier of the mapping to delete. /// The cancellation token. + /// A task that represents the asynchronous delete operation. Task DeleteAsync(Guid id, CancellationToken cancellationToken); } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Services/LdapGroupRoleMappingService.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Services/LdapGroupRoleMappingService.cs index 283fadbc..c79809a4 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Services/LdapGroupRoleMappingService.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Services/LdapGroupRoleMappingService.cs @@ -10,10 +10,7 @@ namespace ZB.MOM.WW.OtOpcUa.Configuration.Services; /// public sealed class LdapGroupRoleMappingService(OtOpcUaConfigDbContext db) : ILdapGroupRoleMappingService { - /// Gets LDAP group role mappings for the specified groups. - /// The LDAP group names to query. - /// The cancellation token. - /// The matching role mappings. + /// public async Task> GetByGroupsAsync( IEnumerable ldapGroups, CancellationToken cancellationToken) { diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Validation/DraftValidator.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Validation/DraftValidator.cs index 718485b8..19f4487c 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Validation/DraftValidator.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Validation/DraftValidator.cs @@ -21,6 +21,7 @@ public static class DraftValidator /// Validates a draft snapshot and returns all validation errors found in a single pass. /// /// The draft snapshot to validate. + /// A read-only list of all validation errors found; empty if the draft is valid. public static IReadOnlyList Validate(DraftSnapshot draft) { var errors = new List(); @@ -147,6 +148,7 @@ public static class DraftValidator /// Decision #125: EquipmentId = 'EQ-' + lowercase first 12 hex chars of the UUID. /// The equipment UUID to derive the ID from. + /// The derived equipment ID string in the form EQ-xxxxxxxxxxxx. public static string DeriveEquipmentId(Guid uuid) => "EQ-" + uuid.ToString("N")[..12].ToLowerInvariant(); @@ -203,6 +205,7 @@ public static class DraftValidator /// /// The server cluster to validate. /// The cluster nodes to validate against the cluster configuration. + /// A read-only list of all validation errors found; empty if the topology is valid. public static IReadOnlyList ValidateClusterTopology( ServerCluster cluster, IReadOnlyList clusterNodes) diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/DriverTypeRegistry.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/DriverTypeRegistry.cs index aa60242d..6c792556 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/DriverTypeRegistry.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/DriverTypeRegistry.cs @@ -55,6 +55,7 @@ public sealed class DriverTypeRegistry /// Look up a driver type by name. Throws if unknown. /// The driver type name to look up. + /// The registered for the specified type name. public DriverTypeMetadata Get(string driverType) { ArgumentException.ThrowIfNullOrWhiteSpace(driverType); @@ -69,6 +70,7 @@ public sealed class DriverTypeRegistry /// Try to look up a driver type by name. Returns null if unknown (no exception). /// The driver type name to look up. + /// The matching , or null if not registered. public DriverTypeMetadata? TryGet(string driverType) { ArgumentException.ThrowIfNullOrWhiteSpace(driverType); @@ -76,6 +78,7 @@ public sealed class DriverTypeRegistry } /// Snapshot of all registered driver types. + /// A read-only collection of all currently registered driver type metadata entries. public IReadOnlyCollection All() => _types.Values.ToList(); } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/Historian/IHistorianDataSource.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/Historian/IHistorianDataSource.cs index 0f234500..d6895225 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/Historian/IHistorianDataSource.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/Historian/IHistorianDataSource.cs @@ -28,6 +28,7 @@ public interface IHistorianDataSource : IDisposable /// The end of the time range in UTC. /// The maximum number of values to return per node. /// A cancellation token that can be used to cancel the operation. + /// A task resolving to a containing the raw samples. Task ReadRawAsync( string fullReference, DateTime startUtc, @@ -46,6 +47,7 @@ public interface IHistorianDataSource : IDisposable /// The interval for bucketing samples. /// The aggregation function to apply to each bucket. /// A cancellation token that can be used to cancel the operation. + /// A task resolving to a containing the processed interval samples. Task ReadProcessedAsync( string fullReference, DateTime startUtc, @@ -63,6 +65,7 @@ public interface IHistorianDataSource : IDisposable /// The full reference of the tag to read. /// The list of timestamps to read values at. /// A cancellation token that can be used to cancel the operation. + /// A task resolving to a with one sample per requested timestamp. Task ReadAtTimeAsync( string fullReference, IReadOnlyList timestampsUtc, @@ -93,6 +96,7 @@ public interface IHistorianDataSource : IDisposable /// The end of the time range in UTC. /// The maximum number of events to return, or a non-positive value to use the default backend cap. /// A cancellation token that can be used to cancel the operation. + /// A task resolving to a containing historical alarm and event records. Task ReadEventsAsync( string? sourceName, DateTime startUtc, @@ -104,5 +108,6 @@ public interface IHistorianDataSource : IDisposable /// Point-in-time health snapshot for diagnostics and dashboards. Pure /// observation; never blocks on backend I/O. /// + /// The current for this data source. HistorianHealthSnapshot GetHealthSnapshot(); } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IAddressSpaceBuilder.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IAddressSpaceBuilder.cs index 85118c95..33e6183f 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IAddressSpaceBuilder.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IAddressSpaceBuilder.cs @@ -18,6 +18,7 @@ public interface IAddressSpaceBuilder /// /// OPC UA browse name (the segment of the path under the parent). /// Human-readable display name. May equal . + /// A child builder scoped to inside this folder. IAddressSpaceBuilder Folder(string browseName, string displayName); /// @@ -27,6 +28,7 @@ public interface IAddressSpaceBuilder /// OPC UA browse name (the segment of the path under the parent folder). /// Human-readable display name. May equal . /// Driver-side metadata for the variable. + /// An opaque handle for the registered variable. IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo attributeInfo); /// @@ -56,6 +58,7 @@ public interface IVariableHandle /// Acknowledge, Deactivate). /// /// The alarm condition information. + /// A sink that receives alarm lifecycle transitions for this condition. IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info); } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IAlarmSource.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IAlarmSource.cs index fa66e5a4..1bcbf0d5 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IAlarmSource.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IAlarmSource.cs @@ -13,6 +13,7 @@ public interface IAlarmSource /// /// The driver node IDs to subscribe to. /// Cancellation token for the operation. + /// A task that resolves to an opaque for the new subscription. Task SubscribeAlarmsAsync( IReadOnlyList sourceNodeIds, CancellationToken cancellationToken); @@ -20,11 +21,13 @@ public interface IAlarmSource /// Cancel an alarm subscription returned by . /// The subscription handle returned from . /// Cancellation token for the operation. + /// A task that represents the asynchronous operation. Task UnsubscribeAlarmsAsync(IAlarmSubscriptionHandle handle, CancellationToken cancellationToken); /// Acknowledge one or more active alarms by source node ID + condition ID. /// The batch of alarm acknowledgement requests. /// Cancellation token for the operation. + /// A task that represents the asynchronous operation. Task AcknowledgeAsync( IReadOnlyList acknowledgements, CancellationToken cancellationToken); diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriver.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriver.cs index 9e07ee25..646a22b9 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriver.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriver.cs @@ -23,6 +23,7 @@ public interface IDriver /// Initialize the driver from its DriverConfig JSON; open connections; prepare for first use. /// The driver configuration as JSON. /// Cancellation token for the operation. + /// A task that represents the asynchronous operation. Task InitializeAsync(string driverConfigJson, CancellationToken cancellationToken); /// @@ -37,13 +38,16 @@ public interface IDriver /// /// The driver configuration as JSON. /// Cancellation token for the operation. + /// A task that represents the asynchronous operation. Task ReinitializeAsync(string driverConfigJson, CancellationToken cancellationToken); /// Stop the driver, close connections, release resources. Called on shutdown or driver removal. /// Cancellation token for the operation. + /// A task that represents the asynchronous operation. Task ShutdownAsync(CancellationToken cancellationToken); /// Current health snapshot, polled by Core for the status dashboard and ServiceLevel. + /// The current driver health snapshot. DriverHealth GetHealth(); /// @@ -56,6 +60,7 @@ public interface IDriver /// allocation tracking". Tier C drivers (process-isolated) report through the same /// interface but the cache-flush is internal to their host. /// + /// The approximate driver-attributable memory footprint in bytes. long GetMemoryFootprint(); /// @@ -63,5 +68,6 @@ public interface IDriver /// Required-for-correctness state must NOT be flushed. /// /// Cancellation token for the operation. + /// A task that represents the asynchronous operation. Task FlushOptionalCachesAsync(CancellationToken cancellationToken); } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriverFactory.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriverFactory.cs index ab154e30..e27fd6a9 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriverFactory.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriverFactory.cs @@ -34,12 +34,8 @@ public sealed class NullDriverFactory : IDriverFactory public static readonly NullDriverFactory Instance = new(); private NullDriverFactory() { } - /// Creates a driver (always returns null in this null implementation). - /// The driver type name. - /// The driver instance identifier. - /// The driver configuration as a JSON string. - /// Always returns null. + /// public IDriver? TryCreate(string driverType, string driverInstanceId, string driverConfigJson) => null; - /// Gets the collection of supported driver types (empty in this null implementation). + /// public IReadOnlyCollection SupportedTypes { get; } = Array.Empty(); } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriverHealthPublisher.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriverHealthPublisher.cs index fa2516db..bfe43c4d 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriverHealthPublisher.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriverHealthPublisher.cs @@ -11,6 +11,10 @@ public interface IDriverHealthPublisher /// Publishes a health snapshot for one driver instance. Implementations must be /// non-blocking and tolerant of being called from any thread. /// + /// The cluster identifier the driver instance belongs to. + /// The unique identifier of the driver instance. + /// The current health state of the driver instance. + /// Number of errors recorded in the past 5 minutes. void Publish( string clusterId, string driverInstanceId, diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriverProbe.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriverProbe.cs index 2043f3cf..5815ae5c 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriverProbe.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriverProbe.cs @@ -17,6 +17,10 @@ public interface IDriverProbe /// timeout cancellation. Never throw on connection failure; instead return a result /// with Ok = false + a message. /// + /// Driver configuration JSON; same shape the runtime driver consumes. + /// Maximum duration for the probe attempt. + /// Cancellation token for the probe operation. + /// A task containing the probe result with success status and optional latency. Task ProbeAsync(string configJson, TimeSpan timeout, CancellationToken ct); } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriverSupervisor.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriverSupervisor.cs index 0e072712..33e5b160 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriverSupervisor.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IDriverSupervisor.cs @@ -22,5 +22,6 @@ public interface IDriverSupervisor /// /// Human-readable reason — flows into the supervisor's logs. /// Cancels the recycle request; an in-flight restart is not interrupted. + /// A task that represents the asynchronous operation. Task RecycleAsync(string reason, CancellationToken cancellationToken); } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IHistoryProvider.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IHistoryProvider.cs index 70e089bd..173e3026 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IHistoryProvider.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IHistoryProvider.cs @@ -94,6 +94,7 @@ public interface IHistoryProvider /// HistorianDataSource). The asymmetry is intentional — Core.Abstractions-006. /// /// Request cancellation. + /// A task that resolves to the historical events result for the requested window. /// /// Default implementation throws. Only drivers with an event historian (Galaxy via the /// Wonderware Alarm & Events log) override. Modbus / the OPC UA Client driver stay diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IHostConnectivityProbe.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IHostConnectivityProbe.cs index 3a446b1d..e712ca9b 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IHostConnectivityProbe.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IHostConnectivityProbe.cs @@ -16,6 +16,7 @@ public interface IHostConnectivityProbe /// Snapshot of host-level connectivity. The Core uses this to drive Bad-quality /// fan-out scoped to the affected host's subtree (not the whole driver namespace). /// + /// A snapshot list of per-host connectivity statuses. IReadOnlyList GetHostStatuses(); /// Fired when a host transitions Running ↔ Stopped (or similar lifecycle change). diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/ITagDiscovery.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/ITagDiscovery.cs index e66f3a9e..87b02f80 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/ITagDiscovery.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/ITagDiscovery.cs @@ -13,5 +13,6 @@ public interface ITagDiscovery /// /// The address space builder to stream discovered nodes into. /// A cancellation token for the discovery operation. + /// A task that represents the asynchronous discovery operation. Task DiscoverAsync(IAddressSpaceBuilder builder, CancellationToken cancellationToken); } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IWritable.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IWritable.cs index 4abf60e4..75f69c3c 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IWritable.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IWritable.cs @@ -19,6 +19,7 @@ public interface IWritable /// /// Pairs of full reference + value to write. /// Cancellation token; the driver should abort the batch if cancelled. + /// A task that resolves to one per requested write, in the same order. Task> WriteAsync( IReadOnlyList writes, CancellationToken cancellationToken); diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/PollGroupEngine.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/PollGroupEngine.cs index f8c54378..ed526ca5 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/PollGroupEngine.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/PollGroupEngine.cs @@ -41,6 +41,7 @@ public sealed class PollGroupEngine : IAsyncDisposable /// Default floor for publishing intervals — matches the Modbus 100 ms cap. public static readonly TimeSpan DefaultMinInterval = TimeSpan.FromMilliseconds(100); + /// Initializes a new poll-group engine with the supplied reader, change callback, interval floor, and optional error sink. /// Driver-supplied batch reader; snapshots MUST be returned in the same /// order as the input references. /// Callback invoked per changed tag — the driver forwards to its own @@ -68,6 +69,7 @@ public sealed class PollGroupEngine : IAsyncDisposable /// Register a new polled subscription and start its background loop. /// The list of tag references to poll. /// The desired polling interval; will be clamped to the configured minimum. + /// A subscription handle that can be passed to to cancel the loop. public ISubscriptionHandle Subscribe(IReadOnlyList fullReferences, TimeSpan publishingInterval) { ArgumentNullException.ThrowIfNull(fullReferences); @@ -207,6 +209,7 @@ public sealed class PollGroupEngine : IAsyncDisposable } /// Cancel every active subscription and await all loop tasks. Idempotent. + /// A value task that represents the asynchronous dispose operation. public async ValueTask DisposeAsync() { // Cancel all loops first so they can all start winding down in parallel. @@ -253,7 +256,7 @@ public sealed class PollGroupEngine : IAsyncDisposable private sealed record PollSubscriptionHandle(long Id) : ISubscriptionHandle { - /// Gets a diagnostic identifier for this subscription. + /// public string DiagnosticId => $"poll-sub-{Id}"; } } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian/IAlarmHistorianSink.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian/IAlarmHistorianSink.cs index 8fc95c86..bcb2b4a7 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian/IAlarmHistorianSink.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian/IAlarmHistorianSink.cs @@ -26,9 +26,11 @@ public interface IAlarmHistorianSink /// Durably enqueue the event. Returns as soon as the queue row is committed. /// The alarm historian event to enqueue. /// A cancellation token for async operations. + /// A task that represents the asynchronous enqueue operation. Task EnqueueAsync(AlarmHistorianEvent evt, CancellationToken cancellationToken); /// Snapshot of current queue depth + drain health. + /// A snapshot of the current queue depth and drain state. HistorianSinkStatus GetStatus(); } @@ -97,6 +99,7 @@ public interface IAlarmHistorianWriter /// Push a batch of events to the historian. Returns one outcome per event, same order. /// The batch of alarm historian events to write. /// A cancellation token for async operations. + /// A task that resolves to one write outcome per event, in the same order as the batch. Task> WriteBatchAsync( IReadOnlyList batch, CancellationToken cancellationToken); } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian/SqliteStoreAndForwardSink.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian/SqliteStoreAndForwardSink.cs index f887990f..eb03013f 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian/SqliteStoreAndForwardSink.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian/SqliteStoreAndForwardSink.cs @@ -255,6 +255,7 @@ public sealed class SqliteStoreAndForwardSink : IAlarmHistorianSink, IDisposable /// /// The alarm historian event to enqueue. /// Cancellation token for the operation. + /// A task that represents the asynchronous operation. public async Task EnqueueAsync(AlarmHistorianEvent evt, CancellationToken cancellationToken) { if (evt is null) throw new ArgumentNullException(nameof(evt)); @@ -345,6 +346,7 @@ public sealed class SqliteStoreAndForwardSink : IAlarmHistorianSink, IDisposable /// connections per tick, each paying the open + PRAGMA cost. /// /// Cancellation token for the operation. + /// A task that represents the asynchronous operation. public async Task DrainOnceAsync(CancellationToken ct) { if (_disposed) return; @@ -490,7 +492,7 @@ public sealed class SqliteStoreAndForwardSink : IAlarmHistorianSink, IDisposable } } - /// Gets the current status of the historian sink including queue depth and drain state. + /// public HistorianSinkStatus GetStatus() { // Core.AlarmHistorian-008: read the non-dead-lettered count from the in-memory @@ -534,6 +536,7 @@ public sealed class SqliteStoreAndForwardSink : IAlarmHistorianSink, IDisposable } /// Operator action from Admin UI — retry every dead-lettered row. Non-cascading: they rejoin the regular queue + get a fresh backoff. + /// The number of rows moved back to the active queue. public int RetryDeadLettered() { using var conn = OpenConnection(); diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms/ScriptedAlarmEngine.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms/ScriptedAlarmEngine.cs index 700ec0bc..8c69dc43 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms/ScriptedAlarmEngine.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms/ScriptedAlarmEngine.cs @@ -97,6 +97,7 @@ public sealed class ScriptedAlarmEngine : IDisposable /// copy under the gate. (Core.ScriptedAlarms-013.) /// /// The alarm identifier to look up. + /// The live read-cache dictionary for the alarm, or if not yet allocated. internal IReadOnlyDictionary? TryGetScratchReadCacheForTest(string alarmId) => _scratchByAlarmId.TryGetValue(alarmId, out var s) ? s.ReadCache : null; @@ -113,6 +114,7 @@ public sealed class ScriptedAlarmEngine : IDisposable /// (Core.ScriptedAlarms-013.) /// /// The alarm identifier to look up. + /// The reusable for the alarm, or if not yet allocated. internal AlarmPredicateContext? TryGetScratchContextForTest(string alarmId) => _scratchByAlarmId.TryGetValue(alarmId, out var s) ? s.Context : null; private readonly ConcurrentDictionary _valueCache @@ -175,6 +177,7 @@ public sealed class ScriptedAlarmEngine : IDisposable /// /// The alarm definitions to load. /// The cancellation token. + /// A task that represents the asynchronous operation. public async Task LoadAsync(IReadOnlyList definitions, CancellationToken ct) { if (_disposed) throw new ObjectDisposedException(nameof(ScriptedAlarmEngine)); @@ -306,10 +309,12 @@ public sealed class ScriptedAlarmEngine : IDisposable /// unknown alarm. Mainly used for diagnostics + the Admin UI status page. /// /// The alarm identifier. + /// The current for the alarm, or if the alarm is unknown. public AlarmConditionState? GetState(string alarmId) => _alarms.TryGetValue(alarmId, out var s) ? s.Condition : null; /// Gets the current persisted state for all loaded alarms. + /// A snapshot collection of all current alarm condition states. public IReadOnlyCollection GetAllStates() => _alarms.Values.Select(a => a.Condition).ToArray(); @@ -318,6 +323,7 @@ public sealed class ScriptedAlarmEngine : IDisposable /// The user performing the acknowledgment. /// An optional comment to attach to the acknowledgment. /// The cancellation token. + /// A task that represents the asynchronous operation. public Task AcknowledgeAsync(string alarmId, string user, string? comment, CancellationToken ct) => ApplyAsync(alarmId, ct, cur => Part9StateMachine.ApplyAcknowledge(cur, user, comment, _clock())); @@ -326,6 +332,7 @@ public sealed class ScriptedAlarmEngine : IDisposable /// The user performing the confirmation. /// An optional comment to attach to the confirmation. /// The cancellation token. + /// A task that represents the asynchronous operation. public Task ConfirmAsync(string alarmId, string user, string? comment, CancellationToken ct) => ApplyAsync(alarmId, ct, cur => Part9StateMachine.ApplyConfirm(cur, user, comment, _clock())); @@ -333,6 +340,7 @@ public sealed class ScriptedAlarmEngine : IDisposable /// The alarm identifier. /// The user performing the shelve operation. /// The cancellation token. + /// A task that represents the asynchronous operation. public Task OneShotShelveAsync(string alarmId, string user, CancellationToken ct) => ApplyAsync(alarmId, ct, cur => Part9StateMachine.ApplyOneShotShelve(cur, user, _clock())); @@ -341,6 +349,7 @@ public sealed class ScriptedAlarmEngine : IDisposable /// The user performing the shelve operation. /// The UTC time at which the shelve will automatically expire. /// The cancellation token. + /// A task that represents the asynchronous operation. public Task TimedShelveAsync(string alarmId, string user, DateTime unshelveAtUtc, CancellationToken ct) => ApplyAsync(alarmId, ct, cur => Part9StateMachine.ApplyTimedShelve(cur, user, unshelveAtUtc, _clock())); @@ -348,6 +357,7 @@ public sealed class ScriptedAlarmEngine : IDisposable /// The alarm identifier. /// The user performing the unshelve operation. /// The cancellation token. + /// A task that represents the asynchronous operation. public Task UnshelveAsync(string alarmId, string user, CancellationToken ct) => ApplyAsync(alarmId, ct, cur => Part9StateMachine.ApplyUnshelve(cur, user, _clock())); @@ -355,6 +365,7 @@ public sealed class ScriptedAlarmEngine : IDisposable /// The alarm identifier. /// The user performing the enable operation. /// The cancellation token. + /// A task that represents the asynchronous operation. public Task EnableAsync(string alarmId, string user, CancellationToken ct) => ApplyAsync(alarmId, ct, cur => Part9StateMachine.ApplyEnable(cur, user, _clock())); @@ -362,6 +373,7 @@ public sealed class ScriptedAlarmEngine : IDisposable /// The alarm identifier. /// The user performing the disable operation. /// The cancellation token. + /// A task that represents the asynchronous operation. public Task DisableAsync(string alarmId, string user, CancellationToken ct) => ApplyAsync(alarmId, ct, cur => Part9StateMachine.ApplyDisable(cur, user, _clock())); @@ -370,6 +382,7 @@ public sealed class ScriptedAlarmEngine : IDisposable /// The user adding the comment. /// The comment text. /// The cancellation token. + /// A task that represents the asynchronous operation. public Task AddCommentAsync(string alarmId, string user, string text, CancellationToken ct) => ApplyAsync(alarmId, ct, cur => Part9StateMachine.ApplyAddComment(cur, user, text, _clock())); diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting/DependencyExtractor.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting/DependencyExtractor.cs index 819c0651..ebbeddca 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting/DependencyExtractor.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting/DependencyExtractor.cs @@ -39,6 +39,7 @@ public static class DependencyExtractor /// paths, or a list of rejection messages if non-literal paths were used. /// /// The script source code to analyze. + /// The extracted dependency paths, or rejection messages for unsupported patterns. public static DependencyExtractionResult Extract(string scriptSource) { if (string.IsNullOrWhiteSpace(scriptSource)) diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting/ScriptContext.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting/ScriptContext.cs index 8b1d0767..e245f151 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting/ScriptContext.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting/ScriptContext.cs @@ -41,6 +41,7 @@ public abstract class ScriptContext /// right upstream tags at load time. /// /// The literal tag path to read. + /// The current for the tag, including value, quality, and timestamp. public abstract DataValueSnapshot GetTag(string path); /// @@ -81,6 +82,7 @@ public abstract class ScriptContext /// The current value to check. /// The previous value to compare against. /// The minimum difference threshold for a change to be detected. + /// when the absolute difference between current and previous exceeds tolerance. public static bool Deadband(double current, double previous, double tolerance) => Math.Abs(current - previous) > tolerance; } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting/ScriptEvaluator.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting/ScriptEvaluator.cs index de482ede..ea508ca8 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting/ScriptEvaluator.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting/ScriptEvaluator.cs @@ -66,6 +66,7 @@ public sealed class ScriptEvaluator : IDisposable /// Compiles user script source into an evaluator. /// The user script source code to compile. + /// A compiled ready to invoke. public static ScriptEvaluator Compile(string scriptSource) { if (scriptSource is null) throw new ArgumentNullException(nameof(scriptSource)); @@ -173,6 +174,7 @@ public sealed class ScriptEvaluator : IDisposable /// Runs the script against an already-constructed context. /// The script context. /// Cancellation token for the operation. + /// A task that resolves to the script's return value. public Task RunAsync(TContext context, CancellationToken ct = default) { if (_disposed) throw new ObjectDisposedException(nameof(ScriptEvaluator)); diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting/ScriptSandbox.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting/ScriptSandbox.cs index aed728d0..5cf6fbde 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting/ScriptSandbox.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting/ScriptSandbox.cs @@ -43,6 +43,7 @@ public static class ScriptSandbox /// to resolve ctx.GetTag(...) calls. /// /// The concrete script context type to use for compilation. + /// The sandbox configuration for compiling scripts with the given context type. public static SandboxConfig Build(Type contextType) { if (contextType is null) throw new ArgumentNullException(nameof(contextType)); diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags/DependencyGraph.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags/DependencyGraph.cs index e655196e..52d7b890 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags/DependencyGraph.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags/DependencyGraph.cs @@ -156,6 +156,7 @@ public sealed class DependencyGraph /// dependencies. Throws if any cycle /// exists. Implemented via Kahn's algorithm. /// + /// A list of node IDs in topological evaluation order. public IReadOnlyList TopologicalSort() { // Kahn's framing: edge u -> v means "u must come before v". For dependencies, @@ -205,6 +206,7 @@ public sealed class DependencyGraph /// Empty list means the graph is a DAG. Useful for surfacing every cycle in one /// rejection pass so operators see all of them, not just one at a time. /// + /// A list of strongly-connected components that form cycles; empty if the graph is acyclic. public IReadOnlyList> DetectCycles() { // Iterative Tarjan's SCC. Avoids recursion so deep graphs don't StackOverflow. diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags/ITagUpstreamSource.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags/ITagUpstreamSource.cs index b6ba7612..dec9abd1 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags/ITagUpstreamSource.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags/ITagUpstreamSource.cs @@ -30,6 +30,7 @@ public interface ITagUpstreamSource /// when the path isn't configured. /// /// The tag path to read. + /// The last-known value and quality snapshot for the tag. DataValueSnapshot ReadTag(string path); /// @@ -40,5 +41,6 @@ public interface ITagUpstreamSource /// /// The tag path to subscribe to. /// The callback to invoke when the value changes. + /// An that cancels the subscription when disposed. IDisposable SubscribeTag(string path, Action observer); } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags/VirtualTagEngine.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags/VirtualTagEngine.cs index efd330fb..4cc929f6 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags/VirtualTagEngine.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags/VirtualTagEngine.cs @@ -198,6 +198,7 @@ public sealed class VirtualTagEngine : IDisposable /// default. Also called after a config reload. /// /// Cancellation token to stop evaluation. + /// A task that represents the asynchronous operation. public async Task EvaluateAllAsync(CancellationToken ct = default) { EnsureLoaded(); @@ -212,6 +213,7 @@ public sealed class VirtualTagEngine : IDisposable /// Evaluate a single tag — used by the timer trigger + test hooks. /// Path of the virtual tag to evaluate. /// Cancellation token to stop evaluation. + /// A task that represents the asynchronous operation. public Task EvaluateOneAsync(string path, CancellationToken ct = default) { EnsureLoaded(); @@ -226,6 +228,7 @@ public sealed class VirtualTagEngine : IDisposable /// evaluation result. /// /// Path of the tag to read. + /// The most recently cached value and quality for the tag path. public DataValueSnapshot Read(string path) { if (string.IsNullOrWhiteSpace(path)) @@ -242,6 +245,7 @@ public sealed class VirtualTagEngine : IDisposable /// /// Path of the tag to subscribe to. /// Callback invoked with the tag path and new value on each evaluation. + /// An that cancels the subscription when disposed. public IDisposable Subscribe(string path, Action observer) { // Race-safe pattern paired with Unsub.Dispose: if Unsub.Dispose removed the diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/AuthorizationDecision.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/AuthorizationDecision.cs index 7050f75c..b811aaf3 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/AuthorizationDecision.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/AuthorizationDecision.cs @@ -19,6 +19,7 @@ public sealed record AuthorizationDecision( public bool IsAllowed => Verdict == AuthorizationVerdict.Allow; /// Convenience constructor for the common "no grants matched" outcome. + /// An with and empty provenance. public static AuthorizationDecision NotGranted() => new(AuthorizationVerdict.NotGranted, []); /// Allow with the list of grants that matched. diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/IPermissionEvaluator.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/IPermissionEvaluator.cs index ea96aeba..5e8ae98c 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/IPermissionEvaluator.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/IPermissionEvaluator.cs @@ -22,5 +22,6 @@ public interface IPermissionEvaluator /// The user session containing resolved LDAP groups and roles. /// The OPC UA operation being requested. /// The node address scope being accessed. + /// An indicating whether the operation is allowed. AuthorizationDecision Authorize(UserAuthorizationState session, OpcUaOperation operation, NodeScope scope); } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/PermissionTrie.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/PermissionTrie.cs index fc82204a..bfd1a339 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/PermissionTrie.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/PermissionTrie.cs @@ -33,6 +33,7 @@ public sealed class PermissionTrie /// /// The node scope to match permissions for. /// The user's LDAP group memberships. + /// The list of grants that apply to the given scope for any of the session's LDAP groups. public IReadOnlyList CollectMatches(NodeScope scope, IEnumerable ldapGroups) { ArgumentNullException.ThrowIfNull(scope); diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/PermissionTrieBuilder.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/PermissionTrieBuilder.cs index 3e6fd838..a9cacec1 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/PermissionTrieBuilder.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/PermissionTrieBuilder.cs @@ -41,6 +41,7 @@ public static class PermissionTrieBuilder /// Core-011 production hazard. The callback fires only when /// is non-null (a null lookup is the explicit deterministic-test fallback mode). /// + /// An immutable for the given cluster and generation. public static PermissionTrie Build( string clusterId, long generationId, diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/PermissionTrieCache.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/PermissionTrieCache.cs index 47161e0a..f61a2bcc 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/PermissionTrieCache.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/PermissionTrieCache.cs @@ -34,6 +34,7 @@ public sealed class PermissionTrieCache /// Get the current-generation trie for a cluster; null when nothing installed. /// The cluster identifier. + /// The current-generation trie, or null if nothing is installed for the cluster. public PermissionTrie? GetTrie(string clusterId) { ArgumentException.ThrowIfNullOrWhiteSpace(clusterId); @@ -43,6 +44,7 @@ public sealed class PermissionTrieCache /// Get a specific (cluster, generation) trie; null if that pair isn't cached. /// The cluster identifier. /// The generation identifier. + /// The trie for the specified cluster and generation, or null if not cached. public PermissionTrie? GetTrie(string clusterId, long generationId) { if (!_byCluster.TryGetValue(clusterId, out var entry)) return null; @@ -51,6 +53,7 @@ public sealed class PermissionTrieCache /// The generation id the shortcut currently serves for a cluster. /// The cluster identifier. + /// The current generation ID, or null if no trie is installed for the cluster. public long? CurrentGenerationId(string clusterId) => _byCluster.TryGetValue(clusterId, out var entry) ? entry.Current.GenerationId : null; @@ -111,11 +114,13 @@ public sealed class PermissionTrieCache /// Creates a cluster entry from a single trie. /// The permission trie to create the entry from. + /// A new containing the single trie as the current generation. public static ClusterEntry FromSingle(PermissionTrie trie) => new(trie, new Dictionary { [trie.GenerationId] = trie }); /// Creates a new entry with an additional trie, updating current if it's newer. /// The new permission trie to add. + /// A new with the trie added and the current pointer updated if the new generation is newer. public ClusterEntry WithAdditional(PermissionTrie trie) { var next = new Dictionary(Tries) { [trie.GenerationId] = trie }; diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/TriePermissionEvaluator.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/TriePermissionEvaluator.cs index a92608cc..5b43b6ed 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/TriePermissionEvaluator.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/TriePermissionEvaluator.cs @@ -24,11 +24,7 @@ public sealed class TriePermissionEvaluator : IPermissionEvaluator _timeProvider = timeProvider ?? TimeProvider.System; } - /// Authorizes an operation against the user's session and node scope. - /// The user's authorization session. - /// The OPC UA operation to authorize. - /// The target node scope. - /// An authorization decision indicating whether the operation is allowed. + /// public AuthorizationDecision Authorize(UserAuthorizationState session, OpcUaOperation operation, NodeScope scope) { ArgumentNullException.ThrowIfNull(session); diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/UserAuthorizationState.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/UserAuthorizationState.cs index e811109c..54331342 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/UserAuthorizationState.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/UserAuthorizationState.cs @@ -64,6 +64,7 @@ public sealed record UserAuthorizationState /// whenever this is true. /// /// The current UTC time. + /// true when the state exceeds its maximum staleness ceiling. public bool IsStale(DateTime utcNow) => utcNow - MembershipResolvedUtc > AuthCacheMaxStaleness; /// @@ -72,6 +73,7 @@ public sealed record UserAuthorizationState /// call still evaluates against the cached memberships. /// /// The current UTC time. + /// true when a background refresh should be initiated but the current cached memberships are still usable. public bool NeedsRefresh(DateTime utcNow) => !IsStale(utcNow) && utcNow - MembershipResolvedUtc > MembershipFreshnessInterval; } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Hosting/DriverFactoryRegistry.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Hosting/DriverFactoryRegistry.cs index bbade74e..d5459fe0 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Hosting/DriverFactoryRegistry.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Hosting/DriverFactoryRegistry.cs @@ -63,6 +63,7 @@ public sealed class DriverFactoryRegistry /// missing-assembly deployment doesn't take down the whole server. /// /// The driver type to look up. + /// The registered factory delegate, or if no factory was registered for the type. public Func? TryGet(string driverType) { ArgumentException.ThrowIfNullOrWhiteSpace(driverType); @@ -75,6 +76,7 @@ public sealed class DriverFactoryRegistry /// case upstream; we don't double-surface that failure here. /// /// The driver type to look up. + /// The registered , or if the type is unknown. public DriverTier GetTier(string driverType) { ArgumentException.ThrowIfNullOrWhiteSpace(driverType); diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Hosting/DriverFactoryRegistryAdapter.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Hosting/DriverFactoryRegistryAdapter.cs index de24430e..fd44bb96 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Hosting/DriverFactoryRegistryAdapter.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Hosting/DriverFactoryRegistryAdapter.cs @@ -20,16 +20,13 @@ public sealed class DriverFactoryRegistryAdapter : IDriverFactory _registry = registry; } - /// Attempts to create a driver instance by type and configuration. - /// The driver type name. - /// The driver instance identifier. - /// The driver configuration as a JSON string. + /// public IDriver? TryCreate(string driverType, string driverInstanceId, string driverConfigJson) { var factory = _registry.TryGet(driverType); return factory?.Invoke(driverInstanceId, driverConfigJson); } - /// Gets the collection of supported driver type names. + /// public IReadOnlyCollection SupportedTypes => _registry.RegisteredTypes; } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Hosting/DriverHost.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Hosting/DriverHost.cs index 9c8dbd86..7bf283f5 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Hosting/DriverHost.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Hosting/DriverHost.cs @@ -21,6 +21,7 @@ public sealed class DriverHost : IAsyncDisposable /// Gets the health status of a registered driver. /// The driver instance identifier to query. + /// The driver health if the driver is registered; otherwise null. public DriverHealth? GetHealth(string driverInstanceId) { lock (_lock) @@ -33,6 +34,7 @@ public sealed class DriverHost : IAsyncDisposable /// startup. Returns null when the driver is not registered. /// /// The driver instance identifier to look up. + /// The driver instance if registered; otherwise null. public IDriver? GetDriver(string driverInstanceId) { lock (_lock) @@ -47,6 +49,7 @@ public sealed class DriverHost : IAsyncDisposable /// The driver instance to register. /// The configuration JSON for the driver. /// Cancellation token for the operation. + /// A task that represents the asynchronous operation. public async Task RegisterAsync(IDriver driver, string driverConfigJson, CancellationToken ct) { ArgumentNullException.ThrowIfNull(driver); @@ -70,6 +73,7 @@ public sealed class DriverHost : IAsyncDisposable /// Unregisters a driver and calls shutdown. /// The driver instance identifier to unregister. /// Cancellation token for the operation. + /// A task that represents the asynchronous operation. public async Task UnregisterAsync(string driverInstanceId, CancellationToken ct) { IDriver? driver; @@ -84,6 +88,7 @@ public sealed class DriverHost : IAsyncDisposable } /// Disposes the driver host and all registered drivers. + /// A value task that represents the asynchronous operation. public async ValueTask DisposeAsync() { List snapshot; diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Observability/DriverHealthReport.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Observability/DriverHealthReport.cs index bd4f0961..99309b9d 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Observability/DriverHealthReport.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Observability/DriverHealthReport.cs @@ -27,6 +27,7 @@ public static class DriverHealthReport { /// Compute the fleet-wide readiness verdict from per-driver states. /// The list of per-driver health snapshots to aggregate. + /// The fleet-wide derived from all driver states. public static ReadinessVerdict Aggregate(IReadOnlyList drivers) { ArgumentNullException.ThrowIfNull(drivers); @@ -54,6 +55,7 @@ public static class DriverHealthReport /// return per the Stream C.1 state matrix. /// /// The readiness verdict to map to HTTP status. + /// The HTTP status code (200 or 503) corresponding to the verdict. public static int HttpStatus(ReadinessVerdict verdict) => verdict switch { ReadinessVerdict.Healthy => 200, diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Observability/LogContextEnricher.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Observability/LogContextEnricher.cs index 97f34a83..7a7bb80b 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Observability/LogContextEnricher.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Observability/LogContextEnricher.cs @@ -22,6 +22,7 @@ public static class LogContextEnricher /// The driver type name. /// The driver capability being invoked. /// The correlation ID for tracing the call. + /// A scope that pops the pushed properties when disposed. public static IDisposable Push(string driverInstanceId, string driverType, DriverCapability capability, string correlationId) { ArgumentException.ThrowIfNullOrWhiteSpace(driverInstanceId); @@ -40,6 +41,7 @@ public static class LogContextEnricher /// 12-hex-char slice of a GUID — long enough for log correlation, short enough to /// scan visually. /// + /// A 12-character hex string suitable for log correlation. public static string NewCorrelationId() => Guid.NewGuid().ToString("N")[..12]; private sealed class CompositeScope : IDisposable diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core/OpcUa/EquipmentNodeWalker.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core/OpcUa/EquipmentNodeWalker.cs index 01da5665..6bda50e5 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core/OpcUa/EquipmentNodeWalker.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core/OpcUa/EquipmentNodeWalker.cs @@ -183,6 +183,7 @@ public static class EquipmentNodeWalker /// wants an opaque non-JSON reference. /// /// The tag configuration JSON or string. + /// The value of the FullName field from the JSON, or the raw string as a fallback. internal static string ExtractFullName(string tagConfig) { if (string.IsNullOrWhiteSpace(tagConfig)) return tagConfig; diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core/OpcUa/GenericDriverNodeManager.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core/OpcUa/GenericDriverNodeManager.cs index 6399075d..16e36612 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core/OpcUa/GenericDriverNodeManager.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core/OpcUa/GenericDriverNodeManager.cs @@ -49,6 +49,7 @@ public class GenericDriverNodeManager(IDriver driver) : IDisposable /// /// The address space builder to populate. /// The cancellation token. + /// A task that represents the asynchronous address space build operation. public async Task BuildAddressSpaceAsync(IAddressSpaceBuilder builder, CancellationToken ct) { ArgumentNullException.ThrowIfNull(builder); @@ -111,23 +112,15 @@ public class GenericDriverNodeManager(IDriver driver) : IDisposable IAddressSpaceBuilder inner, ConcurrentDictionary sinks) : IAddressSpaceBuilder { - /// Adds a folder to the address space. - /// The browse name of the folder node. - /// The display name of the folder node. + /// public IAddressSpaceBuilder Folder(string browseName, string displayName) => new CapturingBuilder(inner.Folder(browseName, displayName), sinks); - /// Adds a variable to the address space. - /// The browse name of the variable node. - /// The display name of the variable node. - /// Metadata describing the variable's data type and properties. + /// public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo attributeInfo) => new CapturingHandle(inner.Variable(browseName, displayName, attributeInfo), sinks); - /// Adds a property to the address space. - /// The browse name of the property node. - /// The OPC UA data type of the property. - /// The initial value of the property, or null. + /// public void AddProperty(string browseName, DriverDataType dataType, object? value) => inner.AddProperty(browseName, dataType, value); } @@ -136,11 +129,10 @@ public class GenericDriverNodeManager(IDriver driver) : IDisposable IVariableHandle inner, ConcurrentDictionary sinks) : IVariableHandle { - /// Gets the full reference for the variable. + /// public string FullReference => inner.FullReference; - /// Marks the variable as an alarm condition and registers its sink. - /// Configuration for the alarm condition. + /// public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) { var sink = inner.MarkAsAlarmCondition(info); diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/AlarmSurfaceInvoker.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/AlarmSurfaceInvoker.cs index ba2c6014..0368945e 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/AlarmSurfaceInvoker.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/AlarmSurfaceInvoker.cs @@ -59,6 +59,7 @@ public sealed class AlarmSurfaceInvoker /// /// The source node IDs to subscribe to. /// The cancellation token. + /// A task that resolves to one subscription handle per resolved host. public async Task> SubscribeAsync( IReadOnlyList sourceNodeIds, CancellationToken cancellationToken) @@ -89,6 +90,7 @@ public sealed class AlarmSurfaceInvoker /// /// The subscription handle to unsubscribe. /// The cancellation token. + /// A task that represents the asynchronous unsubscribe operation. public ValueTask UnsubscribeAsync(IAlarmSubscriptionHandle handle, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(handle); @@ -110,6 +112,7 @@ public sealed class AlarmSurfaceInvoker /// /// The alarm acknowledgement requests. /// The cancellation token. + /// A task that represents the asynchronous acknowledgement operation. public async Task AcknowledgeAsync( IReadOnlyList acknowledgements, CancellationToken cancellationToken) @@ -166,7 +169,7 @@ public sealed class AlarmSurfaceInvoker public IAlarmSubscriptionHandle Inner { get; } = inner; /// Gets the resolved host name. public string Host { get; } = host; - /// Gets the diagnostic ID from the inner handle. + /// public string DiagnosticId => Inner.DiagnosticId; } } diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/CapabilityInvoker.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/CapabilityInvoker.cs index 42fc65fe..0af92cb4 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/CapabilityInvoker.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/CapabilityInvoker.cs @@ -58,6 +58,7 @@ public sealed class CapabilityInvoker /// The host name for logging and status tracking. /// The async function to execute. /// Cancellation token for the operation. + /// The result produced by after executing through the pipeline. public async ValueTask ExecuteAsync( DriverCapability capability, string hostName, @@ -86,6 +87,7 @@ public sealed class CapabilityInvoker /// The host name for logging and status tracking. /// The async function to execute. /// Cancellation token for the operation. + /// A value task that represents the asynchronous operation. public async ValueTask ExecuteAsync( DriverCapability capability, string hostName, @@ -121,6 +123,7 @@ public sealed class CapabilityInvoker /// Whether the write operation is idempotent. /// The async function to execute. /// Cancellation token for the operation. + /// The result produced by after executing through the write pipeline. public async ValueTask ExecuteWriteAsync( string hostName, bool isIdempotent, diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/DriverResilienceOptionsParser.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/DriverResilienceOptionsParser.cs index 168d6677..19f983f5 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/DriverResilienceOptionsParser.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/DriverResilienceOptionsParser.cs @@ -50,6 +50,7 @@ public static class DriverResilienceOptionsParser /// The driver tier for default resilience options. /// The optional JSON configuration string to parse. /// An out parameter containing diagnostic information if parsing fails. + /// The effective resilience options; tier defaults when the JSON is null or malformed. public static DriverResilienceOptions ParseOrDefaults( DriverTier tier, string? resilienceConfigJson, diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/DriverResiliencePipelineBuilder.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/DriverResiliencePipelineBuilder.cs index 99fbcde7..eda8368d 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/DriverResiliencePipelineBuilder.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/DriverResiliencePipelineBuilder.cs @@ -54,6 +54,7 @@ public sealed class DriverResiliencePipelineBuilder /// /// Which capability surface is being called. /// Per-driver-instance options (tier + per-capability overrides). + /// The cached or newly created for the given key. public ResiliencePipeline GetOrCreate( string driverInstanceId, string hostName, diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/DriverResilienceStatusTracker.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/DriverResilienceStatusTracker.cs index 1e32128a..db8b4717 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/DriverResilienceStatusTracker.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/DriverResilienceStatusTracker.cs @@ -128,10 +128,12 @@ public sealed class DriverResilienceStatusTracker /// Snapshot of a specific (instance, host) pair; null if no counters recorded yet. /// The driver instance identifier. /// The host name. + /// The current for the pair, or if no counters have been recorded. public ResilienceStatusSnapshot? TryGet(string driverInstanceId, string hostName) => _status.TryGetValue(new StatusKey(driverInstanceId, hostName), out var snapshot) ? snapshot : null; /// Copy of every currently-tracked (instance, host, snapshot) triple. Safe under concurrent writes. + /// A snapshot list of all currently tracked driver instance and host resilience states. public IReadOnlyList<(string DriverInstanceId, string HostName, ResilienceStatusSnapshot Snapshot)> Snapshot() => _status.Select(kvp => (kvp.Key.DriverInstanceId, kvp.Key.HostName, kvp.Value)).ToList(); diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Stability/MemoryTracking.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Stability/MemoryTracking.cs index 9dad09ee..977a20d8 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core/Stability/MemoryTracking.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core/Stability/MemoryTracking.cs @@ -33,6 +33,7 @@ public sealed class MemoryTracking /// Tier-default multiplier/floor constants per decision #146. /// The driver tier. + /// A tuple with the growth multiplier and the minimum floor bytes for the specified tier. public static (int Multiplier, long FloorBytes) GetTierConstants(DriverTier tier) => tier switch { DriverTier.A => (Multiplier: 3, FloorBytes: 50L * 1024 * 1024), @@ -73,6 +74,7 @@ public sealed class MemoryTracking /// /// The current memory footprint in bytes. /// The current UTC time. + /// The classifying this sample against the soft/hard thresholds. public MemoryTrackingAction Sample(long footprintBytes, DateTime utcNow) { if (_phase == TrackingPhase.WarmingUp) diff --git a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Cli/AbCipCommandBase.cs b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Cli/AbCipCommandBase.cs index e5d9e42a..767d19be 100644 --- a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Cli/AbCipCommandBase.cs +++ b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Cli/AbCipCommandBase.cs @@ -60,6 +60,7 @@ public abstract class AbCipCommandBase : DriverCommandBase /// probe loop would race the operator's own reads. /// /// The list of tag definitions to include in the options. + /// A fully-configured with probe and alarm projection disabled. protected AbCipDriverOptions BuildOptions(IReadOnlyList tags) => new() { Devices = [new AbCipDeviceOptions( diff --git a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Cli/Commands/ReadCommand.cs b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Cli/Commands/ReadCommand.cs index 5bad9277..2c4e6837 100644 --- a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Cli/Commands/ReadCommand.cs +++ b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Cli/Commands/ReadCommand.cs @@ -66,6 +66,7 @@ public sealed class ReadCommand : AbCipCommandBase /// /// The symbolic tag path. /// The data type. + /// A combined tag-name string in path:type form. internal static string SynthesiseTagName(string tagPath, AbCipDataType type) => $"{tagPath}:{type}"; } diff --git a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Cli/Commands/ReadCommand.cs b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Cli/Commands/ReadCommand.cs index 51ac8ca1..cc293ac9 100644 --- a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Cli/Commands/ReadCommand.cs +++ b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Cli/Commands/ReadCommand.cs @@ -58,6 +58,7 @@ public sealed class ReadCommand : AbLegacyCommandBase /// Tag-name key the driver uses internally. Address+type is already unique. /// The PCCC file address. /// The data type of the address. + /// A combined tag name string in the form address:type. internal static string SynthesiseTagName(string address, AbLegacyDataType type) => $"{address}:{type}"; } diff --git a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Cli.Common/SnapshotFormatter.cs b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Cli.Common/SnapshotFormatter.cs index b44ffdc5..49e2e86c 100644 --- a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Cli.Common/SnapshotFormatter.cs +++ b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Cli.Common/SnapshotFormatter.cs @@ -23,6 +23,7 @@ public static class SnapshotFormatter /// /// The tag name to include in the output. /// The data value snapshot to format. + /// A multi-line string representation of the tag and its value. public static string Format(string tagName, DataValueSnapshot snapshot) { ArgumentNullException.ThrowIfNull(snapshot); @@ -42,6 +43,7 @@ public static class SnapshotFormatter /// /// The tag name to include in the output. /// The write result to format. + /// A single-line string showing the tag name and write status. public static string FormatWrite(string tagName, WriteResult result) { ArgumentNullException.ThrowIfNull(result); @@ -54,6 +56,7 @@ public static class SnapshotFormatter /// /// The list of tag names to include as rows. /// The list of data value snapshots to format. + /// An aligned table string with tag, value, status, and source-time columns. public static string FormatTable( IReadOnlyList tagNames, IReadOnlyList snapshots) { diff --git a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Cli/Commands/ReadCommand.cs b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Cli/Commands/ReadCommand.cs index c1a92581..d2577c6d 100644 --- a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Cli/Commands/ReadCommand.cs +++ b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Cli/Commands/ReadCommand.cs @@ -52,6 +52,7 @@ public sealed class ReadCommand : FocasCommandBase /// Constructs a tag name from address and data type. /// The FOCAS address. /// The data type. + /// A synthesized tag name string combining the address and data type. internal static string SynthesiseTagName(string address, FocasDataType type) => $"{address}:{type}"; } diff --git a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Cli/FocasCommandBase.cs b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Cli/FocasCommandBase.cs index cfa9957f..dc5553c8 100644 --- a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Cli/FocasCommandBase.cs +++ b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Cli/FocasCommandBase.cs @@ -47,6 +47,7 @@ public abstract class FocasCommandBase : DriverCommandBase /// as BadCommunicationError. /// /// The tag definitions to include in the driver options. + /// A configured with the CNC target and the supplied tag list. protected FocasDriverOptions BuildOptions(IReadOnlyList tags) => new() { Devices = [new FocasDeviceOptions( diff --git a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli/ModbusCommandBase.cs b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli/ModbusCommandBase.cs index d3441cc9..d8fd176c 100644 --- a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli/ModbusCommandBase.cs +++ b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli/ModbusCommandBase.cs @@ -48,6 +48,7 @@ public abstract class ModbusCommandBase : DriverCommandBase /// command against its own keep-alive reads. /// /// The tag definitions to include in the options. + /// A configured for a one-shot CLI run. protected ModbusDriverOptions BuildOptions(IReadOnlyList tags) => new() { Host = Host, diff --git a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.S7.Cli/Commands/ReadCommand.cs b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.S7.Cli/Commands/ReadCommand.cs index 2cda2f1e..4f3588a3 100644 --- a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.S7.Cli/Commands/ReadCommand.cs +++ b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.S7.Cli/Commands/ReadCommand.cs @@ -61,6 +61,7 @@ public sealed class ReadCommand : S7CommandBase /// Tag-name key used internally. Address + type is already unique. /// The S7 address to encode in the tag name. /// The data type to encode in the tag name. + /// The synthesised tag name encoding the address and type. internal static string SynthesiseTagName(string address, S7DataType type) => $"{address}:{type}"; } diff --git a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.S7.Cli/S7CommandBase.cs b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.S7.Cli/S7CommandBase.cs index ecf725f5..ead55c02 100644 --- a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.S7.Cli/S7CommandBase.cs +++ b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.S7.Cli/S7CommandBase.cs @@ -51,6 +51,7 @@ public abstract class S7CommandBase : DriverCommandBase /// disabled — CLI runs are one-shot. /// /// The tag definitions to include in the options. + /// An populated with the current command-line values and the supplied tags. protected S7DriverOptions BuildOptions(IReadOnlyList tags) => new() { Host = Host, diff --git a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli/Commands/BrowseCommand.cs b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli/Commands/BrowseCommand.cs index fc8bec30..5805f025 100644 --- a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli/Commands/BrowseCommand.cs +++ b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli/Commands/BrowseCommand.cs @@ -90,6 +90,7 @@ public sealed class BrowseCommand : TwinCATCommandBase /// /// The source collection to filter. /// The prefix to filter on, or null to keep everything. + /// A filtered list of variables whose browse names start with the given prefix. internal static List<(string BrowseName, DriverAttributeInfo Info)> FilterByPrefix( IReadOnlyList<(string BrowseName, DriverAttributeInfo Info)> source, string? prefix) => source @@ -102,6 +103,7 @@ public sealed class BrowseCommand : TwinCATCommandBase /// /// The number of matched items. /// The maximum number to show, or 0 for unbounded. + /// The effective print limit: when unbounded, otherwise the lesser of and . internal static int PrintLimit(int matchedCount, int max) => max <= 0 ? matchedCount : Math.Min(max, matchedCount); @@ -112,6 +114,7 @@ public sealed class BrowseCommand : TwinCATCommandBase /// authorization is enforced server-side. /// /// The attribute info to label. + /// "RO" for view-only attributes; "RW" for all others. internal static string AccessTag(DriverAttributeInfo info) => info.SecurityClass == SecurityClassification.ViewOnly ? "RO" : "RW"; diff --git a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli/Commands/WriteCommand.cs b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli/Commands/WriteCommand.cs index 8aa3a0cc..40df4ee5 100644 --- a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli/Commands/WriteCommand.cs +++ b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli/Commands/WriteCommand.cs @@ -69,6 +69,7 @@ public sealed class WriteCommand : TwinCATTagCommandBase /// Parse --value per , invariant culture. /// The raw string value to parse. /// The target TwinCAT data type. + /// The parsed value as a boxed .NET object matching the requested data type. internal static object ParseValue(string raw, TwinCATDataType type) => type switch { TwinCATDataType.Bool => ParseBool(raw), diff --git a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli/TwinCATTagCommandBase.cs b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli/TwinCATTagCommandBase.cs index 928a05d8..23a74907 100644 --- a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli/TwinCATTagCommandBase.cs +++ b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli/TwinCATTagCommandBase.cs @@ -24,6 +24,7 @@ public abstract class TwinCATTagCommandBase : TwinCATCommandBase /// native notifications toggled by . /// /// Tag definitions for the driver. + /// A configured for a single-device CLI run. protected TwinCATDriverOptions BuildOptions(IReadOnlyList tags) => new() { Devices = [new TwinCATDeviceOptions( @@ -39,6 +40,7 @@ public abstract class TwinCATTagCommandBase : TwinCATCommandBase // ---- Test hook ---- /// Test hook that exposes BuildOptions for unit testing. /// Tag definitions for the driver. + /// A configured for a single-device CLI run. internal TwinCATDriverOptions BuildOptionsForTest(IReadOnlyList tags) => BuildOptions(tags); } diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipDriver.cs index bb53fc21..af941676 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipDriver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipDriver.cs @@ -130,10 +130,10 @@ public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery, /// internal AbCipTemplateCache TemplateCache => _templateCache; - /// Gets the unique identifier for this driver instance. + /// public string DriverInstanceId => _driverInstanceId; - /// Gets the driver type identifier. + /// public string DriverType => "AbCip"; /// @@ -244,10 +244,7 @@ public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery, return Task.CompletedTask; } - /// Reinitialize the driver by shutting down and reinitializing with new configuration. - /// The new driver configuration as JSON. - /// Cancellation token for the operation. - /// A task representing the asynchronous reinitialization. + /// public async Task ReinitializeAsync(string driverConfigJson, CancellationToken cancellationToken) { await ShutdownAsync(cancellationToken).ConfigureAwait(false); @@ -305,19 +302,12 @@ public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery, // ---- ISubscribable (polling overlay via shared engine) ---- - /// Subscribe to value changes for the specified tag references. - /// The tag references to subscribe to. - /// The interval at which to publish changes. - /// Cancellation token for the operation. - /// A handle representing the subscription. + /// public Task SubscribeAsync( IReadOnlyList fullReferences, TimeSpan publishingInterval, CancellationToken cancellationToken) => Task.FromResult(_poll.Subscribe(fullReferences, publishingInterval)); - /// Unsubscribe from value changes using a subscription handle. - /// The subscription handle to unsubscribe. - /// Cancellation token for the operation. - /// A completed task. + /// public Task UnsubscribeAsync(ISubscriptionHandle handle, CancellationToken cancellationToken) { _poll.Unsubscribe(handle); @@ -349,19 +339,13 @@ public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery, return _alarmProjection.SubscribeAsync(sourceNodeIds, cancellationToken); } - /// Unsubscribe from alarm events. - /// The alarm subscription handle. - /// Cancellation token for the operation. - /// A completed task. + /// public Task UnsubscribeAlarmsAsync(IAlarmSubscriptionHandle handle, CancellationToken cancellationToken) => _options.EnableAlarmProjection ? _alarmProjection.UnsubscribeAsync(handle, cancellationToken) : Task.CompletedTask; - /// Acknowledge alarms. - /// The alarm acknowledgements to process. - /// Cancellation token for the operation. - /// A completed task. + /// public Task AcknowledgeAsync( IReadOnlyList acknowledgements, CancellationToken cancellationToken) => _options.EnableAlarmProjection @@ -370,8 +354,7 @@ public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery, // ---- IHostConnectivityProbe ---- - /// Gets the connectivity status of all configured devices. - /// A read-only list of host connectivity statuses. + /// public IReadOnlyList GetHostStatuses() => [.. _devices.Values.Select(s => new HostConnectivityStatus(s.Options.HostAddress, s.HostState, s.HostStateChangedUtc))]; @@ -873,8 +856,7 @@ public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery, } } - /// Gets the current health status of the driver. - /// The driver health information. + /// public DriverHealth GetHealth() => _health; /// @@ -885,9 +867,7 @@ public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery, /// The memory footprint in bytes. public long GetMemoryFootprint() => 0; - /// Flushes optional caches to free memory. - /// Cancellation token for the operation. - /// A completed task. + /// public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) { _templateCache.Clear(); diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipStatusMapper.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipStatusMapper.cs index c0fed449..601bcb1c 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipStatusMapper.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipStatusMapper.cs @@ -48,6 +48,7 @@ public static class AbCipStatusMapper /// Map a CIP general-status byte to an OPC UA StatusCode. /// The CIP general-status byte value. + /// The corresponding OPC UA StatusCode. public static uint MapCipGeneralStatus(byte status) => status switch { 0x00 => Good, @@ -72,6 +73,7 @@ public static class AbCipStatusMapper /// operation; every other (negative) member is an error. /// /// The libplctag status code as an integer. + /// The corresponding OPC UA StatusCode. public static uint MapLibplctagStatus(int status) => MapLibplctagStatus((Status)status); /// @@ -80,6 +82,7 @@ public static class AbCipStatusMapper /// seam, which returns the boxed-as-int value. /// /// The libplctag Status enum value. + /// The corresponding OPC UA StatusCode. public static uint MapLibplctagStatus(Status status) => status switch { Status.Ok => Good, diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipSystemTagFilter.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipSystemTagFilter.cs index ed21a36b..a3fafc74 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipSystemTagFilter.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipSystemTagFilter.cs @@ -19,6 +19,7 @@ public static class AbCipSystemTagFilter /// always preserved case and the system-tag prefixes are uppercase by convention. /// /// The tag name to check. + /// if the tag is a system tag that should be hidden; otherwise . public static bool IsSystemTag(string tagName) { if (string.IsNullOrWhiteSpace(tagName)) return true; diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipTagPath.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipTagPath.cs index 2f77b24a..4d1a85d2 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipTagPath.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipTagPath.cs @@ -24,6 +24,7 @@ public sealed record AbCipTagPath( int? BitIndex) { /// Rebuild the canonical Logix tag string. + /// The canonical Logix tag string suitable for the libplctag name= attribute. public string ToLibplctagName() { var buf = new System.Text.StringBuilder(); diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipTemplateCache.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipTemplateCache.cs index 369c45d2..2d416645 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipTemplateCache.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipTemplateCache.cs @@ -23,6 +23,7 @@ public sealed class AbCipTemplateCache /// /// The device host address and port. /// The template instance ID. + /// The cached , or null if not yet populated. public AbCipUdtShape? TryGet(string deviceHostAddress, uint templateInstanceId) => _shapes.TryGetValue((deviceHostAddress, templateInstanceId), out var shape) ? shape : null; diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipUdtMemberLayout.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipUdtMemberLayout.cs index 543d96ba..74fef203 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipUdtMemberLayout.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipUdtMemberLayout.cs @@ -30,6 +30,7 @@ public static class AbCipUdtMemberLayout /// if any member type is unsupported for declaration-only layout. /// /// The list of UDT member declarations. + /// A dictionary mapping member name to byte offset, or if any member type is unsupported. public static IReadOnlyDictionary? TryBuild( IReadOnlyList members) { diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipUdtReadPlanner.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipUdtReadPlanner.cs index 62cef0aa..3803c8f2 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipUdtReadPlanner.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/AbCipUdtReadPlanner.cs @@ -28,6 +28,7 @@ public static class AbCipUdtReadPlanner /// The list of tag references to read. /// Dictionary mapping tag names to their definitions. /// Whether to enable UDT member grouping based on declaration order. + /// An partitioning requests into whole-UDT groups and per-tag fallbacks. public static AbCipUdtReadPlan Build( IReadOnlyList requests, IReadOnlyDictionary tagsByName, diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/IAbCipTagEnumerator.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/IAbCipTagEnumerator.cs index 4f187b39..c79a7fd1 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/IAbCipTagEnumerator.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/IAbCipTagEnumerator.cs @@ -15,6 +15,7 @@ public interface IAbCipTagEnumerator : IDisposable /// /// Parameters for creating device tags. /// Cancellation token. + /// An async enumerable of discovered tags. IAsyncEnumerable EnumerateAsync( AbCipTagCreateParams deviceParams, CancellationToken cancellationToken); @@ -26,6 +27,7 @@ public interface IAbCipTagEnumeratorFactory /// /// Creates a new tag enumerator instance. /// + /// A new instance. IAbCipTagEnumerator Create(); } @@ -59,6 +61,7 @@ internal sealed class EmptyAbCipTagEnumerator : IAbCipTagEnumerator /// /// Parameters for creating device tags. /// Cancellation token. + /// An empty async enumerable of discovered tags. public async IAsyncEnumerable EnumerateAsync( AbCipTagCreateParams deviceParams, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken) @@ -79,5 +82,6 @@ internal sealed class EmptyAbCipTagEnumeratorFactory : IAbCipTagEnumeratorFactor /// /// Creates a new empty tag enumerator. /// + /// A new instance. public IAbCipTagEnumerator Create() => new EmptyAbCipTagEnumerator(); } diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/IAbCipTagRuntime.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/IAbCipTagRuntime.cs index 536ec6fc..2876966b 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/IAbCipTagRuntime.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/IAbCipTagRuntime.cs @@ -11,20 +11,24 @@ public interface IAbCipTagRuntime : IDisposable { /// Create the underlying native tag (equivalent to libplctag's plc_tag_create). /// Cancellation token. + /// A task that represents the asynchronous operation. Task InitializeAsync(CancellationToken cancellationToken); /// Issue a read; on completion the local buffer holds the current PLC value. /// Cancellation token. + /// A task that represents the asynchronous operation. Task ReadAsync(CancellationToken cancellationToken); /// Flush the local buffer to the PLC. /// Cancellation token. + /// A task that represents the asynchronous operation. Task WriteAsync(CancellationToken cancellationToken); /// /// Raw libplctag status code — mapped to an OPC UA StatusCode via /// . Zero on success, negative on error. /// + /// The raw libplctag status integer; zero on success, negative on error. int GetStatus(); /// @@ -34,6 +38,7 @@ public interface IAbCipTagRuntime : IDisposable /// /// CIP data type to decode. /// Bit index for BOOL-within-DINT extraction, or null. + /// The decoded .NET value, or null if the buffer cannot be decoded for the given type. object? DecodeValue(AbCipDataType type, int? bitIndex); /// @@ -48,6 +53,7 @@ public interface IAbCipTagRuntime : IDisposable /// CIP data type to decode. /// Byte offset in the buffer. /// Bit index for BOOL-within-DINT extraction, or null. + /// The decoded .NET value at the specified offset, or null if the offset is unsupported. object? DecodeValueAt(AbCipDataType type, int offset, int? bitIndex); /// @@ -68,6 +74,7 @@ public interface IAbCipTagFactory { /// Creates a tag runtime handle from the specified creation parameters. /// Parameters needed to create the tag runtime. + /// A new instance for the specified tag. IAbCipTagRuntime Create(AbCipTagCreateParams createParams); } diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/IAbCipTemplateReader.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/IAbCipTemplateReader.cs index 993db768..8309d8b1 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/IAbCipTemplateReader.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/IAbCipTemplateReader.cs @@ -16,6 +16,7 @@ public interface IAbCipTemplateReader : IDisposable /// The device connection parameters. /// The template instance ID to read. /// Token to cancel the operation. + /// The raw template blob bytes. Task ReadAsync( AbCipTagCreateParams deviceParams, uint templateInstanceId, @@ -26,5 +27,6 @@ public interface IAbCipTemplateReader : IDisposable public interface IAbCipTemplateReaderFactory { /// Creates a new template reader instance. + /// A new instance. IAbCipTemplateReader Create(); } diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/LibplctagTagEnumerator.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/LibplctagTagEnumerator.cs index 26b241a4..99269714 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/LibplctagTagEnumerator.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/LibplctagTagEnumerator.cs @@ -21,10 +21,7 @@ internal sealed class LibplctagTagEnumerator : IAbCipTagEnumerator { private Tag? _tag; - /// Enumerates all tags in the controller symbol table. - /// Device connection parameters including gateway and path. - /// Cancellation token for the enumeration. - /// An async enumerable of discovered tags. + /// public async IAsyncEnumerable EnumerateAsync( AbCipTagCreateParams deviceParams, [EnumeratorCancellation] CancellationToken cancellationToken) @@ -64,7 +61,6 @@ internal sealed class LibplctagTagEnumerator : IAbCipTagEnumerator /// Factory for . internal sealed class LibplctagTagEnumeratorFactory : IAbCipTagEnumeratorFactory { - /// Creates a new libplctag-based tag enumerator. - /// A new tag enumerator instance. + /// public IAbCipTagEnumerator Create() => new LibplctagTagEnumerator(); } diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/LibplctagTagRuntime.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/LibplctagTagRuntime.cs index 3f3f46ca..f34dd8ee 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/LibplctagTagRuntime.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/LibplctagTagRuntime.cs @@ -36,36 +36,22 @@ internal sealed class LibplctagTagRuntime : IAbCipTagRuntime // negotiates with — Driver.AbCip-013. } - /// Initializes the tag asynchronously. - /// The cancellation token. - /// A task representing the asynchronous initialization. + /// public Task InitializeAsync(CancellationToken cancellationToken) => _tag.InitializeAsync(cancellationToken); - /// Reads the tag value asynchronously. - /// The cancellation token. - /// A task representing the asynchronous read operation. + /// public Task ReadAsync(CancellationToken cancellationToken) => _tag.ReadAsync(cancellationToken); - /// Writes the tag value asynchronously. - /// The cancellation token. - /// A task representing the asynchronous write operation. + /// public Task WriteAsync(CancellationToken cancellationToken) => _tag.WriteAsync(cancellationToken); - /// Gets the current status of the tag. - /// The tag status as an integer. + /// public int GetStatus() => (int)_tag.GetStatus(); - /// Decodes the tag value with the specified data type. - /// The data type to decode. - /// The bit index for bit-level access, if applicable. - /// The decoded value. + /// public object? DecodeValue(AbCipDataType type, int? bitIndex) => DecodeValueAt(type, 0, bitIndex); - /// Decodes the tag value at the specified offset with the specified data type. - /// The data type to decode. - /// The byte offset within the tag buffer. - /// The bit index for bit-level access, if applicable. - /// The decoded value. + /// public object? DecodeValueAt(AbCipDataType type, int offset, int? bitIndex) => type switch { AbCipDataType.Bool => bitIndex is int bit @@ -87,10 +73,7 @@ internal sealed class LibplctagTagRuntime : IAbCipTagRuntime _ => null, }; - /// Encodes the specified value to the tag with the specified data type. - /// The data type to encode. - /// The bit index for bit-level access, if applicable. - /// The value to encode. + /// public void EncodeValue(AbCipDataType type, int? bitIndex, object? value) { switch (type) @@ -177,9 +160,7 @@ internal sealed class LibplctagTagRuntime : IAbCipTagRuntime /// Default implementation of IAbCipTagFactory that creates LibplctagTagRuntime instances. internal sealed class LibplctagTagFactory : IAbCipTagFactory { - /// Creates a new tag runtime with the specified creation parameters. - /// The parameters for creating the tag. - /// A new IAbCipTagRuntime instance. + /// public IAbCipTagRuntime Create(AbCipTagCreateParams createParams) => new LibplctagTagRuntime(createParams); } diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/LibplctagTemplateReader.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/LibplctagTemplateReader.cs index bd687f2e..314c6de6 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/LibplctagTemplateReader.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/LibplctagTemplateReader.cs @@ -25,11 +25,7 @@ internal sealed class LibplctagTemplateReader : IAbCipTemplateReader { private Tag? _tag; - /// Reads a template object from the PLC asynchronously. - /// The device connection parameters. - /// The template instance ID to read. - /// Cancellation token for the operation. - /// A task representing the asynchronous read operation. + /// public async Task ReadAsync( AbCipTagCreateParams deviceParams, uint templateInstanceId, @@ -50,7 +46,7 @@ internal sealed class LibplctagTemplateReader : IAbCipTemplateReader return _tag.GetBuffer(); } - /// + /// Disposes the underlying libplctag Tag instance. public void Dispose() => _tag?.Dispose(); private static PlcType MapPlcType(string attribute) => attribute switch @@ -64,7 +60,6 @@ internal sealed class LibplctagTemplateReader : IAbCipTemplateReader internal sealed class LibplctagTemplateReaderFactory : IAbCipTemplateReaderFactory { - /// Creates a new instance of the libplctag template reader. - /// A new instance of . + /// public IAbCipTemplateReader Create() => new LibplctagTemplateReader(); } diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/PlcFamilies/AbCipPlcFamilyProfile.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/PlcFamilies/AbCipPlcFamilyProfile.cs index 0c5c3495..9d5460cf 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/PlcFamilies/AbCipPlcFamilyProfile.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip/PlcFamilies/AbCipPlcFamilyProfile.cs @@ -21,6 +21,7 @@ public sealed record AbCipPlcFamilyProfile( { /// Look up the profile for a configured family. /// The PLC family to look up the profile for. + /// The profile for the specified PLC family. public static AbCipPlcFamilyProfile ForFamily(AbCipPlcFamily family) => family switch { AbCipPlcFamily.ControlLogix => ControlLogix, diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Contracts/AbLegacyPlcFamilyProfile.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Contracts/AbLegacyPlcFamilyProfile.cs index 8fef30e6..81d3d21b 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Contracts/AbLegacyPlcFamilyProfile.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Contracts/AbLegacyPlcFamilyProfile.cs @@ -13,6 +13,7 @@ public sealed record AbLegacyPlcFamilyProfile( { /// Gets the profile for the specified PLC family. /// The PLC family. + /// The for the specified family. public static AbLegacyPlcFamilyProfile ForFamily(AbLegacyPlcFamily family) => family switch { AbLegacyPlcFamily.Slc500 => Slc500, diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy/AbLegacyAddress.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy/AbLegacyAddress.cs index a509d951..2422885b 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy/AbLegacyAddress.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy/AbLegacyAddress.cs @@ -33,6 +33,7 @@ public sealed record AbLegacyAddress( string? SubElement) { /// Converts the address to the libplctag library address format. + /// The libplctag name= attribute string for this address. public string ToLibplctagName() { var file = FileNumber is null ? FileLetter : $"{FileLetter}{FileNumber}"; @@ -44,6 +45,7 @@ public sealed record AbLegacyAddress( /// Attempts to parse a string into an AB legacy address. /// The address string to parse. + /// A parsed , or if the input is invalid. public static AbLegacyAddress? TryParse(string? value) { if (string.IsNullOrWhiteSpace(value)) return null; diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy/IAbLegacyTagRuntime.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy/IAbLegacyTagRuntime.cs index 7f1921fa..aca0d9b5 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy/IAbLegacyTagRuntime.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy/IAbLegacyTagRuntime.cs @@ -9,22 +9,27 @@ public interface IAbLegacyTagRuntime : IDisposable { /// Initializes the tag runtime. /// Cancellation token for the operation. + /// A task that represents the asynchronous operation. Task InitializeAsync(CancellationToken cancellationToken); /// Reads the current value of the tag. /// Cancellation token for the operation. + /// A task that represents the asynchronous operation. Task ReadAsync(CancellationToken cancellationToken); /// Writes the encoded value to the tag. /// Cancellation token for the operation. + /// A task that represents the asynchronous operation. Task WriteAsync(CancellationToken cancellationToken); /// Gets the current status of the tag operation. + /// The status code of the last tag operation. int GetStatus(); /// Decodes the tag value according to the specified data type. /// The data type to decode. /// Optional bit index for bit-level access. + /// The decoded value, or null if the value cannot be decoded. object? DecodeValue(AbLegacyDataType type, int? bitIndex); /// Encodes a value for writing to the tag. @@ -38,6 +43,7 @@ public interface IAbLegacyTagFactory { /// Creates a tag runtime instance with the specified parameters. /// The tag creation parameters. + /// A new tag runtime instance. IAbLegacyTagRuntime Create(AbLegacyTagCreateParams createParams); } diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy/LibplctagLegacyTagRuntime.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy/LibplctagLegacyTagRuntime.cs index 7caf3c50..d5b3cf3e 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy/LibplctagLegacyTagRuntime.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy/LibplctagLegacyTagRuntime.cs @@ -96,7 +96,7 @@ internal sealed class LibplctagLegacyTagRuntime : IAbLegacyTagRuntime } } - /// + /// Disposes the underlying libplctag tag and releases associated resources. public void Dispose() => _tag.Dispose(); private static PlcType MapPlcType(string attribute) => attribute switch @@ -111,9 +111,7 @@ internal sealed class LibplctagLegacyTagRuntime : IAbLegacyTagRuntime internal sealed class LibplctagLegacyTagFactory : IAbLegacyTagFactory { - /// Creates a new libplctag-backed tag runtime instance. - /// The parameters for tag creation. - /// A new tag runtime instance. + /// public IAbLegacyTagRuntime Create(AbLegacyTagCreateParams createParams) => new LibplctagLegacyTagRuntime(createParams); } diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/FocasDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/FocasDriver.cs index fefc713a..56792f1d 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/FocasDriver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/FocasDriver.cs @@ -65,15 +65,12 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery, OnDataChange?.Invoke(this, new DataChangeEventArgs(handle, tagRef, snapshot))); } - /// Gets the driver instance identifier. + /// public string DriverInstanceId => _driverInstanceId; - /// Gets the driver type name. + /// public string DriverType => "FOCAS"; - /// Initializes the driver with configuration and prepares device connections and polling. - /// JSON configuration string for the driver. - /// Cancellation token for the operation. - /// A task representing the asynchronous initialization operation. + /// public Task InitializeAsync(string driverConfigJson, CancellationToken cancellationToken) { Volatile.Write(ref _health, new DriverHealth(DriverState.Initializing, null, null)); @@ -156,19 +153,14 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery, return Task.CompletedTask; } - /// Reinitializes the driver by shutting down and restarting with new configuration. - /// JSON configuration string for the driver. - /// Cancellation token for the operation. - /// A task representing the asynchronous reinitialization operation. + /// public async Task ReinitializeAsync(string driverConfigJson, CancellationToken cancellationToken) { await ShutdownAsync(cancellationToken).ConfigureAwait(false); await InitializeAsync(driverConfigJson, cancellationToken).ConfigureAwait(false); } - /// Shuts down the driver, cancelling all running operations and releasing resources. - /// Cancellation token for the operation. - /// A task representing the asynchronous shutdown operation. + /// public async Task ShutdownAsync(CancellationToken cancellationToken) { await _poll.DisposeAsync().ConfigureAwait(false); @@ -202,13 +194,11 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery, Volatile.Write(ref _health, new DriverHealth(DriverState.Unknown, Volatile.Read(ref _health).LastSuccessfulRead, null)); } - /// Gets the current health status of the driver. + /// public DriverHealth GetHealth() => Volatile.Read(ref _health); - /// Gets the current memory footprint of the driver. + /// public long GetMemoryFootprint() => 0; - /// Flushes optional internal caches. - /// Cancellation token for the operation. - /// A task representing the asynchronous cache flush operation. + /// public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; /// Gets the number of configured devices. @@ -224,10 +214,7 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery, // ---- IReadable ---- - /// Reads values from one or more tags asynchronously. - /// A read-only list of tag references to read. - /// Cancellation token for the operation. - /// A task representing the asynchronous read operation. + /// public async Task> ReadAsync( IReadOnlyList fullReferences, CancellationToken cancellationToken) { @@ -294,10 +281,7 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery, // ---- IWritable ---- - /// Writes values to one or more tags asynchronously. - /// A read-only list of write requests. - /// Cancellation token for the operation. - /// A task representing the asynchronous write operation. + /// public async Task> WriteAsync( IReadOnlyList writes, CancellationToken cancellationToken) { @@ -363,10 +347,7 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery, // ---- ITagDiscovery ---- - /// Discovers tags and builds the OPC UA address space asynchronously. - /// The address space builder for constructing the OPC UA namespace. - /// Cancellation token for the operation. - /// A task representing the asynchronous discovery operation. + /// public Task DiscoverAsync(IAddressSpaceBuilder builder, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(builder); @@ -543,19 +524,12 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery, // ---- ISubscribable (polling overlay via shared engine) ---- - /// Subscribes to data changes on one or more tags. - /// A read-only list of tag references to subscribe to. - /// The interval at which to publish data changes. - /// Cancellation token for the operation. - /// A task representing the asynchronous subscription operation. + /// public Task SubscribeAsync( IReadOnlyList fullReferences, TimeSpan publishingInterval, CancellationToken cancellationToken) => Task.FromResult(_poll.Subscribe(fullReferences, publishingInterval)); - /// Unsubscribes from a previous subscription. - /// The subscription handle to unsubscribe from. - /// Cancellation token for the operation. - /// A task representing the asynchronous unsubscription operation. + /// public Task UnsubscribeAsync(ISubscriptionHandle handle, CancellationToken cancellationToken) { _poll.Unsubscribe(handle); @@ -564,7 +538,7 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery, // ---- IHostConnectivityProbe ---- - /// Gets the connectivity status of all configured devices. + /// public IReadOnlyList GetHostStatuses() => [.. _devices.Values.Select(s => new HostConnectivityStatus(s.Options.HostAddress, s.HostState, s.HostStateChangedUtc))]; @@ -970,10 +944,7 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery, // ---- IAlarmSource ---- - /// Subscribes to alarm events from the driver. - /// A read-only list of source node IDs to subscribe to. - /// Cancellation token for the operation. - /// A task representing the asynchronous subscription operation. + /// public Task SubscribeAlarmsAsync( IReadOnlyList sourceNodeIds, CancellationToken cancellationToken) { @@ -983,17 +954,11 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery, return _alarmProjection.SubscribeAsync(sourceNodeIds, cancellationToken); } - /// Unsubscribes from a previous alarm subscription. - /// The alarm subscription handle to unsubscribe from. - /// Cancellation token for the operation. - /// A task representing the asynchronous unsubscription operation. + /// public Task UnsubscribeAlarmsAsync(IAlarmSubscriptionHandle handle, CancellationToken cancellationToken) => _alarmProjection is { } p ? p.UnsubscribeAsync(handle, cancellationToken) : Task.CompletedTask; - /// Acknowledges one or more alarms. - /// A read-only list of alarm acknowledgement requests. - /// Cancellation token for the operation. - /// A task representing the asynchronous acknowledgement operation. + /// public Task AcknowledgeAsync( IReadOnlyList acknowledgements, CancellationToken cancellationToken) => _alarmProjection is { } p ? p.AcknowledgeAsync(acknowledgements, cancellationToken) : Task.CompletedTask; @@ -1035,9 +1000,7 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery, // ---- IPerCallHostResolver ---- - /// Resolves the host address for a given tag reference. - /// The full reference of the tag. - /// The host address for the tag reference. + /// public string ResolveHost(string fullReference) { if (_tagsByName.TryGetValue(fullReference, out var def)) @@ -1077,6 +1040,7 @@ public sealed class FocasDriver : IDriver, IReadable, IWritable, ITagDiscovery, /// Disposes the driver and releases all resources synchronously. public void Dispose() => DisposeAsync().AsTask().GetAwaiter().GetResult(); /// Disposes the driver and releases all resources asynchronously. + /// A task that represents the asynchronous dispose operation. public async ValueTask DisposeAsync() => await ShutdownAsync(CancellationToken.None).ConfigureAwait(false); /// diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/IFocasClient.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/IFocasClient.cs index dbc46cef..21c79e38 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/IFocasClient.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/IFocasClient.cs @@ -20,6 +20,7 @@ public interface IFocasClient : IDisposable /// The CNC host address and port. /// The connection timeout duration. /// The cancellation token. + /// A task that represents the asynchronous connect operation. Task ConnectAsync(FocasHostAddress address, TimeSpan timeout, CancellationToken cancellationToken); /// True when the FWLIB handle is valid + the socket is up. @@ -33,6 +34,7 @@ public interface IFocasClient : IDisposable /// The CNC memory address to read from. /// The FOCAS data type to read. /// The cancellation token. + /// A task that resolves to the read value and its OPC UA status code. Task<(object? value, uint status)> ReadAsync( FocasAddress address, FocasDataType type, @@ -46,6 +48,7 @@ public interface IFocasClient : IDisposable /// The FOCAS data type to write. /// The value to write. /// The cancellation token. + /// A task that resolves to the mapped OPC UA status code (0 = Good). Task WriteAsync( FocasAddress address, FocasDataType type, @@ -57,6 +60,7 @@ public interface IFocasClient : IDisposable /// responds with any valid status. /// /// The cancellation token. + /// A task that resolves to true when the CNC responds; otherwise false. Task ProbeAsync(CancellationToken cancellationToken); /// @@ -66,6 +70,7 @@ public interface IFocasClient : IDisposable /// emits transitions (raise / clear) on the driver's OnAlarmEvent. /// /// The cancellation token. + /// A task that resolves to the list of currently active alarms. Task> ReadAlarmsAsync(CancellationToken cancellationToken); // ---- Fixed-tree T1 (identity + axis discovery + fast-poll dynamic bundle) ---- @@ -76,6 +81,7 @@ public interface IFocasClient : IDisposable /// values don't change across the session. /// /// The cancellation token. + /// A task that resolves to the CNC system information snapshot. Task GetSysInfoAsync(CancellationToken cancellationToken); /// @@ -84,6 +90,7 @@ public interface IFocasClient : IDisposable /// calls. /// /// The cancellation token. + /// A task that resolves to the list of configured axis names. Task> GetAxisNamesAsync(CancellationToken cancellationToken); /// @@ -91,6 +98,7 @@ public interface IFocasClient : IDisposable /// the Spindle/{name}/ subtree. /// /// The cancellation token. + /// A task that resolves to the list of configured spindle names. Task> GetSpindleNamesAsync(CancellationToken cancellationToken); /// @@ -101,6 +109,7 @@ public interface IFocasClient : IDisposable /// /// The axis index to read dynamics for. /// The cancellation token. + /// A task that resolves to the dynamic snapshot for the specified axis. Task ReadDynamicAsync(int axisIndex, CancellationToken cancellationToken); // ---- Fixed-tree T2 (program + operation mode) ---- @@ -113,6 +122,7 @@ public interface IFocasClient : IDisposable /// on human-operator timescales. /// /// The cancellation token. + /// A task that resolves to the program and operation-mode snapshot. Task GetProgramInfoAsync(CancellationToken cancellationToken); // ---- Fixed-tree T3 (timers) ---- @@ -124,6 +134,7 @@ public interface IFocasClient : IDisposable /// /// The timer kind to read. /// The cancellation token. + /// A task that resolves to the requested cumulative timer reading. Task GetTimerAsync(FocasTimerKind kind, CancellationToken cancellationToken); // ---- Fixed-tree T3.5 (servo meters) ---- @@ -134,6 +145,7 @@ public interface IFocasClient : IDisposable /// disconnected session or unsupported CNC. /// /// The cancellation token. + /// A task that resolves to the list of servo-load meter readings. Task> GetServoLoadsAsync(CancellationToken cancellationToken); // ---- Fixed-tree T3.6 (spindle meters) ---- @@ -145,6 +157,7 @@ public interface IFocasClient : IDisposable /// series like 16i may return EW_FUNC). /// /// The cancellation token. + /// A task that resolves to the list of per-spindle load percentages. Task> GetSpindleLoadsAsync(CancellationToken cancellationToken); /// @@ -152,6 +165,7 @@ public interface IFocasClient : IDisposable /// bootstrap. Index alignment as per . /// /// The cancellation token. + /// A task that resolves to the list of per-spindle maximum RPM values. Task> GetSpindleMaxRpmsAsync(CancellationToken cancellationToken); } @@ -292,9 +306,7 @@ public interface IFocasClientFactory /// public sealed class UnimplementedFocasClientFactory : IFocasClientFactory { - /// Creates a new client instance (always throws NotSupportedException). - /// Never returns; always throws NotSupportedException. - /// Always thrown to indicate backend is not yet provisioned. + /// public IFocasClient Create() => throw new NotSupportedException( "FOCAS driver backend is 'unimplemented'. Switch to 'Backend: \"wire\"' in driver config " + "once the CNC is provisioned — see docs/drivers/FOCAS.md."); diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/Wire/FocasWireClient.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/Wire/FocasWireClient.cs index 390a9bdc..d97fb083 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/Wire/FocasWireClient.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/Wire/FocasWireClient.cs @@ -70,6 +70,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// The FOCAS/2 TCP port (typically 8193). /// Connection timeout in seconds; zero or negative disables the timeout. /// Cancellation token for the connect operation. + /// A task that represents the asynchronous operation. public Task ConnectAsync( string host, int port, @@ -90,6 +91,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// The FOCAS/2 TCP port (typically 8193). /// Connection timeout duration; disables the timeout. /// Cancellation token for the connect operation. + /// A task that represents the asynchronous operation. public Task ConnectAsync( string host, int port, @@ -183,6 +185,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Async dispose — sends the close PDU when connected and tears down both sockets. /// Idempotent. /// + /// A value task that represents the asynchronous operation. public async ValueTask DisposeAsync() { await _lifetimeGate.WaitAsync(CancellationToken.None).ConfigureAwait(false); @@ -218,6 +221,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the CNC system info result. public async Task> ReadSysInfoAsync( CancellationToken cancellationToken = default, TimeSpan? timeout = null, @@ -233,6 +237,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the CNC status result. public async Task> ReadStatusAsync( CancellationToken cancellationToken = default, TimeSpan? timeout = null, @@ -272,6 +277,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the list of axis name records. public async Task>> ReadAxisNamesAsync( short maxCount = 32, CancellationToken cancellationToken = default, @@ -288,6 +294,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the list of spindle name records. public async Task>> ReadSpindleNamesAsync( short maxCount = 8, CancellationToken cancellationToken = default, @@ -308,6 +315,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the dynamic axis data result. public async Task> ReadDynamic2Async( short axis = 1, CancellationToken cancellationToken = default, @@ -353,6 +361,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the list of servo meter readings. public async Task>> ReadServoMeterAsync( short maxCount = 32, CancellationToken cancellationToken = default, @@ -390,6 +399,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the list of spindle load metrics. public Task>> ReadSpindleLoadAsync( short spindleSelector = -1, CancellationToken cancellationToken = default, @@ -402,6 +412,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the list of spindle maximum RPM metrics. public Task>> ReadSpindleMaxRpmAsync( short spindleSelector = -1, CancellationToken cancellationToken = default, @@ -419,6 +430,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the raw parameter bytes result. public async Task> ReadParameterBytesAsync( short dataNumber, short axis = 0, @@ -438,6 +450,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the typed parameter result. public async Task> ReadParameterAsync( short dataNumber, short type = 0, @@ -458,6 +471,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the byte parameter result. public async Task> ReadParameterByteAsync(short dataNumber, short axis = 0, CancellationToken cancellationToken = default, TimeSpan? timeout = null, ushort? pathId = null) { var result = await ReadParameterBytesAsync(dataNumber, axis, cancellationToken, timeout, pathId).ConfigureAwait(false); @@ -472,6 +486,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the 16-bit integer parameter result. public async Task> ReadParameterInt16Async(short dataNumber, short axis = 0, CancellationToken cancellationToken = default, TimeSpan? timeout = null, ushort? pathId = null) { var result = await ReadParameterBytesAsync(dataNumber, axis, cancellationToken, timeout, pathId).ConfigureAwait(false); @@ -486,6 +501,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the 32-bit integer parameter result. public async Task> ReadParameterInt32Async(short dataNumber, short axis = 0, CancellationToken cancellationToken = default, TimeSpan? timeout = null, ushort? pathId = null) { var result = await ReadParameterBytesAsync(dataNumber, axis, cancellationToken, timeout, pathId).ConfigureAwait(false); @@ -500,6 +516,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the single-precision float parameter result. public async Task> ReadParameterFloat32Async(short dataNumber, short axis = 0, CancellationToken cancellationToken = default, TimeSpan? timeout = null, ushort? pathId = null) { var result = await ReadParameterBytesAsync(dataNumber, axis, cancellationToken, timeout, pathId).ConfigureAwait(false); @@ -514,6 +531,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the double-precision float parameter result. public async Task> ReadParameterFloat64Async(short dataNumber, short axis = 0, CancellationToken cancellationToken = default, TimeSpan? timeout = null, ushort? pathId = null) { var result = await ReadParameterBytesAsync(dataNumber, axis, cancellationToken, timeout, pathId).ConfigureAwait(false); @@ -527,6 +545,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the macro variable result. public Task> ReadMacroAsync( short number, CancellationToken cancellationToken = default, @@ -550,6 +569,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the PMC range result. public async Task> ReadPmcRangeAsync( short area, short dataType, @@ -602,6 +622,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the PMC range result. public Task> ReadPmcRangeAsync( FocasPmcArea area, FocasPmcDataType dataType, @@ -622,6 +643,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the list of active alarms. public async Task>> ReadAlarmsAsync( short type = -1, short count = 32, @@ -641,6 +663,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the operation mode result. public Task> ReadOperationModeAsync( CancellationToken cancellationToken = default, TimeSpan? timeout = null, @@ -658,6 +681,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the raw operation mode code. public Task> ReadOperationModeCodeAsync( CancellationToken cancellationToken = default, TimeSpan? timeout = null, @@ -671,6 +695,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the executing program name result. public Task> ReadExecutingProgramNameAsync( CancellationToken cancellationToken = default, TimeSpan? timeout = null, @@ -681,6 +706,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the executed block count. public Task> ReadBlockCountAsync( CancellationToken cancellationToken = default, TimeSpan? timeout = null, @@ -698,6 +724,7 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable /// Cancellation token for the read operation. /// Optional per-call timeout override. /// Optional path ID override; defaults to . + /// A task containing the timer value result. public Task> ReadTimerAsync( short type, CancellationToken cancellationToken = default, diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/Wire/WireFocasClient.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/Wire/WireFocasClient.cs index fd6f70de..eed94fa2 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/Wire/WireFocasClient.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/Wire/WireFocasClient.cs @@ -36,13 +36,10 @@ public sealed class WireFocasClient : IFocasClient _wire = new FocasWireClient(logger); } - /// Gets a value indicating whether the wire client is connected to the FOCAS host. + /// public bool IsConnected => _wire.IsConnected; - /// Connects to a FOCAS host at the specified address. - /// The host address containing the machine name and port. - /// The connection timeout; values less than or equal to zero are clamped to 1 second. - /// Cancellation token for the operation. + /// public async Task ConnectAsync(FocasHostAddress address, TimeSpan timeout, CancellationToken cancellationToken) { if (_wire.IsConnected) return; @@ -54,11 +51,7 @@ public sealed class WireFocasClient : IFocasClient await _wire.ConnectAsync(address.Host, address.Port, effective, cancellationToken).ConfigureAwait(false); } - /// Reads a value from the specified FOCAS address. - /// The FOCAS address to read from. - /// The FOCAS data type of the value. - /// Cancellation token for the operation. - /// A tuple containing the read value and FOCAS status code. + /// public async Task<(object? value, uint status)> ReadAsync( FocasAddress address, FocasDataType type, CancellationToken cancellationToken) { @@ -74,19 +67,12 @@ public sealed class WireFocasClient : IFocasClient }; } - /// Writes a value to a FOCAS address (always returns BadNotWritable as OtOpcUa is read-only). - /// The FOCAS address to write to. - /// The FOCAS data type of the value. - /// The value to write. - /// Cancellation token for the operation. - /// A task that returns the BadNotWritable status code. + /// public Task WriteAsync( FocasAddress address, FocasDataType type, object? value, CancellationToken cancellationToken) => Task.FromResult(FocasStatusMapper.BadNotWritable); - /// Probes the FOCAS host to verify connectivity. - /// Cancellation token for the operation. - /// True if the probe succeeds; otherwise false. + /// public async Task ProbeAsync(CancellationToken cancellationToken) { if (!_wire.IsConnected) return false; @@ -101,9 +87,7 @@ public sealed class WireFocasClient : IFocasClient } } - /// Reads all active alarms from the FOCAS host. - /// Cancellation token for the operation. - /// A list of active alarms; empty if read fails or not connected. + /// public async Task> ReadAlarmsAsync(CancellationToken cancellationToken) { if (!_wire.IsConnected) return []; @@ -125,9 +109,7 @@ public sealed class WireFocasClient : IFocasClient Message: a.Message ?? string.Empty); } - /// Gets system information from the FOCAS host. - /// Cancellation token for the operation. - /// The FOCAS system information. + /// public async Task GetSysInfoAsync(CancellationToken cancellationToken) { RequireConnected(); @@ -147,9 +129,7 @@ public sealed class WireFocasClient : IFocasClient AxesCount: axesCount); } - /// Gets the names of all axes on the FOCAS host. - /// Cancellation token for the operation. - /// A list of axis names; empty if read fails or not connected. + /// public async Task> GetAxisNamesAsync(CancellationToken cancellationToken) { if (!_wire.IsConnected) return []; @@ -169,9 +149,7 @@ public sealed class WireFocasClient : IFocasClient } } - /// Gets the names of all spindles on the FOCAS host. - /// Cancellation token for the operation. - /// A list of spindle names; empty if read fails or not connected. + /// public async Task> GetSpindleNamesAsync(CancellationToken cancellationToken) { if (!_wire.IsConnected) return []; @@ -190,10 +168,7 @@ public sealed class WireFocasClient : IFocasClient } } - /// Reads the dynamic state of a specified axis. - /// The index of the axis to read. - /// Cancellation token for the operation. - /// The dynamic snapshot of the axis. + /// public async Task ReadDynamicAsync(int axisIndex, CancellationToken cancellationToken) { RequireConnected(); @@ -215,9 +190,7 @@ public sealed class WireFocasClient : IFocasClient DistanceToGo: pos.Distance); } - /// Gets information about the currently executing program. - /// Cancellation token for the operation. - /// The current program information. + /// public async Task GetProgramInfoAsync(CancellationToken cancellationToken) { RequireConnected(); @@ -245,10 +218,7 @@ public sealed class WireFocasClient : IFocasClient Mode: modeResult.IsOk ? modeResult.Value : 0); } - /// Gets a timer value from the FOCAS host. - /// The kind of timer to read (run time, cutting time, etc.). - /// Cancellation token for the operation. - /// The timer value. + /// public async Task GetTimerAsync(FocasTimerKind kind, CancellationToken cancellationToken) { RequireConnected(); @@ -258,9 +228,7 @@ public sealed class WireFocasClient : IFocasClient return new FocasTimer(kind, t.Minutes, t.Milliseconds); } - /// Gets servo load information for all axes. - /// Cancellation token for the operation. - /// A list of servo load values for each axis; empty if read fails or not connected. + /// public async Task> GetServoLoadsAsync(CancellationToken cancellationToken) { if (!_wire.IsConnected) return []; @@ -272,15 +240,11 @@ public sealed class WireFocasClient : IFocasClient .ToList(); } - /// Gets spindle load information for all spindles. - /// Cancellation token for the operation. - /// A list of spindle load percentages; empty if read fails or not connected. + /// public Task> GetSpindleLoadsAsync(CancellationToken cancellationToken) => ReadSpindleMetricAsync((sel, ct) => _wire.ReadSpindleLoadAsync(sel, ct), cancellationToken); - /// Gets maximum RPM information for all spindles. - /// Cancellation token for the operation. - /// A list of maximum RPM values for each spindle; empty if read fails or not connected. + /// public Task> GetSpindleMaxRpmsAsync(CancellationToken cancellationToken) => ReadSpindleMetricAsync((sel, ct) => _wire.ReadSpindleMaxRpmAsync(sel, ct), cancellationToken); @@ -427,7 +391,6 @@ public sealed class WireFocasClientFactory : IFocasClientFactory _logger = logger; } - /// Creates a new WireFocasClient instance. - /// A new IFocasClient implementation. + /// public IFocasClient Create() => new WireFocasClient(_logger); } diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser/GalaxyBrowseSession.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser/GalaxyBrowseSession.cs index 431c867c..1734ea97 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser/GalaxyBrowseSession.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser/GalaxyBrowseSession.cs @@ -22,10 +22,10 @@ internal sealed class GalaxyBrowseSession : IBrowseSession private volatile bool _disposed; private IReadOnlyList? _roots; - /// Opaque token identifying this session in the AdminUI registry. + /// public Guid Token { get; } = Guid.NewGuid(); - /// Wall-clock time of the most recent successful Root/Expand/Attributes call. + /// public DateTime LastUsedUtc { get; private set; } = DateTime.UtcNow; /// @@ -45,6 +45,8 @@ internal sealed class GalaxyBrowseSession : IBrowseSession /// returns them as s. Result is cached; a second call /// returns the cached roots without a re-fetch. /// + /// Cancellation token for the operation. + /// The top-level browse nodes. public async Task> RootAsync(CancellationToken cancellationToken) { ObjectDisposedException.ThrowIf(_disposed, this); @@ -69,6 +71,9 @@ internal sealed class GalaxyBrowseSession : IBrowseSession /// . Throws /// if the tag hasn't been handed out by a prior Root/Expand call. /// + /// The tag name of the node to expand. + /// Cancellation token for the operation. + /// The direct child browse nodes. public async Task> ExpandAsync(string nodeId, CancellationToken cancellationToken) { ObjectDisposedException.ThrowIf(_disposed, this); @@ -89,6 +94,9 @@ internal sealed class GalaxyBrowseSession : IBrowseSession /// via DiscoverHierarchyAsync(MaxDepth=0, RootTagName=nodeId, IncludeAttributes=true). /// Returns an empty list if the gateway has no matching object. /// + /// The tag name of the node to fetch attributes for. + /// Cancellation token for the operation. + /// The attribute information for the node, or an empty list if not found. public async Task> AttributesAsync(string nodeId, CancellationToken cancellationToken) { ObjectDisposedException.ThrowIf(_disposed, this); @@ -162,6 +170,7 @@ internal sealed class GalaxyBrowseSession : IBrowseSession /// Idempotently tears down the underlying repository client. Swallows exceptions /// on shutdown — the registry's reaper may be racing a client-initiated close. /// + /// A task that represents the asynchronous dispose operation. public async ValueTask DisposeAsync() { if (_disposed) return; diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser/GalaxyDriverBrowser.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser/GalaxyDriverBrowser.cs index 35fcdccd..52a0d7bc 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser/GalaxyDriverBrowser.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser/GalaxyDriverBrowser.cs @@ -42,7 +42,7 @@ public sealed class GalaxyDriverBrowser : IDriverBrowser _logger = logger ?? NullLogger.Instance; } - /// Driver type key — matches the AdminUI's persisted "GalaxyMxGateway" value. + /// // Hardcoded literal: this project references Driver.Galaxy.Contracts, not Driver.Galaxy, // so GalaxyDriverFactoryExtensions.DriverTypeName isn't available here. public string DriverType => "GalaxyMxGateway"; @@ -60,6 +60,7 @@ public sealed class GalaxyDriverBrowser : IDriverBrowser /// Thrown when the JSON deserialises to null, when Gateway.Endpoint is empty, /// or when MxAccess.ClientName is empty. /// + /// An open browse session over the Galaxy repository client. public async Task OpenAsync(string configJson, CancellationToken cancellationToken) { var opts = JsonSerializer.Deserialize(configJson, JsonOpts) diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/AlarmRefBuilder.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/AlarmRefBuilder.cs index 2ab50439..e5152c70 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/AlarmRefBuilder.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/AlarmRefBuilder.cs @@ -39,6 +39,7 @@ internal static class AlarmRefBuilder /// The full reference of the alarm-bearing attribute. /// The initial alarm severity level. /// The initial alarm description. + /// An with all five sub-attribute references populated. public static AlarmConditionInfo Build( string fullReference, AlarmSeverity initialSeverity = AlarmSeverity.Medium, diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/DataTypeMap.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/DataTypeMap.cs index 92f08c6c..b9c5f79e 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/DataTypeMap.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/DataTypeMap.cs @@ -20,6 +20,7 @@ internal static class DataTypeMap { /// Maps an MXAccess data type ID to a driver data type. /// The MXAccess data type ID. + /// The corresponding , defaulting to for unknown codes. public static DriverDataType Map(int mxDataType) => mxDataType switch { 0 => DriverDataType.Boolean, diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/DeployWatcher.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/DeployWatcher.cs index c11325fb..aa1649d5 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/DeployWatcher.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/DeployWatcher.cs @@ -98,6 +98,7 @@ public sealed class DeployWatcher : IDisposable } /// Cancels the loop and waits for it to exit cleanly. + /// A task that represents the asynchronous stop operation. public async Task StopAsync() { var cts = _cts; diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/GalaxyDiscoverer.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/GalaxyDiscoverer.cs index d07c79e3..65f579d8 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/GalaxyDiscoverer.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/GalaxyDiscoverer.cs @@ -39,6 +39,7 @@ public sealed class GalaxyDiscoverer /// /// The address space builder to populate with discovery results. /// The cancellation token for the operation. + /// A task that represents the asynchronous discovery operation. public async Task DiscoverAsync(IAddressSpaceBuilder builder, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(builder); diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/GatewayGalaxyDeployWatchSource.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/GatewayGalaxyDeployWatchSource.cs index c984c251..4486efc8 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/GatewayGalaxyDeployWatchSource.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/GatewayGalaxyDeployWatchSource.cs @@ -22,10 +22,7 @@ public sealed class GatewayGalaxyDeployWatchSource : IGalaxyDeployWatchSource _client = client ?? throw new ArgumentNullException(nameof(client)); } - /// Watches for deploy events asynchronously. - /// The last deploy time that was observed. - /// The cancellation token. - /// An async enumerable of deploy events. + /// public IAsyncEnumerable WatchAsync( DateTimeOffset? lastSeenDeployTime, CancellationToken cancellationToken) => _client.WatchDeployEventsAsync(lastSeenDeployTime, cancellationToken); diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/GatewayGalaxyHierarchySource.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/GatewayGalaxyHierarchySource.cs index d3382ddb..9cd4cc3f 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/GatewayGalaxyHierarchySource.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/GatewayGalaxyHierarchySource.cs @@ -24,6 +24,7 @@ public sealed class GatewayGalaxyHierarchySource : IGalaxyHierarchySource /// Discovers the Galaxy object hierarchy asynchronously via the gateway. /// /// Cancellation token for the operation. + /// A task that resolves to the full list of Galaxy objects in the hierarchy. public Task> GetHierarchyAsync(CancellationToken cancellationToken) => _client.DiscoverHierarchyAsync(cancellationToken); } diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/IGalaxyDeployWatchSource.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/IGalaxyDeployWatchSource.cs index 8ed170d3..7e981c73 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/IGalaxyDeployWatchSource.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/IGalaxyDeployWatchSource.cs @@ -21,6 +21,7 @@ public interface IGalaxyDeployWatchSource /// /// The last seen deploy time, or null to receive a bootstrap event. /// The cancellation token. + /// An async stream of deploy events. IAsyncEnumerable WatchAsync( DateTimeOffset? lastSeenDeployTime, CancellationToken cancellationToken); } diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/IGalaxyHierarchySource.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/IGalaxyHierarchySource.cs index fd3c0b06..7512b185 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/IGalaxyHierarchySource.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Browse/IGalaxyHierarchySource.cs @@ -16,5 +16,6 @@ public interface IGalaxyHierarchySource /// callers don't reimplement paging. /// /// Cancellation token for the operation. + /// A task that resolves to the full materialised Galaxy object hierarchy. Task> GetHierarchyAsync(CancellationToken cancellationToken); } diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriver.cs index 1bcc63b6..c52e9950 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriver.cs @@ -126,7 +126,7 @@ public sealed class GalaxyDriver /// Fires when a host transitions Running ↔ Stopped (PR 4.7 HostStatusAggregator). public event EventHandler? OnHostStatusChanged; - /// + /// Fires when an alarm event is received from the Galaxy alarm feed. public event EventHandler? OnAlarmEvent; /// Initializes a new instance of the class. @@ -202,6 +202,7 @@ public sealed class GalaxyDriver /// asserted deterministically (Driver.Galaxy-013). /// /// Cancellation token for the replay operation. + /// A task that represents the asynchronous replay operation. internal Task InvokeReplayForTestAsync(CancellationToken cancellationToken) => ReplayAsync(cancellationToken); @@ -449,6 +450,7 @@ public sealed class GalaxyDriver /// changing the call site. /// /// The secret reference string to resolve. + /// The resolved API key string. internal static string ResolveApiKey(string secretRef) => ResolveApiKey(secretRef, logger: null); /// @@ -459,6 +461,7 @@ public sealed class GalaxyDriver /// /// The secret reference string to resolve. /// Optional logger for warning on cleartext keys. + /// The resolved API key string. internal static string ResolveApiKey(string secretRef, ILogger? logger) { ArgumentException.ThrowIfNullOrEmpty(secretRef); @@ -1205,6 +1208,7 @@ public sealed class GalaxyDriver /// GetAwaiter().GetResult() for every async sub-component, risking a /// deadlock under thread-pool starvation). /// + /// A value task that represents the asynchronous disposal operation. public async ValueTask DisposeAsync() { if (_disposed) return; @@ -1278,26 +1282,18 @@ public sealed class GalaxyDriver System.Collections.Concurrent.ConcurrentDictionary map) : IAddressSpaceBuilder { - /// Creates a folder node and returns a builder for populating it. - /// The OPC UA BrowseName of the folder. - /// The display name for the folder. + /// public IAddressSpaceBuilder Folder(string browseName, string displayName) => new SecurityCapturingBuilder(inner.Folder(browseName, displayName), map); - /// Creates a variable node and captures its security classification. - /// The OPC UA BrowseName of the variable. - /// The display name for the variable. - /// The driver attribute metadata including security classification. + /// public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo attributeInfo) { map[attributeInfo.FullName] = attributeInfo.SecurityClass; return inner.Variable(browseName, displayName, attributeInfo); } - /// Adds a property node to the current parent. - /// The OPC UA BrowseName of the property. - /// The OPC UA data type of the property. - /// The property value. + /// public void AddProperty(string browseName, DriverDataType dataType, object? value) => inner.AddProperty(browseName, dataType, value); } diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriverFactoryExtensions.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriverFactoryExtensions.cs index 328aae88..3b92c059 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriverFactoryExtensions.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriverFactoryExtensions.cs @@ -35,6 +35,7 @@ public static class GalaxyDriverFactoryExtensions /// Convenience for tests + standalone callers. /// The unique identifier for the driver instance. /// The driver configuration in JSON format. + /// A configured instance. public static GalaxyDriver CreateInstance(string driverInstanceId, string driverConfigJson) => CreateInstance(driverInstanceId, driverConfigJson, loggerFactory: null); @@ -42,6 +43,7 @@ public static class GalaxyDriverFactoryExtensions /// The unique identifier for the driver instance. /// The driver configuration in JSON format. /// The optional logger factory for creating drivers. + /// A configured instance. public static GalaxyDriver CreateInstance( string driverInstanceId, string driverConfigJson, ILoggerFactory? loggerFactory) { diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Health/HostStatusAggregator.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Health/HostStatusAggregator.cs index a606f908..cea862d3 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Health/HostStatusAggregator.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Health/HostStatusAggregator.cs @@ -38,6 +38,7 @@ public sealed class HostStatusAggregator /// Snapshot the current host set. Suitable as the body of /// IHostConnectivityProbe.GetHostStatuses(). /// + /// A snapshot of all currently tracked host connectivity statuses. public IReadOnlyList Snapshot() { lock (_lock) diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Health/PerPlatformProbeWatcher.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Health/PerPlatformProbeWatcher.cs index daefb2ea..b766e38b 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Health/PerPlatformProbeWatcher.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Health/PerPlatformProbeWatcher.cs @@ -77,6 +77,7 @@ public sealed class PerPlatformProbeWatcher : IDisposable /// /// The platform tag names to synchronize. /// A cancellation token to cancel the operation. + /// A task that represents the asynchronous operation. public async Task SyncPlatformsAsync( IEnumerable platformTagNames, CancellationToken cancellationToken) { diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GalaxyMxSession.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GalaxyMxSession.cs index 7913c69c..746ef7ca 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GalaxyMxSession.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GalaxyMxSession.cs @@ -55,6 +55,7 @@ public sealed class GalaxyMxSession : IAsyncDisposable /// /// The MX gateway client options. /// The cancellation token. + /// A task that represents the asynchronous connect operation. public async Task ConnectAsync(MxGatewayClientOptions clientOptions, CancellationToken cancellationToken) { ObjectDisposedException.ThrowIf(_disposed, this); @@ -89,6 +90,7 @@ public sealed class GalaxyMxSession : IAsyncDisposable public MxGatewaySession? Session => _session; /// Disposes the session and underlying gateway client resources. + /// A value task that represents the asynchronous dispose operation. public async ValueTask DisposeAsync() { if (_disposed) return; diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GalaxySubscriptionHandle.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GalaxySubscriptionHandle.cs index 9103e395..290984d6 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GalaxySubscriptionHandle.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GalaxySubscriptionHandle.cs @@ -8,6 +8,6 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime; /// internal sealed record GalaxySubscriptionHandle(long SubscriptionId) : ISubscriptionHandle { - /// Gets the diagnostic identifier for the subscription. + /// public string DiagnosticId => $"galaxy-sub-{SubscriptionId}"; } diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GatewayGalaxyAlarmAcknowledger.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GatewayGalaxyAlarmAcknowledger.cs index 19b957da..d5fc35f1 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GatewayGalaxyAlarmAcknowledger.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GatewayGalaxyAlarmAcknowledger.cs @@ -37,6 +37,7 @@ internal sealed class GatewayGalaxyAlarmAcknowledger : IGalaxyAlarmAcknowledger /// An operator-supplied comment attached to the acknowledgement. /// The name of the operator performing the acknowledgement. /// A token to cancel the asynchronous operation. + /// A task that represents the asynchronous acknowledge operation. public async Task AcknowledgeAsync( string alarmFullReference, string comment, diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GatewayGalaxyAlarmFeed.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GatewayGalaxyAlarmFeed.cs index e01c1c61..3a584f30 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GatewayGalaxyAlarmFeed.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GatewayGalaxyAlarmFeed.cs @@ -39,6 +39,7 @@ internal sealed class GatewayGalaxyAlarmFeed : IGalaxyAlarmFeed /// /// The stream request parameters. /// A cancellation token. + /// An async enumerable of alarm feed messages from the gateway stream. internal delegate IAsyncEnumerable AlarmStreamFactory( StreamAlarmsRequest request, CancellationToken cancellationToken); @@ -261,6 +262,7 @@ internal sealed class GatewayGalaxyAlarmFeed : IGalaxyAlarmFeed }; /// Releases the alarm feed resources and stops the background stream task. + /// A that completes when the background stream loop has stopped. public async ValueTask DisposeAsync() { if (_disposed) return; diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GatewayGalaxySubscriber.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GatewayGalaxySubscriber.cs index e38430f6..664b9b2d 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GatewayGalaxySubscriber.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/GatewayGalaxySubscriber.cs @@ -28,11 +28,7 @@ public sealed class GatewayGalaxySubscriber : IGalaxySubscriber _session = session ?? throw new ArgumentNullException(nameof(session)); } - /// Subscribes to a bulk list of Galaxy references with optional buffered update interval. - /// The full Galaxy tag references to subscribe to. - /// The buffered update interval in milliseconds. - /// The cancellation token. - /// A task that returns a list of subscribe results. + /// public async Task> SubscribeBulkAsync( IReadOnlyList fullReferences, int bufferedUpdateIntervalMs, CancellationToken cancellationToken) { @@ -100,10 +96,7 @@ public sealed class GatewayGalaxySubscriber : IGalaxySubscriber } } - /// Unsubscribes from a bulk list of item handles. - /// The item handles to unsubscribe from. - /// The cancellation token. - /// A task representing the unsubscribe operation. + /// public async Task UnsubscribeBulkAsync(IReadOnlyList itemHandles, CancellationToken cancellationToken) { if (itemHandles.Count == 0) return; @@ -117,9 +110,7 @@ public sealed class GatewayGalaxySubscriber : IGalaxySubscriber .ConfigureAwait(false); } - /// Streams Galaxy MX events asynchronously. - /// The cancellation token. - /// An async enumerable of MX events. + /// public IAsyncEnumerable StreamEventsAsync(CancellationToken cancellationToken) { var session = _session.Session diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/IGalaxyAlarmAcknowledger.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/IGalaxyAlarmAcknowledger.cs index aa9b1c89..752c4c30 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/IGalaxyAlarmAcknowledger.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/IGalaxyAlarmAcknowledger.cs @@ -24,6 +24,7 @@ internal interface IGalaxyAlarmAcknowledger /// OPC UA session by the server-side ACL layer before reaching the driver. /// /// Cancels the gateway RPC. + /// A task that represents the asynchronous acknowledge operation. Task AcknowledgeAsync( string alarmFullReference, string comment, diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/IGalaxyDataWriter.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/IGalaxyDataWriter.cs index f91e66e2..7b78e2d5 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/IGalaxyDataWriter.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/IGalaxyDataWriter.cs @@ -26,6 +26,7 @@ public interface IGalaxyDataWriter /// (the safest default — non-secured Write). /// /// Aborts the in-flight batch. + /// A task resolving to one per input entry, in input order. Task> WriteAsync( IReadOnlyList writes, Func securityResolver, diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/IGalaxySubscriber.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/IGalaxySubscriber.cs index 9d33968e..3c66c9cb 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/IGalaxySubscriber.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/IGalaxySubscriber.cs @@ -20,12 +20,14 @@ public interface IGalaxySubscriber /// The list of tag references to subscribe to. /// The buffered update interval in milliseconds. /// Cancellation token for the operation. + /// A task that resolves to one per requested reference, in input order. Task> SubscribeBulkAsync( IReadOnlyList fullReferences, int bufferedUpdateIntervalMs, CancellationToken cancellationToken); /// Unsubscribe a batch of item handles obtained from . /// The item handles to unsubscribe. /// Cancellation token for the operation. + /// A task that represents the asynchronous operation. Task UnsubscribeBulkAsync(IReadOnlyList itemHandles, CancellationToken cancellationToken); /// @@ -34,5 +36,6 @@ public interface IGalaxySubscriber /// its . /// /// Cancellation token for the stream. + /// An async sequence of items emitted by the gateway. IAsyncEnumerable StreamEventsAsync(CancellationToken cancellationToken); } diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/MxAccessSeverityMapper.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/MxAccessSeverityMapper.cs index 338b94fa..a2e6897a 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/MxAccessSeverityMapper.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/MxAccessSeverityMapper.cs @@ -37,6 +37,7 @@ internal static class MxAccessSeverityMapper /// + OPC UA Part 9 numeric severity tuple. /// /// The raw MXAccess severity value (0-999 range, clamped if out of range). + /// A tuple of the OPC UA bucket and the corresponding Part 9 numeric severity. public static (AlarmSeverity Bucket, int OpcUaSeverity) Map(int rawMxAccessSeverity) { if (rawMxAccessSeverity < 250) diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/MxValueDecoder.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/MxValueDecoder.cs index 93f60aad..09ed4f0f 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/MxValueDecoder.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/MxValueDecoder.cs @@ -15,6 +15,7 @@ internal static class MxValueDecoder { /// Decodes a gateway MxValue into a boxed CLR object. /// The MxValue to decode, or null. + /// The decoded CLR value, or null when the input is null or empty. public static object? Decode(MxValue? value) { if (value is null) return null; diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/ReconnectSupervisor.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/ReconnectSupervisor.cs index 6e3fbd90..a3ea49d8 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/ReconnectSupervisor.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/ReconnectSupervisor.cs @@ -147,6 +147,7 @@ public sealed class ReconnectSupervisor : IDisposable /// and for orchestration that wants to gate calls on recovery completing. /// /// Token to cancel the wait operation. + /// A task that completes when the supervisor reaches the Healthy state or the token is cancelled. public async Task WaitForHealthyAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested && IsDegraded) diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/SubscriptionRegistry.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/SubscriptionRegistry.cs index 1f70e82f..b3ca1604 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/SubscriptionRegistry.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/SubscriptionRegistry.cs @@ -32,6 +32,7 @@ internal sealed class SubscriptionRegistry public int TrackedItemHandleCount => _subscribersByItemHandle.Count; /// Allocate a fresh subscription id. Monotonic; unique per registry lifetime. + /// A new unique subscription identifier. public long NextSubscriptionId() => Interlocked.Increment(ref _nextSubscriptionId); /// @@ -108,6 +109,7 @@ internal sealed class SubscriptionRegistry } /// Snapshot every active binding for diagnostic output. + /// A flat list of all active tag bindings across all subscriptions. public IReadOnlyList SnapshotAllBindings() => [.. _bySubscriptionId.Values.SelectMany(entry => entry.Bindings)]; @@ -116,6 +118,7 @@ internal sealed class SubscriptionRegistry /// Used by the reconnect replay path so it can re-issue SubscribeBulk per subscription /// and then each one with the post-reconnect item handles. /// + /// A list of (SubscriptionId, Bindings) pairs for all active subscriptions. public IReadOnlyList<(long SubscriptionId, IReadOnlyList Bindings)> SnapshotEntries() => [.. _bySubscriptionId.Values.Select(entry => (entry.SubscriptionId, entry.Bindings))]; diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/TracedGalaxyDataWriter.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/TracedGalaxyDataWriter.cs index 4c15b21c..2f87fb9c 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/TracedGalaxyDataWriter.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/TracedGalaxyDataWriter.cs @@ -14,6 +14,7 @@ internal sealed class TracedGalaxyDataWriter(IGalaxyDataWriter inner, string cli /// The list of write requests to process. /// Function to resolve security classification for tag references. /// Cancellation token for the operation. + /// A task that resolves to the write results for each request. public async Task> WriteAsync( IReadOnlyList writes, Func securityResolver, diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/TracedGalaxySubscriber.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/TracedGalaxySubscriber.cs index 55717860..a951f9a6 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/TracedGalaxySubscriber.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/Runtime/TracedGalaxySubscriber.cs @@ -10,10 +10,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime; /// internal sealed class TracedGalaxySubscriber(IGalaxySubscriber inner, string clientName) : IGalaxySubscriber { - /// Subscribes to multiple Galaxy tags in bulk with tracing. - /// The full tag references to subscribe to. - /// The buffered update interval in milliseconds. - /// The cancellation token. + /// public async Task> SubscribeBulkAsync( IReadOnlyList fullReferences, int bufferedUpdateIntervalMs, CancellationToken cancellationToken) { @@ -35,9 +32,7 @@ internal sealed class TracedGalaxySubscriber(IGalaxySubscriber inner, string cli } } - /// Unsubscribes from multiple Galaxy tags in bulk with tracing. - /// The item handles to unsubscribe from. - /// The cancellation token. + /// public async Task UnsubscribeBulkAsync(IReadOnlyList itemHandles, CancellationToken cancellationToken) { using var activity = GalaxyTelemetry.ActivitySource.StartActivity("galaxy.unsubscribe_bulk"); @@ -60,6 +55,7 @@ internal sealed class TracedGalaxySubscriber(IGalaxySubscriber inner, string cli /// visibility through 's metrics in PR 6.2 instead. /// /// The cancellation token. + /// An async sequence of items forwarded from the inner subscriber. public async IAsyncEnumerable StreamEventsAsync( [EnumeratorCancellation] CancellationToken cancellationToken) { diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client/Ipc/FrameWriter.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client/Ipc/FrameWriter.cs index 33e9609d..130c760c 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client/Ipc/FrameWriter.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client/Ipc/FrameWriter.cs @@ -26,6 +26,7 @@ public sealed class FrameWriter : IDisposable /// The frame message kind tag. /// The message object to serialize and write. /// The cancellation token. + /// A task that represents the asynchronous write operation. public async Task WriteAsync(MessageKind kind, T message, CancellationToken ct) { var body = MessagePackSerializer.Serialize(message, cancellationToken: ct); diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client/WonderwareHistorianClient.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client/WonderwareHistorianClient.cs index 2aacfbcf..a29c8ca9 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client/WonderwareHistorianClient.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client/WonderwareHistorianClient.cs @@ -66,13 +66,7 @@ public sealed class WonderwareHistorianClient : IHistorianDataSource, IAlarmHist // ===== IHistorianDataSource ===== - /// Asynchronously reads raw historical data for a tag within a time range. - /// The full reference path of the tag to read. - /// The start time in UTC for the read range. - /// The end time in UTC for the read range. - /// The maximum number of values to return. - /// The cancellation token. - /// A task that returns the historical read result. + /// public async Task ReadRawAsync( string fullReference, DateTime startUtc, DateTime endUtc, uint maxValuesPerNode, CancellationToken cancellationToken) @@ -91,14 +85,7 @@ public sealed class WonderwareHistorianClient : IHistorianDataSource, IAlarmHist return new HistoryReadResult(ToSnapshots(reply.Samples), ContinuationPoint: null); } - /// Asynchronously reads processed historical data with aggregation for a tag within a time range. - /// The full reference path of the tag to read. - /// The start time in UTC for the read range. - /// The end time in UTC for the read range. - /// The time interval for aggregation. - /// The type of aggregation to apply. - /// The cancellation token. - /// A task that returns the historical read result with aggregated data. + /// public async Task ReadProcessedAsync( string fullReference, DateTime startUtc, DateTime endUtc, TimeSpan interval, HistoryAggregateType aggregate, CancellationToken cancellationToken) @@ -118,11 +105,7 @@ public sealed class WonderwareHistorianClient : IHistorianDataSource, IAlarmHist return new HistoryReadResult(ToAggregateSnapshots(reply.Buckets), ContinuationPoint: null); } - /// Asynchronously reads historical data at specific timestamps for a tag. - /// The full reference path of the tag to read. - /// The specific timestamps in UTC to read values for. - /// The cancellation token. - /// A task that returns the historical read result with values at the specified times. + /// public async Task ReadAtTimeAsync( string fullReference, IReadOnlyList timestampsUtc, CancellationToken cancellationToken) { @@ -184,13 +167,7 @@ public sealed class WonderwareHistorianClient : IHistorianDataSource, IAlarmHist return result; } - /// Asynchronously reads historical events within a time range. - /// The source name filter for events, or null to read all sources. - /// The start time in UTC for the read range. - /// The end time in UTC for the read range. - /// The maximum number of events to return. - /// The cancellation token. - /// A task that returns the historical events result. + /// public async Task ReadEventsAsync( string? sourceName, DateTime startUtc, DateTime endUtc, int maxEvents, CancellationToken cancellationToken) @@ -231,6 +208,7 @@ public sealed class WonderwareHistorianClient : IHistorianDataSource, IAlarmHist /// incremented any counter). (Finding 003 / 004.) /// /// + /// A reflecting current operation counters and channel connection state. public HistorianHealthSnapshot GetHealthSnapshot() { lock (_healthLock) diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/AahClientManagedAlarmEventWriter.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/AahClientManagedAlarmEventWriter.cs index 71e46bdb..c25e2ab3 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/AahClientManagedAlarmEventWriter.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/AahClientManagedAlarmEventWriter.cs @@ -37,6 +37,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend /// /// The alarm events to write. /// Cancellation token. + /// A task resolving to a bool array where each element indicates whether the corresponding event was acknowledged. public async Task WriteAsync(AlarmHistorianEventDto[] events, CancellationToken cancellationToken) { if (events is null || events.Length == 0) @@ -91,6 +92,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend /// The HRESULT code from the SDK call. /// Indicates whether the error is a communication-class error. /// Indicates whether the input was malformed. + /// The write outcome for the SDK call result. public static AlarmHistorianWriteOutcome MapOutcome(int hresult, bool isCommunicationError, bool isMalformedInput) { // Order matters: malformed input is permanent regardless of HRESULT pattern; diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/HistorianClusterEndpointPicker.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/HistorianClusterEndpointPicker.cs index a65ff312..37be9db1 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/HistorianClusterEndpointPicker.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/HistorianClusterEndpointPicker.cs @@ -48,6 +48,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend } /// Gets the list of currently healthy nodes. + /// A read-only list of node names that are currently healthy and not in cooldown. public IReadOnlyList GetHealthyNodes() { lock (_lock) @@ -101,6 +102,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend } /// Returns a snapshot of all node states. + /// A list of objects representing each configured node. public List SnapshotNodeStates() { lock (_lock) diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/HistorianDataSource.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/HistorianDataSource.cs index 22f3afb8..02877e3f 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/HistorianDataSource.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/HistorianDataSource.cs @@ -83,6 +83,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend /// shared SDK connection should therefore be reset. Internal for unit testing. /// /// The historian access error code. + /// true if the error code indicates a connection-class failure; otherwise false. internal static bool IsConnectionClassError(HistorianAccessError.ErrorValue code) => ConnectionErrorCodes.Contains(code); @@ -97,6 +98,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend /// /// The historian configuration. /// The cancellation token. + /// A linked pre-wired with the optional request timeout. internal static CancellationTokenSource BuildRequestCts(HistorianConfiguration cfg, CancellationToken ct) { var cts = CancellationTokenSource.CreateLinkedTokenSource(ct); @@ -160,7 +162,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend }; } - /// Gets a snapshot of the current health status. + /// public HistorianHealthSnapshot GetHealthSnapshot() { var nodeStates = _picker.SnapshotNodeStates(); @@ -396,12 +398,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend } } - /// Reads raw historical samples for the specified tag. - /// The tag name. - /// The start time for the query. - /// The end time for the query. - /// The maximum number of values to return. - /// Cancellation token for the operation. + /// public Task> ReadRawAsync( string tagName, DateTime startTime, DateTime endTime, int maxValues, CancellationToken ct = default) @@ -485,13 +482,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend return Task.FromResult(results); } - /// Reads aggregate historical samples for the specified tag. - /// The tag name. - /// The start time for the query. - /// The end time for the query. - /// The interval in milliseconds. - /// The aggregate column name. - /// Cancellation token for the operation. + /// public Task> ReadAggregateAsync( string tagName, DateTime startTime, DateTime endTime, double intervalMs, string aggregateColumn, @@ -572,10 +563,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend return Task.FromResult(results); } - /// Reads historical samples at specific timestamps for the specified tag. - /// The tag name. - /// The timestamps to read. - /// Cancellation token for the operation. + /// public Task> ReadAtTimeAsync( string tagName, DateTime[] timestamps, CancellationToken ct = default) @@ -658,12 +646,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend return Task.FromResult(results); } - /// Reads historical events within the specified time range. - /// The optional event source name filter. - /// The start time for the query. - /// The end time for the query. - /// The maximum number of events to return. - /// Cancellation token for the operation. + /// public Task> ReadEventsAsync( string? sourceName, DateTime startTime, DateTime endTime, int maxEvents, CancellationToken ct = default) @@ -764,6 +747,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend /// /// /// The history query result. + /// The resolved value — the string value when non-empty and numeric value is zero, otherwise the numeric value. internal static object? SelectValue(HistoryQueryResult result) => SelectValueFromPair(result.Value, result.StringValue); @@ -775,6 +759,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend /// /// The numeric value. /// The string value. + /// The string value when non-empty and numeric value is zero, otherwise the numeric value. internal static object? SelectValueFromPair(double value, string? stringValue) { if (!string.IsNullOrEmpty(stringValue) && value == 0) @@ -785,6 +770,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend /// Extracts the specified aggregate value from an analog summary query result. /// The analog summary query result. /// The aggregate column name. + /// The value of the specified aggregate column, or null if the column name is unknown. internal static double? ExtractAggregateValue(AnalogSummaryQueryResult result, string column) { switch (column) @@ -800,7 +786,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend } } - /// Disposes the historian data source and releases its resources. + /// public void Dispose() { if (_disposed) return; diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/IAlarmHistorianWriteBackend.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/IAlarmHistorianWriteBackend.cs index 3c655a15..b0e9df38 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/IAlarmHistorianWriteBackend.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/IAlarmHistorianWriteBackend.cs @@ -25,6 +25,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend /// /// The events to write to the historian. /// Token to cancel the operation. + /// A task that resolves to one per input event, in the same order. Task WriteBatchAsync( AlarmHistorianEventDto[] events, CancellationToken cancellationToken); diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/SdkAlarmHistorianWriteBackend.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/SdkAlarmHistorianWriteBackend.cs index c1a2a9b6..3f2886e3 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/SdkAlarmHistorianWriteBackend.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/SdkAlarmHistorianWriteBackend.cs @@ -103,10 +103,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend _picker = picker ?? new HistorianClusterEndpointPicker(config); } - /// Writes a batch of alarm events to the historian, returning outcomes for each event. - /// The alarm events to write. - /// The cancellation token. - /// An array of outcomes corresponding to each input event. + /// public Task WriteBatchAsync( AlarmHistorianEventDto[] events, CancellationToken cancellationToken) @@ -379,7 +376,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend RequestTimeoutSeconds = _config.RequestTimeoutSeconds, }; - /// Disposes the connection and releases resources. + /// public void Dispose() { if (_disposed) return; diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Ipc/FrameReader.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Ipc/FrameReader.cs index 7593a408..39473c39 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Ipc/FrameReader.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Ipc/FrameReader.cs @@ -52,6 +52,7 @@ public sealed class FrameReader : IDisposable /// Deserializes the message body to the specified type. /// The type to deserialize to. /// The serialized message body. + /// The deserialized value of type . public static T Deserialize(byte[] body) => MessagePackSerializer.Deserialize(body); private async Task ReadExactAsync(byte[] buffer, CancellationToken ct) @@ -70,7 +71,7 @@ public sealed class FrameReader : IDisposable return true; } - /// Disposes the frame reader and optionally closes the underlying stream. + /// public void Dispose() { if (!_leaveOpen) _stream.Dispose(); diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Ipc/FrameWriter.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Ipc/FrameWriter.cs index fd88a7e7..de6c05e6 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Ipc/FrameWriter.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Ipc/FrameWriter.cs @@ -32,6 +32,7 @@ public sealed class FrameWriter : IDisposable /// The message kind identifier. /// The message to serialize and write. /// The cancellation token. + /// A task that represents the asynchronous write operation. public async Task WriteAsync(MessageKind kind, T message, CancellationToken ct) { var body = MessagePackSerializer.Serialize(message, cancellationToken: ct); @@ -57,7 +58,7 @@ public sealed class FrameWriter : IDisposable finally { _gate.Release(); } } - /// Disposes the frame writer and releases resources. + /// public void Dispose() { _gate.Dispose(); diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Ipc/HistorianFrameHandler.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Ipc/HistorianFrameHandler.cs index 9eb9c915..9b70e9df 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Ipc/HistorianFrameHandler.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Ipc/HistorianFrameHandler.cs @@ -34,11 +34,7 @@ public sealed class HistorianFrameHandler : IFrameHandler _alarmWriter = alarmWriter; } - /// Handles an incoming frame by dispatching to the appropriate historian operation. - /// The frame message kind. - /// The frame body bytes. - /// The frame writer for sending responses. - /// Cancellation token. + /// public Task HandleAsync(MessageKind kind, byte[] body, FrameWriter writer, CancellationToken ct) => kind switch { @@ -262,5 +258,6 @@ public interface IAlarmEventWriter /// /// Alarm events to write. /// Cancellation token. + /// A task that resolves to an array of booleans indicating whether each event was successfully persisted. Task WriteAsync(AlarmHistorianEventDto[] events, CancellationToken cancellationToken); } diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Ipc/PipeServer.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Ipc/PipeServer.cs index ffd9cb0c..298189ed 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Ipc/PipeServer.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Ipc/PipeServer.cs @@ -32,6 +32,7 @@ public sealed class PipeServer : IDisposable /// The named pipe server stream to verify. /// The allowed security identifier. /// The rejection reason if verification fails. + /// if the caller is verified; with a populated otherwise. internal delegate bool CallerVerifier(NamedPipeServerStream pipe, SecurityIdentifier allowedSid, out string reason); /// Initializes a new instance of the class. @@ -68,6 +69,7 @@ public sealed class PipeServer : IDisposable /// /// The frame handler to process frames. /// Cancellation token for the operation. + /// A task that completes when the client disconnects or the operation is cancelled. public async Task RunOneConnectionAsync(IFrameHandler handler, CancellationToken ct) { using var linked = CancellationTokenSource.CreateLinkedTokenSource(_cts.Token, ct); @@ -179,6 +181,7 @@ public sealed class PipeServer : IDisposable /// /// The frame handler to process frames. /// Cancellation token for the operation. + /// A task that runs until cancelled or the failure limit is exceeded. public async Task RunAsync(IFrameHandler handler, CancellationToken ct) { var consecutiveFailures = 0; @@ -233,7 +236,7 @@ public sealed class PipeServer : IDisposable catch (Exception ex) { reason = ex.Message; return false; } } - /// Disposes the pipe server and cancels any pending operations. + /// public void Dispose() { _cts.Cancel(); @@ -254,5 +257,6 @@ public interface IFrameHandler /// The serialized message body. /// The frame writer to send responses. /// Cancellation token for the operation. + /// A task that completes when the frame has been handled and any response written. Task HandleAsync(MessageKind kind, byte[] body, FrameWriter writer, CancellationToken ct); } diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Program.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Program.cs index db77d55d..6d569911 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Program.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Program.cs @@ -120,6 +120,7 @@ public static class Program /// set OTOPCUA_HISTORIAN_ALARM_WRITE_ENABLED=false to keep a read-only /// deployment that still loads the SDK for reads. /// + /// The alarm-event writer, or null when alarm writes are disabled. internal static IAlarmEventWriter? BuildAlarmWriter() { var raw = Environment.GetEnvironmentVariable("OTOPCUA_HISTORIAN_ALARM_WRITE_ENABLED"); diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Addressing/DirectLogicAddress.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Addressing/DirectLogicAddress.cs index 88c73903..cdbe8b8b 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Addressing/DirectLogicAddress.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Addressing/DirectLogicAddress.cs @@ -29,6 +29,7 @@ public static class DirectLogicAddress /// /// Input is null / empty / contains non-octal digits (8,9). /// Parsed value exceeds ushort.MaxValue (0xFFFF). + /// The 0-based Modbus PDU address. public static ushort UserVMemoryToPdu(string vAddress) => (ushort)DecodeOctalVAddress(vAddress); // DecodeOctalVAddress guards the 0xFFFF ceiling @@ -80,11 +81,16 @@ public static class DirectLogicAddress /// Octal 40400 == decimal 16640 (0x4100). public const ushort SystemVMemoryOctalBase = 0x4100; // octal 40400 decoded + /// + /// Convert a 0-based offset within the system V-memory bank to its Modbus PDU address, + /// using the fixed system base at . + /// /// /// 0-based register offset within the system bank. Pass 0 for V40400 itself; pass 1 for /// V40401 (octal), and so on. NOT an octal-decoded value — the system bank lives at /// consecutive PDU addresses, so the offset is plain decimal. /// + /// The 0-based Modbus PDU address for the system V-memory register. public static ushort SystemVMemoryToPdu(ushort offsetWithinSystemBank) { var pdu = SystemVMemoryBasePdu + offsetWithinSystemBank; @@ -109,6 +115,7 @@ public static class DirectLogicAddress /// /// Input is null / empty / contains non-octal digits. /// The result exceeds the 16-bit Modbus PDU range. + /// The 0-based Modbus PDU address, routed through the correct bank formula. public static ushort VMemoryToPdu(string vAddress) { var octalValue = DecodeOctalVAddress(vAddress); @@ -155,6 +162,7 @@ public static class DirectLogicAddress /// ladder-logic editor's notation. /// /// The Y-output address (octal, with optional Y prefix). + /// The 0-based Modbus coil address. public static ushort YOutputToCoil(string yAddress) => AddOctalOffset(YOutputBaseCoil, StripPrefix(yAddress, 'Y')); @@ -163,6 +171,7 @@ public static class DirectLogicAddress /// 0-based Modbus coil address. /// /// The C-relay address (octal, with optional C prefix). + /// The 0-based Modbus coil address. public static ushort CRelayToCoil(string cAddress) => AddOctalOffset(CRelayBaseCoil, StripPrefix(cAddress, 'C')); @@ -172,6 +181,7 @@ public static class DirectLogicAddress /// exception — the CPU sizes the table to configured I/O, not installed modules. /// /// The X-input address (octal, with optional X prefix). + /// The 0-based Modbus discrete-input address. public static ushort XInputToDiscrete(string xAddress) => AddOctalOffset(XInputBaseDiscrete, StripPrefix(xAddress, 'X')); @@ -180,6 +190,7 @@ public static class DirectLogicAddress /// Modbus discrete-input address. Accepts "SP" prefix case-insensitively. /// /// The SP special-relay address (octal, with optional SP prefix). + /// The 0-based Modbus discrete-input address. public static ushort SpecialToDiscrete(string spAddress) { if (string.IsNullOrWhiteSpace(spAddress)) diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Addressing/MelsecAddress.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Addressing/MelsecAddress.cs index d3f272aa..b98ad45e 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Addressing/MelsecAddress.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Addressing/MelsecAddress.cs @@ -53,6 +53,7 @@ public static class MelsecAddress /// 0-based Modbus DI address the assignment-block has configured X0 to land at. /// Typical default on QJ71MT91 sample projects: 0. Pass the site-specific value. /// + /// The 0-based Modbus discrete-input address. public static ushort XInputToDiscrete(string xAddress, MelsecFamily family, ushort xBankBase = 0) => AddFamilyOffset(xBankBase, StripPrefix(xAddress, 'X'), family); @@ -63,6 +64,7 @@ public static class MelsecAddress /// MELSEC Y address. Y prefix optional, case-insensitive. /// The PLC family — determines whether the trailing digits are hex or octal. /// 0-based Modbus coil address the assignment-block has configured Y0 to land at. + /// The 0-based Modbus coil address. public static ushort YOutputToCoil(string yAddress, MelsecFamily family, ushort yBankBase = 0) => AddFamilyOffset(yBankBase, StripPrefix(yAddress, 'Y'), family); @@ -73,6 +75,7 @@ public static class MelsecAddress /// /// MELSEC M address. M prefix optional, case-insensitive. /// 0-based Modbus coil address the assignment-block has configured M0 to land at. + /// The 0-based Modbus coil address. public static ushort MRelayToCoil(string mAddress, ushort mBankBase = 0) { var digits = StripPrefix(mAddress, 'M'); @@ -93,6 +96,7 @@ public static class MelsecAddress /// /// MELSEC D address. D prefix optional, case-insensitive. /// 0-based Modbus holding register address the assignment-block has configured D0 to land at. + /// The 0-based Modbus holding register address. public static ushort DRegisterToHolding(string dAddress, ushort dBankBase = 0) { var digits = StripPrefix(dAddress, 'D'); diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Addressing/ModbusModiconAddress.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Addressing/ModbusModiconAddress.cs index 942e7a36..43801f18 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Addressing/ModbusModiconAddress.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Addressing/ModbusModiconAddress.cs @@ -45,6 +45,7 @@ public static class ModbusModiconAddress /// The parsed Modbus region, or default if parsing fails. /// The zero-based PDU offset, or 0 if parsing fails. /// The error message if parsing fails, or null on success. + /// true if the address was parsed successfully; false otherwise. public static bool TryParse(string? address, out ModbusRegion region, out ushort offset, out string? error) { region = default; diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriver.cs index cd1ab6b2..9b300a3b 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriver.cs @@ -135,6 +135,7 @@ public sealed class ModbusDriver /// breaker without tripping siblings on the same TCP socket. /// /// Tag reference to resolve the host for. + /// The per-slave host string used as the circuit-breaker key. public string ResolveHost(string fullReference) { if (_tagsByName.TryGetValue(fullReference, out var tag)) @@ -171,14 +172,12 @@ public sealed class ModbusDriver return true; } - /// Gets the unique identifier of this driver instance. + /// public string DriverInstanceId => _driverInstanceId; - /// Gets the driver type name. + /// public string DriverType => "Modbus"; - /// Initializes the driver with the specified configuration JSON. - /// JSON configuration string. - /// Cancellation token. + /// public async Task InitializeAsync(string driverConfigJson, CancellationToken cancellationToken) { WriteHealth(new DriverHealth(DriverState.Initializing, null, null)); @@ -212,17 +211,14 @@ public sealed class ModbusDriver } } - /// Reinitializes the driver with new configuration. - /// New JSON configuration string. - /// Cancellation token. + /// public async Task ReinitializeAsync(string driverConfigJson, CancellationToken cancellationToken) { await ShutdownAsync(cancellationToken); await InitializeAsync(driverConfigJson, cancellationToken); } - /// Shuts down the driver and releases resources. - /// Cancellation token. + /// public async Task ShutdownAsync(CancellationToken cancellationToken) { var lastRead = ReadHealth().LastSuccessfulRead; @@ -230,7 +226,7 @@ public sealed class ModbusDriver WriteHealth(new DriverHealth(DriverState.Unknown, lastRead, null)); } - /// Gets the current driver health status. + /// public DriverHealth GetHealth() => ReadHealth(); /// @@ -245,17 +241,14 @@ public sealed class ModbusDriver /// Driver.Modbus-003: barrier-protected publish of a new _health snapshot. /// private void WriteHealth(DriverHealth value) => Volatile.Write(ref _health, value); - /// Gets the memory footprint of the driver. + /// public long GetMemoryFootprint() => 0; - /// Flushes optional caches to free memory. - /// Cancellation token. + /// public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; // ---- ITagDiscovery ---- - /// Discovers tags and builds the OPC UA address space. - /// Address space builder. - /// Cancellation token. + /// public Task DiscoverAsync(IAddressSpaceBuilder builder, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(builder); @@ -277,9 +270,7 @@ public sealed class ModbusDriver // ---- IReadable ---- - /// Reads the specified tag references from the Modbus device. - /// Tag references to read. - /// Cancellation token. + /// public async Task> ReadAsync( IReadOnlyList fullReferences, CancellationToken cancellationToken) { @@ -537,6 +528,7 @@ public sealed class ModbusDriver /// on coalesced-read failure (#148), narrowed by bisection (#150), cleared by the /// re-probe loop (#151) when ranges become healthy again. /// + /// A read-only list of currently auto-prohibited Modbus ranges. public IReadOnlyList GetAutoProhibitedRanges() { lock (_autoProhibitedLock) @@ -587,6 +579,7 @@ public sealed class ModbusDriver /// retry (single-register or already-narrowed). /// /// Cancellation token. + /// A task that represents the asynchronous operation. internal async Task RunReprobeOnceForTestAsync(CancellationToken ct) { var transport = _transport ?? throw new InvalidOperationException("Transport not connected"); @@ -920,9 +913,7 @@ public sealed class ModbusDriver // ---- IWritable ---- - /// Writes values to the specified tag references on the Modbus device. - /// Write requests to execute. - /// Cancellation token. + /// public async Task> WriteAsync( IReadOnlyList writes, CancellationToken cancellationToken) { @@ -1160,17 +1151,12 @@ public sealed class ModbusDriver // ---- ISubscribable (polling overlay via shared engine) ---- - /// Subscribes to value changes on the specified tag references. - /// Tag references to subscribe to. - /// Interval for publishing changes. - /// Cancellation token. + /// public Task SubscribeAsync( IReadOnlyList fullReferences, TimeSpan publishingInterval, CancellationToken cancellationToken) => Task.FromResult(_poll.Subscribe(fullReferences, publishingInterval)); - /// Unsubscribes from value changes using the specified handle. - /// Subscription handle. - /// Cancellation token. + /// public Task UnsubscribeAsync(ISubscriptionHandle handle, CancellationToken cancellationToken) { _poll.Unsubscribe(handle); @@ -1179,7 +1165,7 @@ public sealed class ModbusDriver // ---- IHostConnectivityProbe ---- - /// Gets the current connectivity status for all hosts. + /// public IReadOnlyList GetHostStatuses() { lock (_probeLock) @@ -1246,6 +1232,7 @@ public sealed class ModbusDriver /// from 2 chars per register). /// /// Tag definition to measure. + /// The number of 16-bit registers the tag occupies. internal static ushort RegisterCount(ModbusTagDefinition tag) => tag.DataType switch { ModbusDataType.Int16 or ModbusDataType.UInt16 or ModbusDataType.BitInRegister or ModbusDataType.Bcd16 => 1, @@ -1306,6 +1293,7 @@ public sealed class ModbusDriver /// Decodes a register value according to the tag's data type. /// Raw register bytes. /// Tag definition specifying the data type. + /// The decoded value as a boxed .NET type. internal static object DecodeRegister(ReadOnlySpan data, ModbusTagDefinition tag) { switch (tag.DataType) @@ -1387,6 +1375,7 @@ public sealed class ModbusDriver /// Encodes a value into register bytes according to the tag's data type. /// Value to encode. /// Tag definition specifying the data type. + /// The encoded register bytes. internal static byte[] EncodeRegister(object? value, ModbusTagDefinition tag) { switch (tag.DataType) @@ -1522,6 +1511,7 @@ public sealed class ModbusDriver /// /// Raw BCD value. /// Number of nibbles to decode. + /// The decoded decimal value. internal static uint DecodeBcd(uint raw, int nibbles) { uint result = 0; @@ -1542,6 +1532,7 @@ public sealed class ModbusDriver /// /// Decimal value to encode. /// Number of nibbles to encode. + /// The BCD-encoded value. internal static uint EncodeBcd(uint value, int nibbles) { uint result = 0; @@ -1576,6 +1567,7 @@ public sealed class ModbusDriver /// extensions. /// /// Modbus exception code. + /// The corresponding OPC UA status code. internal static uint MapModbusExceptionToStatus(byte exceptionCode) => exceptionCode switch { 0x01 => StatusBadNotSupported, // Illegal Function — FC not in supported list @@ -1596,6 +1588,7 @@ public sealed class ModbusDriver /// leak the probe loop, re-probe loop, and poll-engine background tasks. Shares /// with to keep them in sync. /// + /// A task that represents the asynchronous dispose operation. public async ValueTask DisposeAsync() { await TeardownAsync().ConfigureAwait(false); diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriverFactoryExtensions.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriverFactoryExtensions.cs index 392cc5d1..113e7b47 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriverFactoryExtensions.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusDriverFactoryExtensions.cs @@ -33,6 +33,7 @@ public static class ModbusDriverFactoryExtensions /// Public for the Server-side bootstrapper + test consumers (Admin.Tests, etc.). /// The unique identifier for the driver instance. /// The JSON configuration string for the driver. + /// A configured instance. public static ModbusDriver CreateInstance(string driverInstanceId, string driverConfigJson) => CreateInstance(driverInstanceId, driverConfigJson, loggerFactory: null); @@ -40,6 +41,7 @@ public static class ModbusDriverFactoryExtensions /// The unique identifier for the driver instance. /// The JSON configuration string for the driver. /// Optional logger factory for creating loggers per driver instance. + /// A configured instance. public static ModbusDriver CreateInstance(string driverInstanceId, string driverConfigJson, ILoggerFactory? loggerFactory) { ArgumentException.ThrowIfNullOrWhiteSpace(driverInstanceId); diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusTcpTransport.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusTcpTransport.cs index f317af5d..772e1a8a 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusTcpTransport.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus/ModbusTcpTransport.cs @@ -63,8 +63,7 @@ public sealed class ModbusTcpTransport : IModbusTransport _reconnect = reconnect ?? new ModbusReconnectOptions(); } - /// Connects to the Modbus TCP server with IPv4 preference. - /// The cancellation token for the operation. + /// public async Task ConnectAsync(CancellationToken ct) { // Resolve the host explicitly + prefer IPv4. .NET's TcpClient default-constructor is @@ -121,16 +120,14 @@ public sealed class ModbusTcpTransport : IModbusTransport /// keep-alive timing into "use the default" on most OSes. /// /// The timespan to clamp to whole seconds. + /// The number of whole seconds, with a minimum of 1. internal static int ClampToWholeSeconds(TimeSpan ts) { var seconds = (int)Math.Ceiling(ts.TotalSeconds); return seconds < 1 ? 1 : seconds; } - /// Sends a Modbus PDU and returns the response, with automatic retry on socket failure. - /// The Modbus unit/slave ID. - /// The protocol data unit to send. - /// The cancellation token for the operation. + /// public async Task SendAsync(byte unitId, byte[] pdu, CancellationToken ct) { if (_disposed) throw new ObjectDisposedException(nameof(ModbusTcpTransport)); @@ -284,6 +281,7 @@ public sealed class ModbusTcpTransport : IModbusTransport } /// Asynchronously disposes the transport and underlying socket resources. + /// A value task that represents the asynchronous dispose operation. public async ValueTask DisposeAsync() { if (_disposed) return; diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser/OpcUaClientBrowseSession.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser/OpcUaClientBrowseSession.cs index ef9dacaa..01e54ce5 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser/OpcUaClientBrowseSession.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser/OpcUaClientBrowseSession.cs @@ -32,25 +32,17 @@ internal sealed class OpcUaClientBrowseSession : IBrowseSession _rootNodeId = rootNodeId; } - /// Opaque token identifying this session in the AdminUI registry. + /// public Guid Token { get; } = Guid.NewGuid(); - /// Wall-clock time of the most recent successful browse call; the reaper uses - /// this for idle eviction. + /// public DateTime LastUsedUtc { get; private set; } = DateTime.UtcNow; - /// Browse one level under the configured root node. - /// Cancellation token. + /// public Task> RootAsync(CancellationToken cancellationToken) => BrowseOneLevelAsync(_rootNodeId, cancellationToken); - /// Browse one level under the node identified by , - /// which must be a stable reference produced by - /// (or a plain ns=N;… form). - /// Stable reference string for the parent node. - /// Cancellation token. - /// Thrown when cannot be - /// resolved against the live session's namespace table. + /// public Task> ExpandAsync(string nodeId, CancellationToken cancellationToken) { if (!NamespaceMap.TryResolve(_session, nodeId, out var resolved)) @@ -59,10 +51,7 @@ internal sealed class OpcUaClientBrowseSession : IBrowseSession return BrowseOneLevelAsync(resolved, cancellationToken); } - /// The OPC UA picker treats variables as terminal leaves and does not surface - /// a per-attribute side-panel, so this always returns empty. - /// Ignored. - /// Ignored. + /// public Task> AttributesAsync(string nodeId, CancellationToken cancellationToken) => Task.FromResult>(Array.Empty()); @@ -158,6 +147,7 @@ internal sealed class OpcUaClientBrowseSession : IBrowseSession /// Idempotent best-effort dispose: closes the underlying session if it's a /// concrete , disposes it, and disposes the gate. Close errors are /// swallowed because the registry reaper may be racing a remote disconnect. + /// A value task that represents the asynchronous dispose operation. public async ValueTask DisposeAsync() { if (_disposed) return; diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser/OpcUaClientDriverBrowser.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser/OpcUaClientDriverBrowser.cs index dd0a0e64..aedf9080 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser/OpcUaClientDriverBrowser.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser/OpcUaClientDriverBrowser.cs @@ -31,13 +31,10 @@ public sealed class OpcUaClientDriverBrowser : IDriverBrowser _logger = logger ?? NullLogger.Instance; } - /// Driver type key — matches the AdminUI's persisted "OpcUaClient" value. + /// public string DriverType => "OpcUaClient"; - /// Opens a transient OPC UA session and returns a browse session over it. - /// Driver options serialized as JSON; same shape the runtime - /// driver would consume. - /// Cancellation for the connect phase only. + /// public async Task OpenAsync(string configJson, CancellationToken cancellationToken) { var opts = JsonSerializer.Deserialize(configJson, JsonOpts) diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Contracts/NamespaceMap.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Contracts/NamespaceMap.cs index 82ab003f..67c110f7 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Contracts/NamespaceMap.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Contracts/NamespaceMap.cs @@ -48,6 +48,7 @@ public sealed class NamespaceMap /// Snapshot the namespace table from a live session. /// The OPC UA session to snapshot. + /// A new capturing the session's current namespace table. public static NamespaceMap FromSession(ISession session) { ArgumentNullException.ThrowIfNull(session); @@ -60,6 +61,7 @@ public sealed class NamespaceMap /// a live . /// /// The namespace table to snapshot. + /// A new capturing the supplied namespace table. public static NamespaceMap FromTable(NamespaceTable namespaceUris) { ArgumentNullException.ThrowIfNull(namespaceUris); @@ -71,11 +73,13 @@ public sealed class NamespaceMap /// The namespace URI at the given index, or null if out of range. /// The zero-based index in the namespace table. + /// The namespace URI string, or if is out of range. public string? UriForIndex(int index) => index >= 0 && index < _uris.Length ? _uris[index] : null; /// The index for a namespace URI, or null if the URI is not in the table. /// The namespace URI to look up. + /// The zero-based index, or if the URI is not present. public ushort? IndexForUri(string uri) => _uriToIndex.TryGetValue(uri, out var idx) ? idx : null; @@ -87,6 +91,7 @@ public sealed class NamespaceMap /// address space so it survives a remote namespace-table reorder. /// /// The NodeId to render as a stable reference. + /// A stable nsu=… reference string, or the raw node ID form for namespace 0 or unknown namespaces. public string ToStableReference(NodeId nodeId) { ArgumentNullException.ThrowIfNull(nodeId); @@ -115,6 +120,7 @@ public sealed class NamespaceMap /// The OPC UA session whose namespace table to resolve against. /// The reference string to resolve (either nsu=… or ns=N;… format). /// On success, the resolved NodeId; otherwise NodeId.Null. + /// if the reference was resolved to a non-null NodeId; otherwise . public static bool TryResolve(ISession currentSession, string reference, out NodeId nodeId) { nodeId = NodeId.Null; diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/OpcUaClientDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/OpcUaClientDriver.cs index d81ed3b7..651f8b25 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/OpcUaClientDriver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/OpcUaClientDriver.cs @@ -32,6 +32,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit { private readonly ILogger _logger; + /// Initializes a new instance of with the given options and instance ID. /// Driver configuration. /// Stable logical ID from the config DB. /// Optional logger; defaults to NullLogger when not supplied. @@ -121,14 +122,12 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit /// private NamespaceMap? _namespaceMap; - /// Gets the stable logical identifier for this driver instance from the config database. + /// public string DriverInstanceId => _driverInstanceId; - /// Gets the driver type identifier. + /// public string DriverType => "OpcUaClient"; - /// Initializes the OPC UA client driver with the given configuration. - /// JSON-serialized driver configuration. - /// Cancellation token for the operation. + /// public async Task InitializeAsync(string driverConfigJson, CancellationToken cancellationToken) { _health = new DriverHealth(DriverState.Initializing, null, null); @@ -303,6 +302,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit /// existing single-endpoint configs keep working without migration. /// /// Driver options containing endpoint configuration. + /// An ordered list of endpoint URL candidates for connection failover. internal static IReadOnlyList ResolveEndpointCandidates(OpcUaClientDriverOptions opts) { if (opts.EndpointUrls is { Count: > 0 }) return opts.EndpointUrls; @@ -352,6 +352,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit /// private key N times, wasteful + keeps the password in memory longer. /// /// Driver options containing authentication configuration. + /// A configured for the requested authentication type. internal static UserIdentity BuildUserIdentity(OpcUaClientDriverOptions options) => options.AuthType switch { @@ -455,6 +456,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit /// challenge during session activation). /// /// Driver options containing certificate configuration. + /// A backed by the loaded client user certificate. internal static UserIdentity BuildCertificateIdentity(OpcUaClientDriverOptions options) { if (string.IsNullOrWhiteSpace(options.UserCertificatePath)) @@ -482,6 +484,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit /// Convert a driver to the OPC UA policy URI. /// The driver security policy to map. + /// The OPC UA security policy URI string corresponding to the given policy. internal static string MapSecurityPolicy(OpcUaSecurityPolicy policy) => policy switch { OpcUaSecurityPolicy.None => SecurityPolicies.None, @@ -496,17 +499,14 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit private static string ShortPolicyName(string policyUri) => policyUri?.Substring(policyUri.LastIndexOf('#') + 1) ?? "(null)"; - /// Reinitializes the driver with new configuration, shutting down and restarting the session. - /// JSON-serialized driver configuration. - /// Cancellation token for the operation. + /// public async Task ReinitializeAsync(string driverConfigJson, CancellationToken cancellationToken) { await ShutdownAsync(cancellationToken).ConfigureAwait(false); await InitializeAsync(driverConfigJson, cancellationToken).ConfigureAwait(false); } - /// Gracefully shuts down the OPC UA session, unsubscribing all active monitoring items and closing the connection. - /// Cancellation token for the operation. + /// public async Task ShutdownAsync(CancellationToken cancellationToken) { // Tear down remote subscriptions first — otherwise Session.Close will try and may fail @@ -582,7 +582,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit _health = new DriverHealth(DriverState.Unknown, _health.LastSuccessfulRead, null); } - /// Gets the current health status of the OPC UA client driver. + /// public DriverHealth GetHealth() => _health; /// @@ -594,6 +594,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit /// 10k-node remote server reports ~5 MB — well within the budget and detectable by the /// Core slope alarm (Driver.OpcUaClient-013). /// + /// Approximate in-driver memory usage in bytes based on the discovered node count. public long GetMemoryFootprint() => _discoveredNodeCount * 512L; /// @@ -604,6 +605,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit /// will rebuild it cleanly from the remote server. /// /// Cancellation token for the operation. + /// A task that represents the asynchronous operation. public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) { _discoveredNodeCount = 0; @@ -612,9 +614,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit // ---- IReadable ---- - /// Reads the current values of the specified nodes from the remote OPC UA server. - /// Fully-qualified node identifiers to read. - /// Cancellation token for the operation. + /// public async Task> ReadAsync( IReadOnlyList fullReferences, CancellationToken cancellationToken) { @@ -703,9 +703,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit // ---- IWritable ---- - /// Writes values to the specified nodes on the remote OPC UA server. - /// Write requests specifying nodes and values to write. - /// Cancellation token for the operation. + /// public async Task> WriteAsync( IReadOnlyList writes, CancellationToken cancellationToken) { @@ -801,6 +799,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit /// The OPC UA session to resolve the node ID against. /// The full reference string to parse. /// The parsed node ID when successful. + /// if the reference was parsed to a valid node ID; otherwise. internal static bool TryParseNodeId(ISession session, string fullReference, out NodeId nodeId) => NamespaceMap.TryResolve(session, fullReference, out nodeId); @@ -817,9 +816,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit // ---- ITagDiscovery ---- - /// Discovers the remote OPC UA server's address space and materializes it through the supplied builder. - /// Address space builder for materializing discovered nodes. - /// Cancellation token for the operation. + /// public async Task DiscoverAsync(IAddressSpaceBuilder builder, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(builder); @@ -1086,6 +1083,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit /// StatusCode + timestamps. /// /// The OPC UA data type NodeId to map. + /// The corresponding to the given OPC UA data type. internal static DriverDataType MapUpstreamDataType(NodeId dataType) { if (dataType == DataTypeIds.Boolean) return DriverDataType.Boolean; @@ -1114,6 +1112,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit /// ; read-only as . /// /// The OPC UA access level bitmask. + /// The corresponding to the given access level. internal static SecurityClassification MapAccessLevelToSecurityClass(byte accessLevel) { const byte CurrentWrite = 2; // AccessLevels.CurrentWrite = 0x02 @@ -1124,10 +1123,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit // ---- ISubscribable ---- - /// Subscribes to monitored value changes on the specified nodes from the remote OPC UA server. - /// Fully-qualified node identifiers to monitor. - /// Desired minimum interval between publish cycles. - /// Cancellation token for the operation. + /// public async Task SubscribeAsync( IReadOnlyList fullReferences, TimeSpan publishingInterval, CancellationToken cancellationToken) { @@ -1198,9 +1194,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit return handle; } - /// Unsubscribes from monitored value changes for the specified subscription handle. - /// The subscription handle to unsubscribe. - /// Cancellation token for the operation. + /// public async Task UnsubscribeAsync(ISubscriptionHandle handle, CancellationToken cancellationToken) { if (handle is not OpcUaSubscriptionHandle h) return; @@ -1272,7 +1266,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit private sealed record OpcUaSubscriptionHandle(long Id) : ISubscriptionHandle { - /// Gets the diagnostic identifier for this subscription. + /// public string DiagnosticId => $"opcua-sub-{Id}"; } @@ -1290,9 +1284,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit private const int AlarmFieldTime = 5; private const int AlarmFieldConditionId = 6; - /// Subscribes to alarm and event notifications from the remote OPC UA server. - /// Source node identifiers to subscribe alarms from. - /// Cancellation token for the operation. + /// public async Task SubscribeAlarmsAsync( IReadOnlyList sourceNodeIds, CancellationToken cancellationToken) { @@ -1378,9 +1370,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit return handle; } - /// Unsubscribes from alarm and event notifications for the specified alarm subscription handle. - /// The alarm subscription handle to unsubscribe. - /// Cancellation token for the operation. + /// public async Task UnsubscribeAlarmsAsync(IAlarmSubscriptionHandle handle, CancellationToken cancellationToken) { if (handle is not OpcUaAlarmSubscriptionHandle h) return; @@ -1400,9 +1390,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit finally { _gate.Release(); } } - /// Acknowledges multiple alarms by calling the remote OPC UA server's Acknowledge method. - /// List of alarm acknowledgement requests. - /// Cancellation token for the operation. + /// public async Task AcknowledgeAsync( IReadOnlyList acknowledgements, CancellationToken cancellationToken) { @@ -1517,6 +1505,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit /// guidance: 1-200 Low, 201-500 Medium, 501-800 High, 801-1000 Critical. /// /// The OPC UA severity value (1-1000). + /// The coarse-grained bucket for the given OPC UA severity. internal static AlarmSeverity MapSeverity(ushort opcSeverity) => opcSeverity switch { <= 200 => AlarmSeverity.Low, @@ -1539,18 +1528,13 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit private sealed record OpcUaAlarmSubscriptionHandle(long Id) : IAlarmSubscriptionHandle { - /// Gets the diagnostic identifier for this alarm subscription. + /// public string DiagnosticId => $"opcua-alarm-sub-{Id}"; } // ---- IHistoryProvider (passthrough to upstream server) ---- - /// Reads raw historical data from the remote OPC UA server. - /// Fully-qualified node identifier to read history for. - /// Start time in UTC for the history query. - /// End time in UTC for the history query. - /// Maximum number of values to return. - /// Cancellation token for the operation. + /// public async Task ReadRawAsync( string fullReference, DateTime startUtc, DateTime endUtc, uint maxValuesPerNode, CancellationToken cancellationToken) @@ -1567,13 +1551,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit .ConfigureAwait(false); } - /// Reads processed (aggregated) historical data from the remote OPC UA server. - /// Fully-qualified node identifier to read history for. - /// Start time in UTC for the history query. - /// End time in UTC for the history query. - /// Time interval for aggregation. - /// The aggregation function to apply. - /// Cancellation token for the operation. + /// public async Task ReadProcessedAsync( string fullReference, DateTime startUtc, DateTime endUtc, TimeSpan interval, HistoryAggregateType aggregate, CancellationToken cancellationToken) @@ -1590,10 +1568,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit .ConfigureAwait(false); } - /// Reads historical data at specific timestamps from the remote OPC UA server. - /// Fully-qualified node identifier to read history for. - /// List of specific timestamps to read values at. - /// Cancellation token for the operation. + /// public async Task ReadAtTimeAsync( string fullReference, IReadOnlyList timestampsUtc, CancellationToken cancellationToken) { @@ -1665,6 +1640,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit /// Map to the OPC UA Part 13 standard aggregate NodeId. /// The aggregation function type to map. + /// The OPC UA aggregate function NodeId corresponding to the given aggregate type. internal static NodeId MapAggregateToNodeId(HistoryAggregateType aggregate) => aggregate switch { HistoryAggregateType.Average => ObjectIds.AggregateFunction_Average, @@ -1694,7 +1670,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit ?? ResolveEndpointCandidates(_options).FirstOrDefault() ?? _options.EndpointUrl; - /// Gets the current connectivity status of the remote OPC UA server host. + /// public IReadOnlyList GetHostStatuses() { lock (_probeLock) @@ -1927,6 +1903,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit } /// Asynchronously disposes the driver and releases all associated resources. + /// A task that represents the asynchronous dispose operation. public async ValueTask DisposeAsync() { if (_disposed) return; diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7CpuTypeMap.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7CpuTypeMap.cs index 9f591094..8de3c279 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7CpuTypeMap.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7CpuTypeMap.cs @@ -4,6 +4,9 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.S7; internal static class S7CpuTypeMap { + /// Maps an enum value to the equivalent S7.Net . + /// The S7 CPU type to convert. + /// The corresponding value. public static S7NetCpuType ToS7Net(S7CpuType type) => type switch { S7CpuType.S7200 => S7NetCpuType.S7200, diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7Driver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7Driver.cs index 312144dc..c63636f5 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7Driver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7Driver.cs @@ -103,15 +103,12 @@ public sealed class S7Driver(S7DriverOptions options, string driverInstanceId, I private DriverHealth _health = new(DriverState.Unknown, null, null); private bool _disposed; - /// Gets the unique driver instance identifier. + /// public string DriverInstanceId => driverInstanceId; - /// Gets the driver type name. + /// public string DriverType => "S7"; - /// Initializes the driver with the provided configuration. - /// JSON configuration string. - /// Cancellation token. - /// A task representing the asynchronous operation. + /// public async Task InitializeAsync(string driverConfigJson, CancellationToken cancellationToken) { _health = new DriverHealth(DriverState.Initializing, null, null); @@ -192,10 +189,7 @@ public sealed class S7Driver(S7DriverOptions options, string driverInstanceId, I } } - /// Reinitializes the driver with a new configuration. - /// JSON configuration string. - /// Cancellation token. - /// A task representing the asynchronous operation. + /// public async Task ReinitializeAsync(string driverConfigJson, CancellationToken cancellationToken) { // InitializeAsync re-parses driverConfigJson, so a config change delivered here is @@ -204,9 +198,7 @@ public sealed class S7Driver(S7DriverOptions options, string driverInstanceId, I await InitializeAsync(driverConfigJson, cancellationToken).ConfigureAwait(false); } - /// Shuts down the driver and releases resources. - /// Cancellation token. - /// A task representing the asynchronous operation. + /// public async Task ShutdownAsync(CancellationToken cancellationToken) { // Signal cancellation to the probe + poll loops first, collect their Task handles, @@ -251,13 +243,10 @@ public sealed class S7Driver(S7DriverOptions options, string driverInstanceId, I _health = new DriverHealth(DriverState.Unknown, _health.LastSuccessfulRead, null); } - /// Gets the current driver health. - /// The current health state. + /// public DriverHealth GetHealth() => _health; - /// Flushes optional caches to free memory. - /// Cancellation token. - /// A task representing the asynchronous operation. + /// public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; /// @@ -338,14 +327,12 @@ public sealed class S7Driver(S7DriverOptions options, string driverInstanceId, I /// under 4 KB; return 0 because the contract asks for a /// driver-attributable growth number and S7.Net doesn't expose one. /// + /// Zero, as S7.Net does not expose a queryable memory footprint. public long GetMemoryFootprint() => 0; // ---- IReadable ---- - /// Reads values from the specified tag references. - /// Tag references to read. - /// Cancellation token. - /// A task representing the asynchronous operation returning a list of data value snapshots. + /// public async Task> ReadAsync( IReadOnlyList fullReferences, CancellationToken cancellationToken) { @@ -457,10 +444,7 @@ public sealed class S7Driver(S7DriverOptions options, string driverInstanceId, I // ---- IWritable ---- - /// Writes values to the specified tags. - /// Write requests containing tag references and values. - /// Cancellation token. - /// A task representing the asynchronous operation returning a list of write results. + /// public async Task> WriteAsync( IReadOnlyList writes, CancellationToken cancellationToken) { @@ -597,10 +581,7 @@ public sealed class S7Driver(S7DriverOptions options, string driverInstanceId, I // ---- ITagDiscovery ---- - /// Discovers tags and builds the OPC UA address space. - /// Address space builder. - /// Cancellation token. - /// A task representing the asynchronous operation. + /// public Task DiscoverAsync(IAddressSpaceBuilder builder, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(builder); @@ -638,11 +619,7 @@ public sealed class S7Driver(S7DriverOptions options, string driverInstanceId, I // ---- ISubscribable (polling overlay) ---- - /// Subscribes to changes on the specified tag references. - /// Tag references to subscribe to. - /// Polling interval. - /// Cancellation token. - /// A task representing the asynchronous operation returning a subscription handle. + /// public Task SubscribeAsync( IReadOnlyList fullReferences, TimeSpan publishingInterval, CancellationToken cancellationToken) { @@ -662,10 +639,7 @@ public sealed class S7Driver(S7DriverOptions options, string driverInstanceId, I return Task.FromResult(handle); } - /// Unsubscribes from a subscription. - /// Subscription handle. - /// Cancellation token. - /// A task representing the asynchronous operation. + /// public Task UnsubscribeAsync(ISubscriptionHandle handle, CancellationToken cancellationToken) { if (handle is S7SubscriptionHandle h && _subscriptions.TryRemove(h.Id, out var state)) @@ -812,7 +786,7 @@ public sealed class S7Driver(S7DriverOptions options, string driverInstanceId, I private sealed record S7SubscriptionHandle(long Id) : ISubscriptionHandle { - /// Gets the diagnostic identifier for this subscription. + /// public string DiagnosticId => $"s7-sub-{Id}"; } @@ -825,8 +799,7 @@ public sealed class S7Driver(S7DriverOptions options, string driverInstanceId, I /// public string HostName => $"{_options.Host}:{_options.Port}"; - /// Gets the host connectivity statuses. - /// A list containing the current host status. + /// public IReadOnlyList GetHostStatuses() { lock (_probeLock) diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/ITwinCATClient.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/ITwinCATClient.cs index 03a12b4b..6b0eba9a 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/ITwinCATClient.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/ITwinCATClient.cs @@ -17,6 +17,7 @@ public interface ITwinCATClient : IDisposable /// The target AMS address. /// The connection timeout. /// Cancellation token for the connection attempt. + /// A task that represents the asynchronous operation. Task ConnectAsync(TwinCATAmsAddress address, TimeSpan timeout, CancellationToken cancellationToken); /// True when the AMS router + target both accept commands. @@ -41,6 +42,7 @@ public interface ITwinCATClient : IDisposable /// The target data type. /// Optional bit index for bit extraction within a word. /// Cancellation token for the read operation. + /// A tuple of the boxed value (or null) and the mapped OPC UA status code. Task<(object? value, uint status)> ReadValueAsync( string symbolPath, TwinCATDataType type, @@ -56,6 +58,7 @@ public interface ITwinCATClient : IDisposable /// Optional bit index for bit manipulation within a word. /// The value to write. /// Cancellation token for the write operation. + /// The mapped OPC UA status code (0 = Good). Task WriteValueAsync( string symbolPath, TwinCATDataType type, @@ -68,6 +71,7 @@ public interface ITwinCATClient : IDisposable /// Used by 's probe loop. /// /// Cancellation token for the probe operation. + /// true when the target's AMS state is reachable; otherwise false. Task ProbeAsync(CancellationToken cancellationToken); /// @@ -85,6 +89,7 @@ public interface ITwinCATClient : IDisposable /// immediately (Driver.TwinCAT-014). /// Invoked with (symbolPath, boxedValue) per notification. /// Cancels the initial registration; does not tear down an established notification. + /// An opaque handle whose tears the notification down. Task AddNotificationAsync( string symbolPath, TwinCATDataType type, @@ -102,6 +107,7 @@ public interface ITwinCATClient : IDisposable /// decide whether to drill in via their own walker. /// /// Cancellation token for the enumeration operation. + /// An async sequence of discovered symbols from the target's symbol table. IAsyncEnumerable BrowseSymbolsAsync(CancellationToken cancellationToken); } @@ -125,5 +131,6 @@ public sealed record TwinCATDiscoveredSymbol( public interface ITwinCATClientFactory { /// Creates a new TwinCAT client instance. + /// A new instance. ITwinCATClient Create(); } diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriver.cs index 89feddc4..e3cfe5ec 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriver.cs @@ -56,15 +56,12 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery OnDataChange?.Invoke(this, new DataChangeEventArgs(handle, tagRef, snapshot))); } - /// Gets the unique driver instance identifier. + /// public string DriverInstanceId => _driverInstanceId; - /// Gets the driver type name. + /// public string DriverType => "TwinCAT"; - /// Initializes the driver with configuration and establishes device connections. - /// JSON configuration string for the driver. - /// Cancellation token. - /// Completion task. + /// public Task InitializeAsync(string driverConfigJson, CancellationToken cancellationToken) { _health = new DriverHealth(DriverState.Initializing, null, null); @@ -109,19 +106,14 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery return Task.CompletedTask; } - /// Reinitializes the driver by shutting down and reinitializing with new configuration. - /// JSON configuration string for the driver. - /// Cancellation token. - /// Completion task. + /// public async Task ReinitializeAsync(string driverConfigJson, CancellationToken cancellationToken) { await ShutdownAsync(cancellationToken).ConfigureAwait(false); await InitializeAsync(driverConfigJson, cancellationToken).ConfigureAwait(false); } - /// Shuts down the driver and releases all device connections and subscriptions. - /// Cancellation token. - /// Completion task. + /// public async Task ShutdownAsync(CancellationToken cancellationToken) { // Native subs first — disposing the handles is cheap + lets the client close its @@ -154,8 +146,7 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery _health = new DriverHealth(DriverState.Unknown, _health.LastSuccessfulRead, null); } - /// Gets the current driver health status. - /// Driver health information. + /// public DriverHealth GetHealth() => _health; /// @@ -165,6 +156,7 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery /// ~256 bytes per pre-declared tag (tag-definition record + dictionary overhead) and /// ~512 bytes per active native subscription. /// + /// The estimated memory usage in bytes attributable to this driver instance. public long GetMemoryFootprint() => (_tagsByName.Count * 256L) + (_nativeSubs.Count * 512L); @@ -187,10 +179,7 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery // ---- IReadable ---- - /// Reads values for the specified tag references from ADS devices. - /// The full tag references to read. - /// Cancellation token. - /// Data value snapshots for each reference. + /// public async Task> ReadAsync( IReadOnlyList fullReferences, CancellationToken cancellationToken) { @@ -245,10 +234,7 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery // ---- IWritable ---- - /// Writes values to the specified tags on ADS devices. - /// The write requests to execute. - /// Cancellation token. - /// Write results for each request. + /// public async Task> WriteAsync( IReadOnlyList writes, CancellationToken cancellationToken) { @@ -309,10 +295,7 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery // ---- ITagDiscovery ---- - /// Discovers devices and tags from ADS configuration and optionally controller symbols. - /// Address space builder for adding discovered nodes. - /// Cancellation token. - /// Completion task. + /// public async Task DiscoverAsync(IAddressSpaceBuilder builder, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(builder); @@ -447,10 +430,7 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery return handle; } - /// Unsubscribes from a native or poll-based subscription. - /// The subscription handle to unsubscribe. - /// Cancellation token. - /// Completion task. + /// public Task UnsubscribeAsync(ISubscriptionHandle handle, CancellationToken cancellationToken) { if (handle is NativeSubscriptionHandle native && _nativeSubs.TryRemove(native.Id, out var sub)) @@ -464,7 +444,7 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery private sealed record NativeSubscriptionHandle(long Id) : ISubscriptionHandle { - /// Gets the diagnostic identifier for the subscription. + /// public string DiagnosticId => $"twincat-native-sub-{Id}"; } @@ -474,8 +454,7 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery // ---- IHostConnectivityProbe ---- - /// Gets the connectivity status for all configured devices. - /// List of host connectivity statuses. + /// public IReadOnlyList GetHostStatuses() => [.. _devices.Values.Select(s => new HostConnectivityStatus(s.Options.HostAddress, s.HostState, s.HostStateChangedUtc))]; @@ -536,9 +515,7 @@ public sealed class TwinCATDriver : IDriver, IReadable, IWritable, ITagDiscovery /// public const string UnresolvedHostSentinel = ""; - /// Resolves the device host address for the specified tag reference. - /// The full tag reference. - /// The host address or if not found. + /// public string ResolveHost(string fullReference) { if (_tagsByName.TryGetValue(fullReference, out var def)) diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriverFactoryExtensions.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriverFactoryExtensions.cs index 72430816..0195f04b 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriverFactoryExtensions.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATDriverFactoryExtensions.cs @@ -24,6 +24,7 @@ public static class TwinCATDriverFactoryExtensions /// Creates a TwinCAT driver instance from the provided configuration. /// The driver instance identifier. /// The driver configuration as JSON. + /// A new configured from the supplied JSON. internal static TwinCATDriver CreateInstance(string driverInstanceId, string driverConfigJson) { ArgumentException.ThrowIfNullOrWhiteSpace(driverInstanceId); @@ -39,6 +40,7 @@ public static class TwinCATDriverFactoryExtensions /// /// The JSON configuration string. /// The driver instance identifier. + /// The parsed . internal static TwinCATDriverOptions ParseOptions(string driverConfigJson, string driverInstanceId) { ArgumentException.ThrowIfNullOrWhiteSpace(driverConfigJson); @@ -79,6 +81,7 @@ public static class TwinCATDriverFactoryExtensions /// /// The JSON configuration string. /// The driver instance identifier. + /// The parsed . public static TwinCATDriverOptions ParseOptionsForTests(string driverConfigJson, string driverInstanceId) => ParseOptions(driverConfigJson, driverInstanceId); diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATSystemSymbolFilter.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATSystemSymbolFilter.cs index 7db22b59..9be270f0 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATSystemSymbolFilter.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT/TwinCATSystemSymbolFilter.cs @@ -10,6 +10,7 @@ public static class TwinCATSystemSymbolFilter { /// Gets a value indicating whether a symbol is a system or infrastructure symbol. /// The symbol instance path to check. + /// when the symbol is a TwinCAT system or infrastructure symbol; otherwise . public static bool IsSystemSymbol(string instancePath) { if (string.IsNullOrWhiteSpace(instancePath)) return true; diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Browsing/BrowseSessionReaper.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Browsing/BrowseSessionReaper.cs index cffe77a6..96f61753 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Browsing/BrowseSessionReaper.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Browsing/BrowseSessionReaper.cs @@ -38,6 +38,8 @@ public sealed class BrowseSessionReaper( /// Evicts every session whose /// is older than . Internal so tests can drive a tick directly. + /// Cancellation token for the reap operation. + /// A task that represents the asynchronous operation. internal async Task ReapOnceAsync(CancellationToken ct) { var now = DateTime.UtcNow; diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Browsing/BrowseSessionRegistry.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Browsing/BrowseSessionRegistry.cs index ca6feee9..112b8fb7 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Browsing/BrowseSessionRegistry.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Browsing/BrowseSessionRegistry.cs @@ -14,17 +14,25 @@ public sealed class BrowseSessionRegistry private readonly ConcurrentDictionary _sessions = new(); /// Adds (or replaces) a session in the registry keyed by its token. + /// The browse session to register. public void Register(IBrowseSession session) => _sessions[session.Token] = session; /// Looks up a session by token without removing it. + /// The token to look up. + /// The session, if found. + /// true if the session was found; otherwise false. public bool TryGet(Guid token, out IBrowseSession session) => _sessions.TryGetValue(token, out session!); /// Atomically removes a session from the registry, returning it for disposal. + /// The token of the session to remove. + /// The removed session, if found. + /// true if the session was removed; otherwise false. public bool TryRemove(Guid token, out IBrowseSession session) => _sessions.TryRemove(token, out session!); /// Returns a point-in-time snapshot of all currently registered sessions. + /// A read-only list of token/session pairs. public IReadOnlyList<(Guid Token, IBrowseSession Session)> Snapshot() => _sessions.Select(kv => (kv.Key, kv.Value)).ToList(); } diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Browsing/IBrowserSessionService.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Browsing/IBrowserSessionService.cs index b7b171bd..7fb57ed5 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Browsing/IBrowserSessionService.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Browsing/IBrowserSessionService.cs @@ -21,21 +21,38 @@ public interface IBrowserSessionService { /// Opens a session against the named driver type using the given JSON config. /// Never throws — all errors are surfaced via . + /// The driver type name to open a browse session for. + /// The driver configuration as a JSON string. + /// Cancellation token. + /// A task that resolves to the open result, including a token on success. Task OpenAsync(string driverType, string configJson, CancellationToken ct); /// Returns the root nodes of an open session. Throws /// if the token is unknown. + /// The session token returned by . + /// Cancellation token. + /// A task that resolves to the root browse nodes. Task> RootAsync(Guid token, CancellationToken ct); /// Returns the direct children of in an open session. /// Throws if the token is unknown. + /// The session token returned by . + /// The node identifier to expand. + /// Cancellation token. + /// A task that resolves to the child browse nodes. Task> ExpandAsync(Guid token, string nodeId, CancellationToken ct); /// Returns the attributes of in an open session. Throws /// if the token is unknown. + /// The session token returned by . + /// The node identifier to read attributes for. + /// Cancellation token. + /// A task that resolves to the attribute information for the node. Task> AttributesAsync(Guid token, string nodeId, CancellationToken ct); /// Removes the session from the registry and disposes it. No-op for unknown tokens. + /// The session token to close. + /// A task that represents the asynchronous operation. Task CloseAsync(Guid token); } diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Clients/AdminOperationsClient.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Clients/AdminOperationsClient.cs index 651ec286..8dbec6c0 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Clients/AdminOperationsClient.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Clients/AdminOperationsClient.cs @@ -25,10 +25,7 @@ public sealed class AdminOperationsClient : IAdminOperationsClient _proxy = registry.Get(); } - /// Starts a deployment via the admin operations actor. - /// The username of who initiated the deployment. - /// The cancellation token. - /// The deployment start result. + /// public async Task StartDeploymentAsync(string createdBy, CancellationToken ct) { var msg = new StartDeployment(createdBy, CorrelationId.NewId()); @@ -42,6 +39,10 @@ public sealed class AdminOperationsClient : IAdminOperationsClient /// Uses the caller-supplied for cancellation; does not impose an /// additional internal timeout beyond what the proxy itself enforces. /// + /// The expected response type from the actor. + /// The message to forward to the AdminOperationsActor singleton. + /// Cancellation token for the ask operation. + /// A task that resolves to the actor's response of type . public Task AskAsync(object message, CancellationToken ct) => _proxy.Ask(message, cancellationToken: ct); } diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Clients/AdminProbeService.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Clients/AdminProbeService.cs index 49415b66..4a9d5792 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Clients/AdminProbeService.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Clients/AdminProbeService.cs @@ -25,6 +25,7 @@ public sealed class AdminProbeService /// Driver config as JSON (same shape as DriverInstance.DriverConfig). /// Per-probe timeout; actor clamps to [1, 60]. /// Optional cancellation token from the caller. + /// A task that resolves to the from the probe. public async Task TestAsync( string driverType, string configJson, diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Clients/FleetDiagnosticsClient.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Clients/FleetDiagnosticsClient.cs index 1780d899..81a0b6cc 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Clients/FleetDiagnosticsClient.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Clients/FleetDiagnosticsClient.cs @@ -31,10 +31,7 @@ public sealed class FleetDiagnosticsClient : IFleetDiagnosticsClient _systemName = options.Value.SystemName; } - /// Gets diagnostics for a cluster node. - /// The node identifier to query. - /// Cancellation token. - /// Diagnostics snapshot for the node, or an empty snapshot if the query fails. + /// public async Task GetDiagnosticsAsync(NodeId nodeId, CancellationToken ct) { var selection = _system.ActorSelection($"akka.tcp://{_systemName}@{nodeId.Value}/user/driver-host"); diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Clients/ServiceCollectionExtensions.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Clients/ServiceCollectionExtensions.cs index c0d947a2..8aa8debb 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Clients/ServiceCollectionExtensions.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Clients/ServiceCollectionExtensions.cs @@ -10,6 +10,7 @@ public static class ServiceCollectionExtensions /// Registers the Admin UI client services in the dependency injection container. /// /// The service collection to register clients into. + /// The same service collection for chaining. public static IServiceCollection AddOtOpcUaAdminClients(this IServiceCollection services) { services.AddScoped(); diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Pickers/AbLegacyAddressBuilder.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Pickers/AbLegacyAddressBuilder.cs index 03137eaf..a9a3d459 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Pickers/AbLegacyAddressBuilder.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Pickers/AbLegacyAddressBuilder.cs @@ -7,6 +7,11 @@ namespace ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.Pickers; /// public static class AbLegacyAddressBuilder { + /// Builds the canonical AB Legacy PLC address string from its components. + /// The file-type prefix (e.g. "N", "F", "B"). + /// The file number. + /// The element index within the file. + /// The canonical address string, for example N7:0. public static string Build(string fileType, int fileNumber, int element) => $"{fileType}{fileNumber}:{element}"; } diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Pickers/FocasAddressBuilder.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Pickers/FocasAddressBuilder.cs index b5ebd94c..729c1a77 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Pickers/FocasAddressBuilder.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Pickers/FocasAddressBuilder.cs @@ -7,6 +7,10 @@ namespace ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.Pickers; /// public static class FocasAddressBuilder { + /// Builds a canonical FOCAS address string from a parameter group and parameter ID. + /// The FOCAS parameter group (e.g. "axis"). + /// The FOCAS parameter identifier. + /// The canonical address string in the form group:parameterId. public static string Build(string group, int parameterId) => $"{group}:{parameterId}"; } diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Pickers/HistorianWonderwareAddressBuilder.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Pickers/HistorianWonderwareAddressBuilder.cs index 2fc5b467..df0e19a4 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Pickers/HistorianWonderwareAddressBuilder.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Pickers/HistorianWonderwareAddressBuilder.cs @@ -7,6 +7,11 @@ namespace ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.Pickers; /// public static class HistorianWonderwareAddressBuilder { + /// Builds a Wonderware Historian address query string from tag name, mode, and interval. + /// The Wonderware Historian tag name. + /// The retrieval mode (e.g., Cyclic, Delta). + /// The retrieval interval in seconds. + /// The canonical address query string. public static string Build(string tagName, string mode, int interval) => $"{tagName}?mode={mode}&interval={interval}"; } diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Pickers/ModbusAddressBuilder.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Pickers/ModbusAddressBuilder.cs index 13b90164..3aee2225 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Pickers/ModbusAddressBuilder.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Pickers/ModbusAddressBuilder.cs @@ -7,6 +7,11 @@ namespace ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.Pickers; /// public static class ModbusAddressBuilder { + /// Builds a canonical Modbus address string from the given register type, offset, and length. + /// The register type (Coil, DiscreteInput, Input, or Holding). + /// The zero-based register offset. + /// The number of registers. + /// The canonical address string in the form Xx00000-N. public static string Build(string regType, int offset, int length) { var prefix = regType switch diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Pickers/S7AddressBuilder.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Pickers/S7AddressBuilder.cs index 6280681d..7ec13e1b 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Pickers/S7AddressBuilder.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Pickers/S7AddressBuilder.cs @@ -7,10 +7,12 @@ namespace ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.Pickers; /// public static class S7AddressBuilder { + /// Builds the canonical S7 address string from the given components. /// DB / M / I / Q /// Only relevant when area == "DB". /// Byte offset (decimal). /// X / B / W / D / REAL + /// The canonical S7 address string (e.g. DB10.DBD20:REAL, M0.0:X). public static string Build(string area, int dbNumber, int offset, string s7Type) { if (area == "DB") diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/ResilienceFormModel.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/ResilienceFormModel.cs index 729b6ab3..bc7178da 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/ResilienceFormModel.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/ResilienceFormModel.cs @@ -14,19 +14,27 @@ public sealed class ResilienceFormModel public static readonly string[] Capabilities = ["Read", "Write", "Discover", "Subscribe", "Probe", "AlarmSubscribe", "AlarmAcknowledge", "HistoryRead"]; + /// Gets or sets the maximum number of concurrent bulkhead operations, or to use the driver default. public int? BulkheadMaxConcurrent { get; set; } + /// Gets or sets the maximum bulkhead queue depth, or to use the driver default. public int? BulkheadMaxQueue { get; set; } + /// Gets or sets the driver recycle interval in seconds, or to use the driver default. public int? RecycleIntervalSeconds { get; set; } // capability name -> (timeout, retry, breaker), each nullable. + /// Gets or sets the per-capability policy overrides keyed by capability name. public Dictionary Policies { get; set; } = Capabilities.ToDictionary(c => c, _ => new CapabilityRow(), StringComparer.OrdinalIgnoreCase); public sealed class CapabilityRow { + /// Gets or sets the timeout in seconds for this capability, or to use the driver default. public int? TimeoutSeconds { get; set; } + /// Gets or sets the retry count for this capability, or to use the driver default. public int? RetryCount { get; set; } + /// Gets or sets the circuit-breaker failure threshold for this capability, or to use the driver default. public int? BreakerFailureThreshold { get; set; } + /// Gets a value indicating whether all policy fields are null (no overrides set). public bool IsEmpty => TimeoutSeconds is null && RetryCount is null && BreakerFailureThreshold is null; } @@ -37,6 +45,9 @@ public sealed class ResilienceFormModel DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, }; + /// Deserializes a from the given JSON string, returning an empty model on null or malformed input. + /// The JSON string to deserialize, or . + /// A populated , or an empty one if is null/blank/malformed. public static ResilienceFormModel FromJson(string? json) { var model = new ResilienceFormModel(); @@ -62,6 +73,7 @@ public sealed class ResilienceFormModel } /// Emit only the non-null overrides; returns null when nothing is overridden. + /// A JSON string containing only the non-null overrides, or if nothing is overridden. public string? ToJson() { var caps = Policies @@ -89,16 +101,23 @@ public sealed class ResilienceFormModel private sealed class Shape { + /// Gets or sets the maximum number of concurrent bulkhead operations. public int? BulkheadMaxConcurrent { get; set; } + /// Gets or sets the maximum bulkhead queue depth. public int? BulkheadMaxQueue { get; set; } + /// Gets or sets the recycle interval in seconds. public int? RecycleIntervalSeconds { get; set; } + /// Gets or sets the per-capability policy shapes. public Dictionary? CapabilityPolicies { get; set; } } private sealed class PolicyShape { + /// Gets or sets the timeout in seconds. public int? TimeoutSeconds { get; set; } + /// Gets or sets the retry count. public int? RetryCount { get; set; } + /// Gets or sets the circuit-breaker failure threshold. public int? BreakerFailureThreshold { get; set; } } } diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/EndpointRouteBuilderExtensions.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/EndpointRouteBuilderExtensions.cs index eafdbd44..90959eda 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/EndpointRouteBuilderExtensions.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/EndpointRouteBuilderExtensions.cs @@ -18,6 +18,7 @@ public static class EndpointRouteBuilderExtensions /// /// The root component type for Razor pages. /// The endpoint route builder. + /// The same for chaining. public static IEndpointRouteBuilder MapAdminUI(this IEndpointRouteBuilder app) where TApp : IComponent { @@ -33,6 +34,7 @@ public static class EndpointRouteBuilderExtensions /// Adds AdminUI services to the dependency injection container. /// /// The service collection. + /// The same for chaining. public static IServiceCollection AddAdminUI(this IServiceCollection services) { services.AddRazorComponents().AddInteractiveServerComponents(); diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/AlertSignalRBridge.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/AlertSignalRBridge.cs index 906db2a3..145a0683 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/AlertSignalRBridge.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/AlertSignalRBridge.cs @@ -25,6 +25,7 @@ public sealed class AlertSignalRBridge : ReceiveActor /// /// The SignalR hub context to send alerts to. /// In-process fan-out read directly by the Blazor Server Alerts page. + /// Akka for creating an actor. public static Props Props(IHubContext hub, IInProcessBroadcaster broadcaster) => Akka.Actor.Props.Create(() => new AlertSignalRBridge(hub, broadcaster)); diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/DriverStatusHub.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/DriverStatusHub.cs index 88a70ee7..0a4de860 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/DriverStatusHub.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/DriverStatusHub.cs @@ -28,6 +28,8 @@ public sealed class DriverStatusHub : Hub /// the current snapshot from the in-memory store so the client renders state without /// waiting for the next change event. /// + /// The driver instance identifier to subscribe to. + /// A task that represents the asynchronous operation. public async Task JoinDriver(string driverInstanceId) { var groupName = GroupName(driverInstanceId); @@ -40,5 +42,7 @@ public sealed class DriverStatusHub : Hub } /// Builds the SignalR group name for a given driver instance. + /// The driver instance identifier. + /// The SignalR group name for the specified driver instance. public static string GroupName(string driverInstanceId) => $"driver:{driverInstanceId}"; } diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/DriverStatusSignalRBridge.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/DriverStatusSignalRBridge.cs index 6fc52c9b..88a8215c 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/DriverStatusSignalRBridge.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/DriverStatusSignalRBridge.cs @@ -24,6 +24,7 @@ public sealed class DriverStatusSignalRBridge : ReceiveActor /// Creates actor props for a . /// The SignalR hub context for pushing snapshots to grouped clients. /// Snapshot store updated before each SignalR push. + /// Akka for creating a actor. public static Props Props(IHubContext hub, IDriverStatusSnapshotStore store) => Akka.Actor.Props.Create(() => new DriverStatusSignalRBridge(hub, store)); diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/HubRouteBuilderExtensions.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/HubRouteBuilderExtensions.cs index f30b5a55..5e707e48 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/HubRouteBuilderExtensions.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/HubRouteBuilderExtensions.cs @@ -10,6 +10,7 @@ public static class HubRouteBuilderExtensions /// Maps all OtOpcUa Admin UI SignalR hubs to their configured endpoints. /// /// The endpoint route builder to register hubs on. + /// The same for chaining. public static IEndpointRouteBuilder MapOtOpcUaHubs(this IEndpointRouteBuilder app) { app.MapHub(FleetStatusHub.Endpoint); diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/HubServiceCollectionExtensions.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/HubServiceCollectionExtensions.cs index 3e5a2fff..0e1096e1 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/HubServiceCollectionExtensions.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/HubServiceCollectionExtensions.cs @@ -24,6 +24,7 @@ public static class HubServiceCollectionExtensions /// /// /// The service collection. + /// The same for chaining. public static IServiceCollection AddOtOpcUaDriverStatusServices(this IServiceCollection services) { services.AddSingleton(); @@ -46,6 +47,7 @@ public static class HubServiceCollectionExtensions /// /// /// The Akka configuration builder. + /// The same for chaining. public static AkkaConfigurationBuilder WithOtOpcUaSignalRBridges(this AkkaConfigurationBuilder builder) { builder.WithActors((system, registry, resolver) => diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/IDriverStatusSnapshotStore.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/IDriverStatusSnapshotStore.cs index 0527d555..baa49adf 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/IDriverStatusSnapshotStore.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/IDriverStatusSnapshotStore.cs @@ -11,7 +11,14 @@ namespace ZB.MOM.WW.OtOpcUa.AdminUI.Hubs; /// public interface IDriverStatusSnapshotStore { + /// Stores or updates the latest health snapshot for the given driver instance. + /// The health snapshot to store. void Upsert(DriverHealthChanged snapshot); + + /// Attempts to retrieve the latest snapshot for the specified driver instance. + /// The driver instance identifier to look up. + /// The latest snapshot if found; otherwise the default value. + /// if a snapshot was found; otherwise . bool TryGet(string driverInstanceId, out DriverHealthChanged snapshot); /// diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/IInProcessBroadcaster.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/IInProcessBroadcaster.cs index 46e0d9e8..f4c5f2eb 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/IInProcessBroadcaster.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/IInProcessBroadcaster.cs @@ -25,6 +25,7 @@ public interface IInProcessBroadcaster event Action? Received; /// Fan the item out to all current subscribers. + /// The event payload to broadcast to all subscribers. void Publish(T item); } @@ -32,7 +33,7 @@ public interface IInProcessBroadcaster /// The event payload type. public sealed class InProcessBroadcaster : IInProcessBroadcaster { - /// + /// Raised once per with the published item. public event Action? Received; /// diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/InMemoryDriverStatusSnapshotStore.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/InMemoryDriverStatusSnapshotStore.cs index 353fe6de..76170b90 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/InMemoryDriverStatusSnapshotStore.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/InMemoryDriverStatusSnapshotStore.cs @@ -11,7 +11,7 @@ public sealed class InMemoryDriverStatusSnapshotStore : IDriverStatusSnapshotSto { private readonly ConcurrentDictionary _byInstance = new(); - /// + /// Raised after every with the just-stored snapshot, allowing in-process consumers to receive live updates. public event Action? SnapshotChanged; /// diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/ScriptLogSignalRBridge.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/ScriptLogSignalRBridge.cs index 8fac9fb6..9de983f7 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/ScriptLogSignalRBridge.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/ScriptLogSignalRBridge.cs @@ -21,6 +21,7 @@ public sealed class ScriptLogSignalRBridge : ReceiveActor /// Creates a Props instance for the ScriptLogSignalRBridge. /// The SignalR hub context for sending messages to clients. /// In-process fan-out read directly by the Blazor Server Script log page. + /// An Akka instance for creating a actor. public static Props Props(IHubContext hub, IInProcessBroadcaster broadcaster) => Akka.Actor.Props.Create(() => new ScriptLogSignalRBridge(hub, broadcaster)); diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/AdminOperations/ConfigComposer.cs b/src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/AdminOperations/ConfigComposer.cs index b5c637c9..a6cae369 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/AdminOperations/ConfigComposer.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/AdminOperations/ConfigComposer.cs @@ -25,6 +25,7 @@ public static class ConfigComposer /// Reads the current configuration and returns a deterministic snapshot blob with revision hash. /// The configuration database context. /// The cancellation token for the operation. + /// A task resolving to a containing the serialized blob and its SHA-256 hash. public static async Task SnapshotAndFlattenAsync( OtOpcUaConfigDbContext db, CancellationToken ct = default) { @@ -53,6 +54,7 @@ public static class ConfigComposer /// Returns the SHA-256 hex digest of the supplied artifact bytes (lowercase, no prefix). /// The bytes to hash. + /// The lowercase hex-encoded SHA-256 digest of the input bytes. public static string HashOf(ReadOnlySpan blob) => Convert.ToHexStringLower(SHA256.HashData(blob)); } diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/Audit/AuditWriterActor.cs b/src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/Audit/AuditWriterActor.cs index 9f1d3f4a..7474b153 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/Audit/AuditWriterActor.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/Audit/AuditWriterActor.cs @@ -39,6 +39,7 @@ public sealed class AuditWriterActor : ReceiveActor, IWithTimers, IAuditWriter /// Creates a Props factory for the AuditWriterActor. /// The database context factory for creating ConfigDb connections. + /// A Props for creating the . public static Props Props(IDbContextFactory dbFactory) => Akka.Actor.Props.Create(() => new AuditWriterActor(dbFactory)); diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/Coordinators/ConfigPublishCoordinator.cs b/src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/Coordinators/ConfigPublishCoordinator.cs index e6cf5ce6..77aa6a01 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/Coordinators/ConfigPublishCoordinator.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/Coordinators/ConfigPublishCoordinator.cs @@ -41,6 +41,7 @@ public sealed class ConfigPublishCoordinator : ReceiveActor, IWithTimers /// Creates actor props for the ConfigPublishCoordinator with the specified configuration. /// The database context factory for accessing configuration data. /// The timeout for waiting for apply acknowledgments from cluster nodes. + /// Akka to create a actor. public static Props Props( IDbContextFactory dbFactory, TimeSpan? applyDeadline = null) => diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Host/Drivers/DriverFactoryBootstrap.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Host/Drivers/DriverFactoryBootstrap.cs index 26ef2284..1671b053 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Host/Drivers/DriverFactoryBootstrap.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Host/Drivers/DriverFactoryBootstrap.cs @@ -37,6 +37,7 @@ public static class DriverFactoryBootstrap /// from DI when spawning DriverHostActor. /// /// The service collection to register driver factories with. + /// The instance for chaining. public static IServiceCollection AddOtOpcUaDriverFactories(this IServiceCollection services) { services.AddSingleton(sp => @@ -74,6 +75,7 @@ public static class DriverFactoryBootstrap /// /// /// The service collection to register driver probes with. + /// The instance for chaining. public static IServiceCollection AddOtOpcUaDriverProbes(this IServiceCollection services) { services.TryAddEnumerable(ServiceDescriptor.Singleton()); diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Host/Health/HealthEndpoints.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Host/Health/HealthEndpoints.cs index 5e29f6c7..bcef81d7 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Host/Health/HealthEndpoints.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Host/Health/HealthEndpoints.cs @@ -14,6 +14,8 @@ public static class HealthEndpoints /// Registers the shared ZB.MOM.WW health probes. Tier semantics preserved: configdb + akka on /// ready+active; admin-leader on active only. /// + /// The service collection to register health checks with. + /// The same for chaining. public static IServiceCollection AddOtOpcUaHealth(this IServiceCollection services) { services.AddHealthChecks() @@ -40,6 +42,7 @@ public static class HealthEndpoints /// Maps the OtOpcUa health check endpoints to the route builder. /// The endpoint route builder. + /// The same for chaining. public static IEndpointRouteBuilder MapOtOpcUaHealth(this IEndpointRouteBuilder app) { app.MapZbHealth(); diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Host/Observability/ObservabilityExtensions.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Host/Observability/ObservabilityExtensions.cs index 34f0d665..da7f3dbd 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Host/Observability/ObservabilityExtensions.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Host/Observability/ObservabilityExtensions.cs @@ -21,6 +21,7 @@ public static class ObservabilityExtensions /// Otlp; OtOpcUa:Telemetry:OtlpEndpoint sets the OTLP endpoint. With no /// config the exporter stays Prometheus (the default). /// + /// The for chaining. public static IServiceCollection AddOtOpcUaObservability(this IServiceCollection services, IConfiguration configuration) { return services.AddZbTelemetry(o => @@ -42,6 +43,7 @@ public static class ObservabilityExtensions /// the default leaves it unauthenticated for local Prometheus scrapes. /// /// The endpoint route builder. + /// The for chaining. public static IEndpointRouteBuilder MapOtOpcUaMetrics(this IEndpointRouteBuilder app) { app.MapZbMetrics(); diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Host/OpcUa/LdapOpcUaUserAuthenticator.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Host/OpcUa/LdapOpcUaUserAuthenticator.cs index 62858193..47d2a692 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Host/OpcUa/LdapOpcUaUserAuthenticator.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Host/OpcUa/LdapOpcUaUserAuthenticator.cs @@ -30,6 +30,7 @@ public sealed class LdapOpcUaUserAuthenticator( /// The username to authenticate. /// The password to authenticate. /// Cancellation token. + /// A task that resolves to the authentication result (allow or deny with roles). public async Task AuthenticateUserNameAsync(string username, string password, CancellationToken ct) { try diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Host/OpcUa/OtOpcUaServerHostedService.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Host/OpcUa/OtOpcUaServerHostedService.cs index 32506e50..ecc8f8bc 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Host/OpcUa/OtOpcUaServerHostedService.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Host/OpcUa/OtOpcUaServerHostedService.cs @@ -57,6 +57,7 @@ public sealed class OtOpcUaServerHostedService : IHostedService, IAsyncDisposabl /// Starts the OPC UA server asynchronously. /// /// Cancellation token. + /// A task that represents the asynchronous start operation. public async Task StartAsync(CancellationToken cancellationToken) { _server = new OtOpcUaSdkServer(); @@ -103,6 +104,7 @@ public sealed class OtOpcUaServerHostedService : IHostedService, IAsyncDisposabl /// Stops the OPC UA server asynchronously. /// /// Cancellation token. + /// A task that represents the asynchronous stop operation. public Task StopAsync(CancellationToken cancellationToken) { // Revert to Null adapters so any in-flight writes from a poison-pilled actor don't hit a @@ -115,6 +117,7 @@ public sealed class OtOpcUaServerHostedService : IHostedService, IAsyncDisposabl /// /// Disposes the hosted service and its resources asynchronously. /// + /// A value task that represents the asynchronous dispose operation. public async ValueTask DisposeAsync() { if (_appHost is not null) await _appHost.DisposeAsync(); diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/OpcUaApplicationHost.cs b/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/OpcUaApplicationHost.cs index 5bd459dd..77b01c31 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/OpcUaApplicationHost.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/OpcUaApplicationHost.cs @@ -122,6 +122,7 @@ public sealed class OpcUaApplicationHost : IAsyncDisposable /// Starts the OPC UA application and server. /// The standard server instance to start. /// A cancellation token for the operation. + /// A task that represents the asynchronous start operation. public async Task StartAsync(StandardServer server, CancellationToken cancellationToken) { _server = server; @@ -371,6 +372,7 @@ public sealed class OpcUaApplicationHost : IAsyncDisposable /// listening endpoints — the misconfiguration is logged and very visible. /// /// The security profiles to build policies for. + /// An enumerable of instances, one per unique profile. internal static IEnumerable BuildSecurityPolicies(IEnumerable profiles) { var seen = new HashSet(); @@ -415,6 +417,7 @@ public sealed class OpcUaApplicationHost : IAsyncDisposable /// policy works on None endpoints too. F13c will plug a real LDAP-bound validator into /// StandardServer.SessionManager.ImpersonateUser. /// + /// An enumerable of instances for anonymous and username token types. internal static IEnumerable BuildUserTokenPolicies() { yield return new UserTokenPolicy(UserTokenType.Anonymous) @@ -430,6 +433,7 @@ public sealed class OpcUaApplicationHost : IAsyncDisposable } /// Disposes the application host and cleans up resources. + /// A that represents the asynchronous dispose operation. public ValueTask DisposeAsync() { if (_impersonateHandler is not null && _server?.CurrentInstance?.SessionManager is { } sessionManager) diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Phase7Plan.cs b/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Phase7Plan.cs index de2e7c2b..47c19602 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Phase7Plan.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Phase7Plan.cs @@ -49,6 +49,7 @@ public static class Phase7Planner /// /// The previous composition result. /// The new composition result. + /// A describing the added, removed, and changed entities. public static Phase7Plan Compute(Phase7CompositionResult previous, Phase7CompositionResult next) { ArgumentNullException.ThrowIfNull(previous); diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/SdkAddressSpaceSink.cs b/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/SdkAddressSpaceSink.cs index 69fb2135..598f248d 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/SdkAddressSpaceSink.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/SdkAddressSpaceSink.cs @@ -20,37 +20,22 @@ public sealed class SdkAddressSpaceSink : IOpcUaAddressSpaceSink _nodeManager = nodeManager; } - /// Writes a value to the OPC UA address space. - /// The OPC UA node identifier. - /// The value being written. - /// The OPC UA quality status. - /// The source timestamp in UTC. + /// public void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc) => _nodeManager.WriteValue(nodeId, value, quality, sourceTimestampUtc); - /// Writes alarm state to the OPC UA address space. - /// The alarm node identifier. - /// Whether the alarm is active. - /// Whether the alarm is acknowledged. - /// The source timestamp in UTC. + /// public void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime sourceTimestampUtc) => _nodeManager.WriteAlarmState(alarmNodeId, active, acknowledged, sourceTimestampUtc); - /// Ensures a folder node exists in the address space. - /// The folder node identifier. - /// The parent folder node identifier. - /// The display name for the folder. + /// public void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName) => _nodeManager.EnsureFolder(folderNodeId, parentNodeId, displayName); - /// Ensures a variable node exists in the address space. - /// The variable node identifier. - /// The parent folder node identifier. - /// The display name for the variable. - /// The OPC UA data type. + /// public void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType) => _nodeManager.EnsureVariable(variableNodeId, parentFolderNodeId, displayName, dataType); - /// Rebuilds the entire OPC UA address space. + /// public void RebuildAddressSpace() => _nodeManager.RebuildAddressSpace(); } diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/SdkServiceLevelPublisher.cs b/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/SdkServiceLevelPublisher.cs index b3f34461..e3d8f708 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/SdkServiceLevelPublisher.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/SdkServiceLevelPublisher.cs @@ -32,8 +32,7 @@ public sealed class SdkServiceLevelPublisher : IServiceLevelPublisher _logger = logger; } - /// Publishes the service level to the OPC UA Server object. - /// The service level value to publish. + /// public void Publish(byte serviceLevel) { var node = _serverInternal.ServerObject?.ServiceLevel; diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DeploymentArtifact.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DeploymentArtifact.cs index 93368eeb..ae516f65 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DeploymentArtifact.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DeploymentArtifact.cs @@ -30,6 +30,7 @@ public static class DeploymentArtifact /// Empty / malformed blobs return an empty list — callers log + treat as "no drivers". /// /// The deployment artifact blob to parse. + /// The parsed list of driver instance specs, or an empty list for empty or malformed input. public static IReadOnlyList ParseDriverInstances(ReadOnlySpan blob) { if (blob.IsEmpty) return Array.Empty(); @@ -92,6 +93,7 @@ public static class DeploymentArtifact /// nodes. /// /// The deployment artifact blob to parse. + /// The projected composition result, or an empty composition for empty or malformed input. public static Phase7CompositionResult ParseComposition(ReadOnlySpan blob) { if (blob.IsEmpty) return Empty(); diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverHostActor.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverHostActor.cs index 3e0d2fe9..cd2a475e 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverHostActor.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverHostActor.cs @@ -58,11 +58,13 @@ public sealed class DriverHostActor : ReceiveActor, IWithTimers private sealed record ChildEntry(IActorRef Actor, DriverInstanceSpec Spec, bool Stubbed) { // Convenience accessors for sites that don't need the full spec. + /// Gets the driver type key for this child entry. public string DriverType => Spec.DriverType; + /// Gets the last serialized driver configuration JSON for this child entry. public string LastConfigJson => Spec.DriverConfig; } - /// + /// Gets or sets the Akka timer scheduler used by this actor for recurring messages. public ITimerScheduler Timers { get; set; } = null!; public sealed class RetryConfigDbConnection @@ -81,6 +83,7 @@ public sealed class DriverHostActor : ReceiveActor, IWithTimers /// Optional actor reference for OPC UA publishing. /// Optional driver-health publisher; defaults to /// so test harnesses and smoke fixtures don't need to wire it. + /// An Akka instance for creating a . public static Props Props( IDbContextFactory dbFactory, CommonsNodeId localNode, diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverInstanceActor.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverInstanceActor.cs index b6f8a296..c525be23 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverInstanceActor.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverInstanceActor.cs @@ -100,6 +100,7 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers /// stub paths don't need to provide one. /// Optional cluster identifier forwarded in messages; /// defaults to an empty string when not provided (e.g. in unit tests). + /// Akka Props for creating a . public static Props Props( IDriver driver, TimeSpan? reconnectInterval = null, @@ -127,6 +128,7 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers /// /// The type identifier of the driver. /// Operational roles configured for this instance. + /// true when the driver should start in DEV-STUB mode. public static bool ShouldStub(string driverType, IEnumerable roles) { var isWindowsOnly = driverType is "Galaxy" or "Historian.Wonderware"; diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverSpawnPlan.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverSpawnPlan.cs index 189d2d53..d12c473f 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverSpawnPlan.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverSpawnPlan.cs @@ -23,6 +23,7 @@ public static class DriverSpawnPlanner /// /// The currently running driver children keyed by ID. /// The target driver instances from the deployment artifact. + /// A containing the spawn, delta, and stop sets. public static DriverSpawnPlan Compute( IReadOnlyDictionary current, IReadOnlyList target) diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Health/DbHealthProbeActor.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Health/DbHealthProbeActor.cs index 61762d8d..d62eab23 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Health/DbHealthProbeActor.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Health/DbHealthProbeActor.cs @@ -28,6 +28,7 @@ public sealed class DbHealthProbeActor : ReceiveActor, IWithTimers /// Creates a Props instance for the DbHealthProbeActor. /// The factory for creating ConfigDb contexts. + /// Akka for constructing a . public static Props Props(IDbContextFactory dbFactory) => Akka.Actor.Props.Create(() => new DbHealthProbeActor(dbFactory)); diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/OpcUa/OpcUaPublishActor.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/OpcUa/OpcUaPublishActor.cs index 6fed3160..b3b23f12 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/OpcUa/OpcUaPublishActor.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/OpcUa/OpcUaPublishActor.cs @@ -67,6 +67,7 @@ public sealed class OpcUaPublishActor : ReceiveActor /// The local cluster node ID. /// The optional database context factory. /// The optional Phase 7 applier. + /// Akka configured with the OPC UA synchronized dispatcher. public static Props Props( IOpcUaAddressSpaceSink? sink = null, IServiceLevelPublisher? serviceLevel = null, @@ -89,6 +90,7 @@ public sealed class OpcUaPublishActor : ReceiveActor /// The local cluster node ID. /// The optional database context factory. /// The optional Phase 7 applier. + /// Akka suitable for use in a test kit without a pinned dispatcher. public static Props PropsForTests( IOpcUaAddressSpaceSink? sink = null, IServiceLevelPublisher? serviceLevel = null, diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/ScriptedAlarms/EfAlarmActorStateStore.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/ScriptedAlarms/EfAlarmActorStateStore.cs index 40d8d34b..1be68720 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/ScriptedAlarms/EfAlarmActorStateStore.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/ScriptedAlarms/EfAlarmActorStateStore.cs @@ -30,10 +30,7 @@ public sealed class EfAlarmActorStateStore : IAlarmActorStateStore _logger = logger; } - /// Loads the alarm state snapshot from the database. - /// The identifier of the alarm. - /// The cancellation token. - /// The alarm state snapshot, or null if not found. + /// public async Task LoadAsync(string alarmId, CancellationToken ct) { using var db = await _dbFactory.CreateDbContextAsync(ct).ConfigureAwait(false); @@ -50,9 +47,7 @@ public sealed class EfAlarmActorStateStore : IAlarmActorStateStore LastAckUser: row.LastAckUser); } - /// Saves the alarm state snapshot to the database. - /// The alarm state snapshot to save. - /// The cancellation token. + /// public async Task SaveAsync(AlarmActorStateSnapshot snapshot, CancellationToken ct) { using var db = await _dbFactory.CreateDbContextAsync(ct).ConfigureAwait(false); diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/ServiceCollectionExtensions.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/ServiceCollectionExtensions.cs index ae03e824..534a7466 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/ServiceCollectionExtensions.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/ServiceCollectionExtensions.cs @@ -36,6 +36,7 @@ public static class ServiceCollectionExtensions /// Call this BEFORE AddAkka. /// /// The service collection to register with. + /// The same for chaining. public static IServiceCollection AddOtOpcUaRuntime(this IServiceCollection services) { services.TryAddSingleton(NullAlarmHistorianSink.Instance); @@ -63,6 +64,7 @@ public static class ServiceCollectionExtensions /// /// /// The Akka configuration builder. + /// The same for chaining. public static AkkaConfigurationBuilder WithOtOpcUaRuntimeActors(this AkkaConfigurationBuilder builder) { // Production cluster HOCON (akka.conf) carries this dispatcher block, but consumers that diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/VirtualTags/VirtualTagActor.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/VirtualTags/VirtualTagActor.cs index d40ebeb7..3aadab37 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/VirtualTags/VirtualTagActor.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/VirtualTags/VirtualTagActor.cs @@ -43,6 +43,7 @@ public sealed class VirtualTagActor : ReceiveActor /// Optional factory for creating DPS publishers. /// Optional list of dependency tag references; defaults to empty. /// Optional reference to a dependency multiplexer actor. + /// An Akka.NET instance for creating a . public static Props Props( string virtualTagId, string expression, diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Security/Blazor/CookieAuthenticationStateProvider.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Security/Blazor/CookieAuthenticationStateProvider.cs index cdbd7b39..06116d41 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Security/Blazor/CookieAuthenticationStateProvider.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Security/Blazor/CookieAuthenticationStateProvider.cs @@ -65,7 +65,8 @@ public sealed class CookieAuthenticationStateProvider : AuthenticationStateProvi } } - /// + /// Cancels the authentication ping loop and releases resources. + /// A task that represents the asynchronous dispose operation. public async ValueTask DisposeAsync() { _cts.Cancel(); diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Security/Jwt/JwtTokenService.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Security/Jwt/JwtTokenService.cs index 89981d9e..342b6996 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Security/Jwt/JwtTokenService.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Security/Jwt/JwtTokenService.cs @@ -135,6 +135,7 @@ public sealed class JwtTokenService /// required pairing whenever a JwtBearer scheme is wired. /// /// + /// A instance configured with the service's signing key and claim-type aliases. public TokenValidationParameters BuildValidationParameters() => new() { ValidateIssuer = true, diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Security/Ldap/LdapOptions.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Security/Ldap/LdapOptions.cs index 50afcb4d..d756d647 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Security/Ldap/LdapOptions.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Security/Ldap/LdapOptions.cs @@ -89,6 +89,7 @@ public sealed class LdapOptions /// handled by the app wrapper around the library service; is carried /// through so the library's own feature gate stays consistent with the app master switch. /// + /// The projected library LDAP options. public LibLdapOptions ToLibraryOptions() => new() { Enabled = Enabled, diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Security/Ldap/OtOpcUaGroupRoleMapper.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Security/Ldap/OtOpcUaGroupRoleMapper.cs index d8500c5e..886f8886 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Security/Ldap/OtOpcUaGroupRoleMapper.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Security/Ldap/OtOpcUaGroupRoleMapper.cs @@ -20,7 +20,10 @@ public sealed class OtOpcUaGroupRoleMapper( IOptions ldapOptions, ILdapGroupRoleMappingService dbMappings) : IGroupRoleMapper { - /// + /// Maps LDAP groups to OtOpcUa role claims by combining the appsettings baseline with system-wide DB grants. + /// The LDAP groups to map. + /// The cancellation token. + /// A task that resolves to the merged role mapping with a null scope. public async Task> MapAsync(IReadOnlyList groups, CancellationToken ct) { ArgumentNullException.ThrowIfNull(groups); diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Security/Ldap/RoleMapper.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Security/Ldap/RoleMapper.cs index 8e0716e6..c3ed2d32 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Security/Ldap/RoleMapper.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Security/Ldap/RoleMapper.cs @@ -33,6 +33,7 @@ public static class RoleMapper /// /// Roles already resolved from appsettings (or the dev stub). /// LdapGroupRoleMapping rows for the user's groups (from GetByGroupsAsync). + /// The merged, deduplicated list of role names. public static IReadOnlyList Merge( IReadOnlyCollection baselineRoles, IReadOnlyCollection dbRows) diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Security/ServiceCollectionExtensions.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Security/ServiceCollectionExtensions.cs index f62b7e5f..cb655ad1 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Security/ServiceCollectionExtensions.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Security/ServiceCollectionExtensions.cs @@ -31,6 +31,7 @@ public static class ServiceCollectionExtensions /// /// The service collection. /// The application configuration root. + /// The service collection for chaining. public static IServiceCollection AddOtOpcUaAuth(this IServiceCollection services, IConfiguration configuration) { services.AddOptions().Bind(configuration.GetSection(JwtOptions.SectionName)); diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/AlarmsCommandTests.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/AlarmsCommandTests.cs index 0a671f29..52927834 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/AlarmsCommandTests.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/AlarmsCommandTests.cs @@ -8,6 +8,7 @@ namespace ZB.MOM.WW.OtOpcUa.Client.CLI.Tests; public class AlarmsCommandTests { /// Verifies that Execute subscribes to alarms. + /// A task that represents the asynchronous test operation. [Fact] public async Task Execute_SubscribesToAlarms() { @@ -33,6 +34,7 @@ public class AlarmsCommandTests } /// Verifies that Execute with node passes source node ID. + /// A task that represents the asynchronous test operation. [Fact] public async Task Execute_WithNode_PassesSourceNodeId() { @@ -58,6 +60,7 @@ public class AlarmsCommandTests } /// Verifies that Execute with refresh requests condition refresh. + /// A task that represents the asynchronous test operation. [Fact] public async Task Execute_WithRefresh_RequestsConditionRefresh() { @@ -83,6 +86,7 @@ public class AlarmsCommandTests } /// Verifies that refresh failure prints error. + /// A task that represents the asynchronous test operation. [Fact] public async Task Execute_RefreshFailure_PrintsError() { @@ -110,6 +114,7 @@ public class AlarmsCommandTests } /// Verifies that Execute unsubscribes on cancellation. + /// A task that represents the asynchronous test operation. [Fact] public async Task Execute_UnsubscribesOnCancellation() { @@ -132,6 +137,7 @@ public class AlarmsCommandTests } /// Verifies that Execute disconnects in finally block. + /// A task that represents the asynchronous test operation. [Fact] public async Task Execute_DisconnectsInFinally() { diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/BrowseCommandTests.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/BrowseCommandTests.cs index 5cf87e5d..b6e46631 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/BrowseCommandTests.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/BrowseCommandTests.cs @@ -9,6 +9,7 @@ namespace ZB.MOM.WW.OtOpcUa.Client.CLI.Tests; public class BrowseCommandTests { /// Verifies that Execute prints browse results correctly. + /// A task that represents the asynchronous operation. [Fact] public async Task Execute_PrintsBrowseResults() { @@ -37,6 +38,7 @@ public class BrowseCommandTests } /// Verifies that Execute browses from the specified node ID. + /// A task that represents the asynchronous operation. [Fact] public async Task Execute_BrowsesFromSpecifiedNode() { @@ -60,6 +62,7 @@ public class BrowseCommandTests } /// Verifies that Execute browses from null node when not specified. + /// A task that represents the asynchronous operation. [Fact] public async Task Execute_DefaultBrowsesFromNull() { @@ -81,6 +84,7 @@ public class BrowseCommandTests } /// Verifies that Execute browses only a single level when not recursive. + /// A task that represents the asynchronous operation. [Fact] public async Task Execute_NonRecursive_BrowsesSingleLevel() { @@ -106,6 +110,7 @@ public class BrowseCommandTests } /// Verifies that Execute browses child nodes when recursive flag is set. + /// A task that represents the asynchronous operation. [Fact] public async Task Execute_Recursive_BrowsesChildren() { @@ -129,6 +134,7 @@ public class BrowseCommandTests } /// Verifies that Execute disconnects and disposes in the finally block. + /// A task that represents the asynchronous operation. [Fact] public async Task Execute_DisconnectsInFinally() { diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/CommandBaseTests.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/CommandBaseTests.cs index d88f7a76..f0ce2d08 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/CommandBaseTests.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/CommandBaseTests.cs @@ -9,6 +9,7 @@ namespace ZB.MOM.WW.OtOpcUa.Client.CLI.Tests; public class CommandBaseTests { /// Verifies that common options map to connection settings correctly. + /// A task that represents the asynchronous operation. [Fact] public async Task CommonOptions_MapToConnectionSettings_Correctly() { @@ -39,6 +40,7 @@ public class CommandBaseTests } /// Verifies that encrypt option maps to SignAndEncrypt. + /// A task that represents the asynchronous operation. [Fact] public async Task SecurityOption_Encrypt_MapsToSignAndEncrypt() { @@ -57,6 +59,7 @@ public class CommandBaseTests } /// Verifies that none option maps to None. + /// A task that represents the asynchronous operation. [Fact] public async Task SecurityOption_None_MapsToNone() { @@ -75,6 +78,7 @@ public class CommandBaseTests } /// Verifies that no failover URLs results in null FailoverUrls. + /// A task that represents the asynchronous operation. [Fact] public async Task NoFailoverUrls_FailoverUrlsIsNull() { diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/CommandRangeValidationTests.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/CommandRangeValidationTests.cs index 67c9bfa6..2afece46 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/CommandRangeValidationTests.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/CommandRangeValidationTests.cs @@ -13,6 +13,7 @@ namespace ZB.MOM.WW.OtOpcUa.Client.CLI.Tests; public class CommandRangeValidationTests { /// Verifies that BrowseCommand rejects negative depth values with a command exception. + /// A task that represents the asynchronous test. [Fact] public async Task BrowseCommand_NegativeDepth_ThrowsCommandException() { @@ -30,6 +31,7 @@ public class CommandRangeValidationTests } /// Verifies that BrowseCommand rejects zero depth values with a command exception. + /// A task that represents the asynchronous test. [Fact] public async Task BrowseCommand_ZeroDepth_ThrowsCommandException() { @@ -47,6 +49,7 @@ public class CommandRangeValidationTests } /// Verifies that SubscribeCommand rejects zero interval values with a command exception. + /// A task that represents the asynchronous test. [Fact] public async Task SubscribeCommand_ZeroInterval_ThrowsCommandException() { @@ -65,6 +68,7 @@ public class CommandRangeValidationTests } /// Verifies that SubscribeCommand rejects negative interval values with a command exception. + /// A task that represents the asynchronous test. [Fact] public async Task SubscribeCommand_NegativeInterval_ThrowsCommandException() { @@ -82,6 +86,7 @@ public class CommandRangeValidationTests } /// Verifies that SubscribeCommand in recursive mode rejects zero max depth with a command exception. + /// A task that represents the asynchronous test. [Fact] public async Task SubscribeCommand_RecursiveZeroMaxDepth_ThrowsCommandException() { @@ -102,6 +107,7 @@ public class CommandRangeValidationTests } /// Verifies that SubscribeCommand rejects negative duration values with a command exception. + /// A task that represents the asynchronous test. [Fact] public async Task SubscribeCommand_NegativeDuration_ThrowsCommandException() { @@ -119,6 +125,7 @@ public class CommandRangeValidationTests } /// Verifies that AlarmsCommand rejects zero interval values with a command exception. + /// A task that represents the asynchronous test. [Fact] public async Task AlarmsCommand_ZeroInterval_ThrowsCommandException() { @@ -136,6 +143,7 @@ public class CommandRangeValidationTests } /// Verifies that HistoryReadCommand rejects negative max values with a command exception. + /// A task that represents the asynchronous test. [Fact] public async Task HistoryReadCommand_NegativeMax_ThrowsCommandException() { @@ -154,6 +162,7 @@ public class CommandRangeValidationTests } /// Verifies that HistoryReadCommand rejects zero interval values with a command exception. + /// A task that represents the asynchronous test. [Fact] public async Task HistoryReadCommand_ZeroInterval_ThrowsCommandException() { diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/ConnectCommandTests.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/ConnectCommandTests.cs index 7af4170b..37a9f47e 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/ConnectCommandTests.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/ConnectCommandTests.cs @@ -9,6 +9,7 @@ namespace ZB.MOM.WW.OtOpcUa.Client.CLI.Tests; public class ConnectCommandTests { /// Verifies that execute prints connection info. + /// A task that represents the asynchronous test. [Fact] public async Task Execute_PrintsConnectionInfo() { @@ -40,6 +41,7 @@ public class ConnectCommandTests } /// Verifies that execute calls connect and disconnect. + /// A task that represents the asynchronous test. [Fact] public async Task Execute_CallsConnectAndDisconnect() { @@ -59,6 +61,7 @@ public class ConnectCommandTests } /// Verifies that execute disconnects on error. + /// A task that represents the asynchronous test. [Fact] public async Task Execute_DisconnectsOnError() { diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/EventHandlerLifecycleTests.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/EventHandlerLifecycleTests.cs index b0200598..ae56e2db 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/EventHandlerLifecycleTests.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/EventHandlerLifecycleTests.cs @@ -15,6 +15,7 @@ namespace ZB.MOM.WW.OtOpcUa.Client.CLI.Tests; public class EventHandlerLifecycleTests { /// Verifies that SubscribeCommand detaches the DataChanged event handler after exit. + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeCommand_AfterExit_DataChangedEventHasNoSubscribers() { @@ -37,6 +38,7 @@ public class EventHandlerLifecycleTests } /// Verifies that AlarmsCommand detaches the AlarmEvent handler after exit. + /// A task that represents the asynchronous operation. [Fact] public async Task AlarmsCommand_AfterExit_AlarmEventHasNoSubscribers() { diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/Fakes/FakeOpcUaClientService.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/Fakes/FakeOpcUaClientService.cs index c10745ad..b10b38e9 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/Fakes/FakeOpcUaClientService.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/Fakes/FakeOpcUaClientService.cs @@ -126,13 +126,13 @@ public sealed class FakeOpcUaClientService : IOpcUaClientService /// public ConnectionInfo? CurrentConnectionInfo => ConnectCalled ? ConnectionInfoResult : null; - /// + /// Raised when a subscribed node value changes. public event EventHandler? DataChanged; - /// + /// Raised when an alarm event is received from the server. public event EventHandler? AlarmEvent; - /// + /// Raised when the connection state changes. public event EventHandler? ConnectionStateChanged; /// True when at least one handler is attached to . diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/Fakes/FakeOpcUaClientServiceFactory.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/Fakes/FakeOpcUaClientServiceFactory.cs index eca858fe..1063f00d 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/Fakes/FakeOpcUaClientServiceFactory.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/Fakes/FakeOpcUaClientServiceFactory.cs @@ -17,6 +17,7 @@ public sealed class FakeOpcUaClientServiceFactory : IOpcUaClientServiceFactory } /// Creates and returns the fake OPC UA client service. + /// The pre-configured instance. public IOpcUaClientService Create() { return _service; diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/HistoryReadCommandTests.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/HistoryReadCommandTests.cs index 245bfd07..c5a5388a 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/HistoryReadCommandTests.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/HistoryReadCommandTests.cs @@ -10,6 +10,7 @@ namespace ZB.MOM.WW.OtOpcUa.Client.CLI.Tests; public class HistoryReadCommandTests { /// Verifies RawRead execution prints values. + /// A task that represents the asynchronous test. [Fact] public async Task Execute_RawRead_PrintsValues() { @@ -44,6 +45,7 @@ public class HistoryReadCommandTests } /// Verifies RawRead execution calls HistoryReadRaw. + /// A task that represents the asynchronous test. [Fact] public async Task Execute_RawRead_CallsHistoryReadRaw() { @@ -65,6 +67,7 @@ public class HistoryReadCommandTests } /// Verifies AggregateRead execution calls HistoryReadAggregate. + /// A task that represents the asynchronous test. [Fact] public async Task Execute_AggregateRead_CallsHistoryReadAggregate() { @@ -87,6 +90,7 @@ public class HistoryReadCommandTests } /// Verifies AggregateRead execution prints aggregate info. + /// A task that represents the asynchronous test. [Fact] public async Task Execute_AggregateRead_PrintsAggregateInfo() { @@ -109,6 +113,7 @@ public class HistoryReadCommandTests } /// Verifies invalid aggregate throws CommandException. + /// A task that represents the asynchronous test. [Fact] public async Task Execute_InvalidAggregate_ThrowsCommandException() { @@ -129,6 +134,7 @@ public class HistoryReadCommandTests } /// Verifies disconnect is called in finally block. + /// A task that represents the asynchronous test. [Fact] public async Task Execute_DisconnectsInFinally() { diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/InputValidationErrorsTests.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/InputValidationErrorsTests.cs index 7a9a7531..b5d972d6 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/InputValidationErrorsTests.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/InputValidationErrorsTests.cs @@ -14,6 +14,7 @@ namespace ZB.MOM.WW.OtOpcUa.Client.CLI.Tests; public class InputValidationErrorsTests { /// Verifies that HistoryReadCommand with invalid start time throws CommandException. + /// A task that represents the asynchronous operation. [Fact] public async Task HistoryReadCommand_InvalidStartTime_ThrowsCommandException() { @@ -32,6 +33,7 @@ public class InputValidationErrorsTests } /// Verifies that HistoryReadCommand with invalid end time throws CommandException. + /// A task that represents the asynchronous operation. [Fact] public async Task HistoryReadCommand_InvalidEndTime_ThrowsCommandException() { @@ -50,6 +52,7 @@ public class InputValidationErrorsTests } /// Verifies that HistoryReadCommand with invalid aggregate throws CommandException. + /// A task that represents the asynchronous operation. [Fact] public async Task HistoryReadCommand_InvalidAggregate_ThrowsCommandException() { @@ -68,6 +71,7 @@ public class InputValidationErrorsTests } /// Verifies that ReadCommand with invalid node ID throws CommandException. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadCommand_InvalidNodeId_ThrowsCommandException() { @@ -85,6 +89,7 @@ public class InputValidationErrorsTests } /// Verifies that SubscribeCommand with invalid node ID throws CommandException. + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeCommand_InvalidNodeId_ThrowsCommandException() { diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/LoggerLifecycleTests.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/LoggerLifecycleTests.cs index fe619a68..90e7f306 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/LoggerLifecycleTests.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/LoggerLifecycleTests.cs @@ -15,6 +15,7 @@ namespace ZB.MOM.WW.OtOpcUa.Client.CLI.Tests; public class LoggerLifecycleTests { /// Verifies that ConfigureLogging disposes previous logger before reassigning. + /// A task that represents the asynchronous test operation. [Fact] public async Task ConfigureLogging_DisposesPreviousLogger_BeforeReassigning() { diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/ReadCommandTests.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/ReadCommandTests.cs index 1b894c0a..c2d3831c 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/ReadCommandTests.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/ReadCommandTests.cs @@ -9,6 +9,7 @@ namespace ZB.MOM.WW.OtOpcUa.Client.CLI.Tests; public class ReadCommandTests { /// Verifies that execute prints the read value. + /// A task that represents the asynchronous operation. [Fact] public async Task Execute_PrintsReadValue() { @@ -41,6 +42,7 @@ public class ReadCommandTests } /// Verifies that execute calls read value with correct node ID. + /// A task that represents the asynchronous operation. [Fact] public async Task Execute_CallsReadValueWithCorrectNodeId() { @@ -60,6 +62,7 @@ public class ReadCommandTests } /// Verifies that execute disconnects in finally. + /// A task that represents the asynchronous operation. [Fact] public async Task Execute_DisconnectsInFinally() { @@ -79,6 +82,7 @@ public class ReadCommandTests } /// Verifies that execute disconnects even on read error. + /// A task that represents the asynchronous operation. [Fact] public async Task Execute_DisconnectsEvenOnReadError() { diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/RedundancyCommandTests.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/RedundancyCommandTests.cs index 755be437..4e5c8afc 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/RedundancyCommandTests.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/RedundancyCommandTests.cs @@ -9,6 +9,7 @@ namespace ZB.MOM.WW.OtOpcUa.Client.CLI.Tests; public class RedundancyCommandTests { /// Verifies that Execute prints redundancy information correctly. + /// A task that represents the asynchronous operation. [Fact] public async Task Execute_PrintsRedundancyInfo() { @@ -36,6 +37,7 @@ public class RedundancyCommandTests } /// Verifies that Execute omits the Server URIs section when none are present. + /// A task that represents the asynchronous operation. [Fact] public async Task Execute_NoServerUris_OmitsUriSection() { @@ -61,6 +63,7 @@ public class RedundancyCommandTests } /// Verifies that Execute calls GetRedundancyInfo on the service. + /// A task that represents the asynchronous operation. [Fact] public async Task Execute_CallsGetRedundancyInfo() { @@ -78,6 +81,7 @@ public class RedundancyCommandTests } /// Verifies that Execute disconnects and disposes in the finally block. + /// A task that represents the asynchronous operation. [Fact] public async Task Execute_DisconnectsInFinally() { diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/SubscribeCommandSummaryTests.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/SubscribeCommandSummaryTests.cs index 272d73c1..a7259626 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/SubscribeCommandSummaryTests.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/SubscribeCommandSummaryTests.cs @@ -14,6 +14,7 @@ namespace ZB.MOM.WW.OtOpcUa.Client.CLI.Tests; public class SubscribeCommandSummaryTests { /// Verifies that nodes with no updates are counted separately from suspects. + /// A task that represents the asynchronous test operation. [Fact] public async Task Summary_NodeWithNoUpdate_IsCountedAsNeverNotAsNeverWentBad() { @@ -41,6 +42,7 @@ public class SubscribeCommandSummaryTests } /// Verifies that nodes with only good values are counted as never went bad. + /// A task that represents the asynchronous test operation. [Fact] public async Task Summary_NodeReceivedOnlyGoodValues_IsCountedAsNeverWentBad() { @@ -73,6 +75,7 @@ public class SubscribeCommandSummaryTests } /// Verifies that nodes with bad values are counted as ever went bad. + /// A task that represents the asynchronous test operation. [Fact] public async Task Summary_NodeReceivedBadValue_IsCountedAsEverWentBad() { @@ -102,6 +105,7 @@ public class SubscribeCommandSummaryTests } /// Verifies that subscription auto-exits when duration expires. + /// A task that represents the asynchronous test operation. [Fact] public async Task Duration_ZeroOrPositive_AutoExits() { @@ -129,6 +133,7 @@ public class SubscribeCommandSummaryTests } /// Verifies that --quiet suppresses updates but prints summary. + /// A task that represents the asynchronous test operation. [Fact] public async Task Quiet_SuppressesPerUpdateOutputButPrintsSummary() { @@ -160,6 +165,7 @@ public class SubscribeCommandSummaryTests } /// Verifies that summary is written to disk when summary file is specified. + /// A task that represents the asynchronous test operation. [Fact] public async Task SummaryFile_WritesSummaryToDisk() { @@ -191,6 +197,7 @@ public class SubscribeCommandSummaryTests } /// Verifies that recursive flag browses subtree and subscribes every variable. + /// A task that represents the asynchronous test operation. [Fact] public async Task Recursive_BrowsesSubtreeAndSubscribesEveryVariable() { @@ -230,6 +237,7 @@ public class SubscribeCommandSummaryTests } /// Verifies that subscription failures are handled gracefully. + /// A task that represents the asynchronous test operation. [Fact] public async Task SubscribeFailure_PrintsFailedMessage_DoesNotCrash() { diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/SubscribeCommandTests.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/SubscribeCommandTests.cs index a9b8c9d6..68c196af 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/SubscribeCommandTests.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/SubscribeCommandTests.cs @@ -8,6 +8,7 @@ namespace ZB.MOM.WW.OtOpcUa.Client.CLI.Tests; public class SubscribeCommandTests { /// Verifies that ExecuteAsync subscribes with the correct parameters. + /// A task that represents the asynchronous operation. [Fact] public async Task Execute_SubscribesWithCorrectParameters() { @@ -37,6 +38,7 @@ public class SubscribeCommandTests } /// Verifies that ExecuteAsync unsubscribes when cancellation is requested. + /// A task that represents the asynchronous operation. [Fact] public async Task Execute_UnsubscribesOnCancellation() { @@ -60,6 +62,7 @@ public class SubscribeCommandTests } /// Verifies that ExecuteAsync disconnects and disposes in a finally block. + /// A task that represents the asynchronous operation. [Fact] public async Task Execute_DisconnectsInFinally() { @@ -84,6 +87,7 @@ public class SubscribeCommandTests } /// Verifies that ExecuteAsync prints the correct subscription message. + /// A task that represents the asynchronous operation. [Fact] public async Task Execute_PrintsSubscriptionMessage() { diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/TestConsoleHelper.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/TestConsoleHelper.cs index f0378a8c..037b89f9 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/TestConsoleHelper.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/TestConsoleHelper.cs @@ -10,6 +10,7 @@ public static class TestConsoleHelper /// /// Creates a new for testing. /// + /// A new instance. public static FakeInMemoryConsole CreateConsole() { return new FakeInMemoryConsole(); diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/WriteCommandTests.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/WriteCommandTests.cs index 9e1236d8..15a485a5 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/WriteCommandTests.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests/WriteCommandTests.cs @@ -9,6 +9,7 @@ namespace ZB.MOM.WW.OtOpcUa.Client.CLI.Tests; public class WriteCommandTests { /// Verifies that write command executes successfully. + /// A task that represents the asynchronous test operation. [Fact] public async Task Execute_WritesSuccessfully() { @@ -33,6 +34,7 @@ public class WriteCommandTests } /// Verifies that write command reports failure. + /// A task that represents the asynchronous test operation. [Fact] public async Task Execute_ReportsFailure() { @@ -57,6 +59,7 @@ public class WriteCommandTests } /// Verifies that write command reads current value before writing. + /// A task that represents the asynchronous test operation. [Fact] public async Task Execute_ReadsCurrentValueThenWrites() { @@ -83,6 +86,7 @@ public class WriteCommandTests } /// Verifies that write command disconnects in finally block. + /// A task that represents the asynchronous test operation. [Fact] public async Task Execute_DisconnectsInFinally() { diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.Shared.Tests/Fakes/FakeEndpointDiscovery.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.Shared.Tests/Fakes/FakeEndpointDiscovery.cs index 59cb537d..a5ee3053 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.Shared.Tests/Fakes/FakeEndpointDiscovery.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.Shared.Tests/Fakes/FakeEndpointDiscovery.cs @@ -12,11 +12,7 @@ internal sealed class FakeEndpointDiscovery : IEndpointDiscovery /// Gets the last endpoint URL passed to SelectEndpoint. public string? LastEndpointUrl { get; private set; } - /// Selects an endpoint for the given configuration and URL, optionally throwing an exception. - /// The application configuration. - /// The endpoint URL to select. - /// The requested security mode. - /// The selected endpoint description. + /// public EndpointDescription SelectEndpoint(ApplicationConfiguration config, string endpointUrl, MessageSecurityMode requestedMode) { diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.Shared.Tests/OpcUaClientServiceTests.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.Shared.Tests/OpcUaClientServiceTests.cs index 0213728b..7cc44f5b 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.Shared.Tests/OpcUaClientServiceTests.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.Shared.Tests/OpcUaClientServiceTests.cs @@ -44,6 +44,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that a valid connection request returns populated connection metadata and marks the client as connected. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectAsync_ValidSettings_ReturnsConnectionInfo() { @@ -58,6 +59,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that invalid connection settings fail validation before any OPC UA session is created. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectAsync_InvalidSettings_ThrowsBeforeCreatingSession() { @@ -71,6 +73,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that server and security details from the session are copied into the exposed connection info. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectAsync_PopulatesConnectionInfo() { @@ -96,6 +99,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that connection-state transitions are raised for the connecting and connected phases. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectAsync_RaisesConnectionStateChangedEvents() { @@ -114,6 +118,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that a failed session creation leaves the client in the disconnected state. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectAsync_SessionFactoryFails_TransitionsToDisconnected() { @@ -130,6 +135,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that username and password settings are passed through to the session-creation pipeline. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectAsync_WithUsername_PassesThroughToFactory() { @@ -148,6 +154,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that disconnect closes the active session and clears exposed connection state. /// + /// A task that represents the asynchronous operation. [Fact] public async Task DisconnectAsync_WhenConnected_ClosesSession() { @@ -164,6 +171,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that disconnect is safe to call when no server session is active. /// + /// A task that represents the asynchronous operation. [Fact] public async Task DisconnectAsync_WhenNotConnected_IsIdempotent() { @@ -174,6 +182,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that repeated disconnect calls do not throw after cleanup has already run. /// + /// A task that represents the asynchronous operation. [Fact] public async Task DisconnectAsync_CalledTwice_IsIdempotent() { @@ -187,6 +196,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that a connected client can read the current value of a node through the session adapter. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ReadValueAsync_WhenConnected_ReturnsValue() { @@ -206,6 +216,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that reads are rejected when the client is not connected to a server. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ReadValueAsync_WhenDisconnected_Throws() { @@ -216,6 +227,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that session-level read failures are surfaced to callers instead of being swallowed. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ReadValueAsync_SessionThrows_PropagatesException() { @@ -232,6 +244,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that writes succeed through the session adapter when the client is connected. /// + /// A task that represents the asynchronous operation. [Fact] public async Task WriteValueAsync_WhenConnected_WritesValue() { @@ -252,6 +265,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that string inputs are coerced to the node's current data type before writing. /// + /// A task that represents the asynchronous operation. [Fact] public async Task WriteValueAsync_StringValue_CoercesToTargetType() { @@ -271,6 +285,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that non-string values are written directly without an extra type-inference read. /// + /// A task that represents the asynchronous operation. [Fact] public async Task WriteValueAsync_NonStringValue_WritesDirectly() { @@ -287,6 +302,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that writes are rejected when the client is disconnected. /// + /// A task that represents the asynchronous operation. [Fact] public async Task WriteValueAsync_WhenDisconnected_Throws() { @@ -298,6 +314,7 @@ public class OpcUaClientServiceTests : IDisposable /// Verifies that writing a string to a node whose current read returns a bad status /// surfaces a clear error instead of writing a mistyped string value (Client.Shared-008). /// + /// A task that represents the asynchronous operation. [Fact] public async Task WriteValueAsync_StringValueWithBadReadStatus_ThrowsInvalidOperationException() { @@ -318,6 +335,7 @@ public class OpcUaClientServiceTests : IDisposable /// Verifies that writing a string to a node whose read returns bad status and null Value /// surfaces a clear error for both the bad-status case (Client.Shared-008). /// + /// A task that represents the asynchronous operation. [Fact] public async Task WriteValueAsync_StringValueWithBadStatus_MessageMentionsNode() { @@ -339,6 +357,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that browse results are mapped into the client browse model used by CLI and UI consumers. /// + /// A task that represents the asynchronous operation. [Fact] public async Task BrowseAsync_WhenConnected_ReturnsMappedResults() { @@ -368,6 +387,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that a null browse root defaults to the OPC UA Objects folder. /// + /// A task that represents the asynchronous operation. [Fact] public async Task BrowseAsync_NullParent_UsesObjectsFolder() { @@ -386,6 +406,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that object nodes trigger child-detection checks so the client can mark expandable branches. /// + /// A task that represents the asynchronous operation. [Fact] public async Task BrowseAsync_ObjectNode_ChecksHasChildren() { @@ -414,6 +435,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that browse continuation points are followed so multi-page address-space branches are fully returned. /// + /// A task that represents the asynchronous operation. [Fact] public async Task BrowseAsync_WithContinuationPoint_FollowsIt() { @@ -452,6 +474,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that browse requests are rejected when the client is disconnected. /// + /// A task that represents the asynchronous operation. [Fact] public async Task BrowseAsync_WhenDisconnected_Throws() { @@ -463,6 +486,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that subscribing to a node creates a monitored item on a data-change subscription. /// + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeAsync_CreatesSubscription() { @@ -479,6 +503,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that duplicate subscribe requests for the same node do not create duplicate monitored items. /// + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeAsync_DuplicateNode_IsIdempotent() { @@ -495,6 +520,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that data-change notifications from the subscription are raised through the shared client event. /// + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeAsync_RaisesDataChangedEvent() { @@ -520,6 +546,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that unsubscribing removes the corresponding monitored item from the active subscription. /// + /// A task that represents the asynchronous operation. [Fact] public async Task UnsubscribeAsync_RemovesMonitoredItem() { @@ -537,6 +564,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that unsubscribing an unknown node is treated as a safe no-op. /// + /// A task that represents the asynchronous operation. [Fact] public async Task UnsubscribeAsync_WhenNotSubscribed_DoesNotThrow() { @@ -551,6 +579,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that data subscriptions cannot be created while the client is disconnected. /// + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeAsync_WhenDisconnected_Throws() { @@ -563,6 +592,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that alarm subscription requests create an event monitored item on the session. /// + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeAlarmsAsync_CreatesEventSubscription() { @@ -579,6 +609,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that duplicate alarm-subscription requests do not create duplicate event subscriptions. /// + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeAlarmsAsync_Duplicate_IsIdempotent() { @@ -595,6 +626,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that OPC UA event notifications are mapped into the shared client alarm event model. /// + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeAlarmsAsync_RaisesAlarmEvent() { @@ -643,6 +675,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that removing alarm monitoring deletes the underlying event subscription. /// + /// A task that represents the asynchronous operation. [Fact] public async Task UnsubscribeAlarmsAsync_DeletesSubscription() { @@ -660,6 +693,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that removing alarms is safe even when no alarm subscription exists. /// + /// A task that represents the asynchronous operation. [Fact] public async Task UnsubscribeAlarmsAsync_WhenNoSubscription_DoesNotThrow() { @@ -673,6 +707,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that condition refresh requests are forwarded to the active alarm subscription. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RequestConditionRefreshAsync_CallsAdapter() { @@ -690,6 +725,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that condition refresh fails fast when no alarm subscription is active. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RequestConditionRefreshAsync_NoAlarmSubscription_Throws() { @@ -704,6 +740,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that alarm subscriptions cannot be created while disconnected. /// + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeAlarmsAsync_WhenDisconnected_Throws() { @@ -716,6 +753,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that raw history reads return the session-provided values. /// + /// A task that represents the asynchronous operation. [Fact] public async Task HistoryReadRawAsync_ReturnsValues() { @@ -738,6 +776,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that raw history reads are rejected while disconnected. /// + /// A task that represents the asynchronous operation. [Fact] public async Task HistoryReadRawAsync_WhenDisconnected_Throws() { @@ -748,6 +787,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that raw-history failures from the session are propagated to callers. /// + /// A task that represents the asynchronous operation. [Fact] public async Task HistoryReadRawAsync_SessionThrows_PropagatesException() { @@ -762,6 +802,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that aggregate history reads return the processed values from the session adapter. /// + /// A task that represents the asynchronous operation. [Fact] public async Task HistoryReadAggregateAsync_ReturnsValues() { @@ -784,6 +825,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that aggregate history reads are rejected while disconnected. /// + /// A task that represents the asynchronous operation. [Fact] public async Task HistoryReadAggregateAsync_WhenDisconnected_Throws() { @@ -796,6 +838,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that aggregate-history failures from the session are propagated to callers. /// + /// A task that represents the asynchronous operation. [Fact] public async Task HistoryReadAggregateAsync_SessionThrows_PropagatesException() { @@ -814,6 +857,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that redundancy mode, service level, and server URIs are read from the standard OPC UA redundancy nodes. /// + /// A task that represents the asynchronous operation. [Fact] public async Task GetRedundancyInfoAsync_ReturnsInfo() { @@ -846,6 +890,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that missing optional redundancy arrays do not prevent a redundancy snapshot from being returned. /// + /// A task that represents the asynchronous operation. [Fact] public async Task GetRedundancyInfoAsync_MissingOptionalArrays_ReturnsGracefully() { @@ -876,6 +921,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that redundancy inspection is rejected while disconnected. /// + /// A task that represents the asynchronous operation. [Fact] public async Task GetRedundancyInfoAsync_WhenDisconnected_Throws() { @@ -887,6 +933,7 @@ public class OpcUaClientServiceTests : IDisposable /// Verifies that RedundancySupport boxed as a different numeric type (e.g. short) is handled /// without InvalidCastException — defensive Convert.ToInt32 coercion (Client.Shared-002). /// + /// A task that represents the asynchronous operation. [Fact] public async Task GetRedundancyInfoAsync_RedundancySupportBoxedAsShort_DoesNotThrow() { @@ -915,6 +962,7 @@ public class OpcUaClientServiceTests : IDisposable /// Verifies that a bad-status response for RedundancySupport/ServiceLevel falls back to defaults /// rather than throwing (Client.Shared-002). /// + /// A task that represents the asynchronous operation. [Fact] public async Task GetRedundancyInfoAsync_BadStatusOnRequiredReads_ReturnsDefaults() { @@ -944,6 +992,7 @@ public class OpcUaClientServiceTests : IDisposable /// Verifies that an alarm event with fewer than 6 fields (but at least 1) is still raised /// with available fields — the old hard <6 early return silently dropped it (Client.Shared-001). /// + /// A task that represents the asynchronous operation. [Fact] public async Task OnAlarmEvent_TruncatedFields_StillRaisesEvent() { @@ -978,6 +1027,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that a null or empty event field list is silently ignored (defensive guard). /// + /// A task that represents the asynchronous operation. [Fact] public async Task OnAlarmEvent_EmptyFields_DoesNotRaiseEvent() { @@ -1003,6 +1053,7 @@ public class OpcUaClientServiceTests : IDisposable /// Verifies that a successful acknowledge call returns /// and reaches the session adapter's CallMethodAsync (Client.Shared-009). /// + /// A task that represents the asynchronous operation. [Fact] public async Task AcknowledgeAlarmAsync_OnSuccess_ReturnsGood() { @@ -1022,6 +1073,7 @@ public class OpcUaClientServiceTests : IDisposable /// , so callers using /// if (StatusCode.IsBad(result)) actually see the failure. /// + /// A task that represents the asynchronous operation. [Fact] public async Task AcknowledgeAlarmAsync_OnServiceResultException_ReturnsBadStatusCode() { @@ -1044,6 +1096,7 @@ public class OpcUaClientServiceTests : IDisposable /// source node, but left alone when the caller already passes the condition node — /// matches the documented contract. /// + /// A task that represents the asynchronous operation. [Fact] public async Task AcknowledgeAlarmAsync_LeavesConditionSuffixAlone() { @@ -1066,6 +1119,7 @@ public class OpcUaClientServiceTests : IDisposable /// InAlarm/Acked from the condition node's Galaxy attributes. Verify /// the alarm event is delivered with the values from the supplemental reads. /// + /// A task that represents the asynchronous operation. [Fact] public async Task OnAlarmEvent_MissingAckedActiveButHasConditionNode_FallbackReadsAndRaisesEvent() { @@ -1139,6 +1193,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that a keep-alive failure moves the client to a configured failover endpoint. /// + /// A task that represents the asynchronous operation. [Fact] public async Task KeepAliveFailure_TriggersFailover() { @@ -1170,6 +1225,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that connection metadata is refreshed to reflect the newly active failover endpoint. /// + /// A task that represents the asynchronous operation. [Fact] public async Task KeepAliveFailure_UpdatesConnectionInfo() { @@ -1196,6 +1252,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that the client falls back to disconnected when every failover endpoint is unreachable. /// + /// A task that represents the asynchronous operation. [Fact] public async Task KeepAliveFailure_AllEndpointsFail_TransitionsToDisconnected() { @@ -1217,6 +1274,7 @@ public class OpcUaClientServiceTests : IDisposable /// failover loop is still in-flight must be ignored, so only one failover runs and only /// one replacement session is created. /// + /// A task that represents the asynchronous operation. [Fact] public async Task KeepAliveFailure_ReentrantWhileFailoverInFlight_RunsFailoverOnce() { @@ -1255,6 +1313,7 @@ public class OpcUaClientServiceTests : IDisposable /// Regression for Client.Shared-005: concurrent subscribe/unsubscribe calls mutating the /// active-subscription bookkeeping must not corrupt the dictionary or throw. /// + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeAndUnsubscribe_ConcurrentCalls_DoNotCorruptState() { @@ -1283,6 +1342,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that dispose releases the underlying session and clears exposed connection state. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Dispose_CleansUpResources() { @@ -1299,6 +1359,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that dispose is safe to call even when no connection was established. /// + /// A task that represents the asynchronous operation. [Fact] public void Dispose_WhenNotConnected_DoesNotThrow() { @@ -1308,6 +1369,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that public operations reject use after the shared client has been disposed. /// + /// A task that represents the asynchronous operation. [Fact] public async Task OperationsAfterDispose_Throw() { @@ -1324,6 +1386,7 @@ public class OpcUaClientServiceTests : IDisposable /// /// Verifies that the factory creates a usable shared OPC UA client service instance. /// + /// A task that represents the asynchronous operation. [Fact] public void OpcUaClientServiceFactory_CreatesService() { diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/AlarmsViewModelTests.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/AlarmsViewModelTests.cs index d24a1043..02109fa2 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/AlarmsViewModelTests.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/AlarmsViewModelTests.cs @@ -47,6 +47,7 @@ public class AlarmsViewModelTests } /// Verifies that SubscribeCommand sets IsSubscribed flag. + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeCommand_SetsIsSubscribed() { @@ -68,6 +69,7 @@ public class AlarmsViewModelTests } /// Verifies that UnsubscribeCommand clears IsSubscribed flag. + /// A task that represents the asynchronous operation. [Fact] public async Task UnsubscribeCommand_ClearsIsSubscribed() { @@ -81,6 +83,7 @@ public class AlarmsViewModelTests } /// Verifies that RefreshCommand calls the service. + /// A task that represents the asynchronous operation. [Fact] public async Task RefreshCommand_CallsService() { @@ -156,6 +159,7 @@ public class AlarmsViewModelTests /// Regression test for Client.UI-006 — when SubscribeAlarmsAsync throws, the failure must be /// surfaced to the operator via the view model's StatusMessage rather than silently swallowed. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Subscribe_OnFailure_SurfacesStatusMessage() { diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/BrowseTreeViewModelTests.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/BrowseTreeViewModelTests.cs index 92eb1b6c..a9c33958 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/BrowseTreeViewModelTests.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/BrowseTreeViewModelTests.cs @@ -30,6 +30,7 @@ public class BrowseTreeViewModelTests } /// Verifies that LoadRootsAsync populates root nodes. + /// A task that represents the asynchronous operation. [Fact] public async Task LoadRootsAsync_PopulatesRootNodes() { @@ -41,6 +42,7 @@ public class BrowseTreeViewModelTests } /// Verifies that LoadRootsAsync browses with null parent. + /// A task that represents the asynchronous operation. [Fact] public async Task LoadRootsAsync_BrowsesWithNullParent() { @@ -61,6 +63,7 @@ public class BrowseTreeViewModelTests } /// Verifies that nodes with children have a placeholder. + /// A task that represents the asynchronous operation. [Fact] public async Task LoadRootsAsync_NodeWithChildren_HasPlaceholder() { @@ -73,6 +76,7 @@ public class BrowseTreeViewModelTests } /// Verifies that nodes without children have no placeholder. + /// A task that represents the asynchronous operation. [Fact] public async Task LoadRootsAsync_NodeWithoutChildren_HasNoPlaceholder() { @@ -84,6 +88,7 @@ public class BrowseTreeViewModelTests } /// Verifies that first tree node expand triggers child browse. + /// A task that represents the asynchronous operation. [Fact] public async Task TreeNode_FirstExpand_TriggersChildBrowse() { @@ -114,6 +119,7 @@ public class BrowseTreeViewModelTests } /// Verifies that second tree node expand does not browse again. + /// A task that represents the asynchronous operation. [Fact] public async Task TreeNode_SecondExpand_DoesNotBrowseAgain() { @@ -143,6 +149,7 @@ public class BrowseTreeViewModelTests } /// Verifies that IsLoading transitions during browse. + /// A task that represents the asynchronous operation. [Fact] public async Task TreeNode_IsLoading_TransitionsDuringBrowse() { diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/Fakes/FakeOpcUaClientService.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/Fakes/FakeOpcUaClientService.cs index 32b7a1a9..1bca9353 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/Fakes/FakeOpcUaClientService.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/Fakes/FakeOpcUaClientService.cs @@ -136,13 +136,13 @@ public sealed class FakeOpcUaClientService : IOpcUaClientService /// public ConnectionInfo? CurrentConnectionInfo { get; set; } - /// + /// Raised when a subscribed node value changes. public event EventHandler? DataChanged; - /// + /// Raised when an alarm condition event is received from the server. public event EventHandler? AlarmEvent; - /// + /// Raised when the OPC UA session connection state changes. public event EventHandler? ConnectionStateChanged; /// diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/Fakes/FakeSettingsService.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/Fakes/FakeSettingsService.cs index 9790e911..c0274d23 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/Fakes/FakeSettingsService.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/Fakes/FakeSettingsService.cs @@ -13,15 +13,14 @@ public sealed class FakeSettingsService : ISettingsService /// Gets the last settings that were saved. public UserSettings? LastSaved { get; private set; } - /// Loads and returns the current settings. + /// public UserSettings Load() { LoadCallCount++; return Settings; } - /// Saves the specified settings. - /// The settings to save. + /// public void Save(UserSettings settings) { SaveCallCount++; diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/HistoryViewModelTests.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/HistoryViewModelTests.cs index e0b65e5e..f10679d3 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/HistoryViewModelTests.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/HistoryViewModelTests.cs @@ -60,6 +60,7 @@ public class HistoryViewModelTests } /// Verifies that a raw history read populates results correctly. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadHistoryCommand_Raw_PopulatesResults() { @@ -77,6 +78,7 @@ public class HistoryViewModelTests } /// Verifies that an aggregate history read populates results correctly. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadHistoryCommand_Aggregate_PopulatesResults() { @@ -94,6 +96,7 @@ public class HistoryViewModelTests } /// Verifies that the read history command clears previous results before loading new ones. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadHistoryCommand_ClearsResultsBefore() { @@ -107,6 +110,7 @@ public class HistoryViewModelTests } /// Verifies that the loading state is false after the read history command completes. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadHistoryCommand_IsLoading_FalseAfterComplete() { @@ -158,6 +162,7 @@ public class HistoryViewModelTests } /// Verifies that read history command errors are displayed in the results. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadHistoryCommand_Error_ShowsErrorInResults() { diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/MainWindowViewModelTests.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/MainWindowViewModelTests.cs index dfb02a37..24a5c1a0 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/MainWindowViewModelTests.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/MainWindowViewModelTests.cs @@ -76,6 +76,7 @@ public class MainWindowViewModelTests /// /// Verifies that a successful connect command updates the shell into the connected state. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectCommand_TransitionsToConnected() { @@ -89,6 +90,7 @@ public class MainWindowViewModelTests /// /// Verifies that the initial browse tree is loaded after a successful connect. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectCommand_LoadsRootNodes() { @@ -101,6 +103,7 @@ public class MainWindowViewModelTests /// /// Verifies that redundancy details are fetched and exposed after connecting. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectCommand_FetchesRedundancyInfo() { @@ -114,6 +117,7 @@ public class MainWindowViewModelTests /// /// Verifies that the session label shows the connected server and session identity. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectCommand_SetsSessionLabel() { @@ -126,6 +130,7 @@ public class MainWindowViewModelTests /// /// Verifies that disconnect returns the shell to the disconnected state. /// + /// A task that represents the asynchronous operation. [Fact] public async Task DisconnectCommand_TransitionsToDisconnected() { @@ -140,6 +145,7 @@ public class MainWindowViewModelTests /// /// Verifies that disconnect clears session-specific UI state such as browse data and redundancy details. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Disconnect_ClearsStateAndChildren() { @@ -155,6 +161,7 @@ public class MainWindowViewModelTests /// /// Verifies that connection-state events from the client update the shell status text and state. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectionStateChangedEvent_UpdatesState() { @@ -171,6 +178,7 @@ public class MainWindowViewModelTests /// /// Verifies that selecting a tree node updates the dependent read/write and history panels. /// + /// A task that represents the asynchronous operation. [Fact] public async Task SelectedTreeNode_PropagatesToChildViewModels() { @@ -186,6 +194,7 @@ public class MainWindowViewModelTests /// /// Verifies that a successful connect propagates connected state into the child tabs. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectCommand_PropagatesIsConnectedToChildViewModels() { @@ -200,6 +209,7 @@ public class MainWindowViewModelTests /// /// Verifies that disconnect propagates disconnected state into the child tabs. /// + /// A task that represents the asynchronous operation. [Fact] public async Task DisconnectCommand_PropagatesIsConnectedFalseToChildViewModels() { @@ -215,6 +225,7 @@ public class MainWindowViewModelTests /// /// Verifies that failed connection attempts restore the disconnected shell state and surface the error text. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectFailure_RevertsToDisconnected() { @@ -229,6 +240,7 @@ public class MainWindowViewModelTests /// /// Verifies that connection-state transitions raise property-changed notifications for UI binding updates. /// + /// A task that represents the asynchronous operation. [Fact] public async Task PropertyChanged_FiredForConnectionState() { @@ -260,6 +272,7 @@ public class MainWindowViewModelTests /// /// Verifies that failover endpoint text is parsed into connection settings on connect. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectCommand_MapsFailoverUrlsToSettings() { @@ -276,6 +289,7 @@ public class MainWindowViewModelTests /// /// Verifies that empty failover text is normalized to no configured failover endpoints. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectCommand_MapsEmptyFailoverUrlsToNull() { @@ -289,6 +303,7 @@ public class MainWindowViewModelTests /// /// Verifies that the configured session timeout is passed into the connection settings. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectCommand_MapsSessionTimeoutToSettings() { @@ -302,6 +317,7 @@ public class MainWindowViewModelTests /// /// Verifies that the auto-accept certificate toggle is passed into the connection settings. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectCommand_MapsAutoAcceptCertificatesToSettings() { @@ -315,6 +331,7 @@ public class MainWindowViewModelTests /// /// Verifies that a custom certificate store path is passed into the connection settings. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectCommand_MapsCertificateStorePathToSettings() { @@ -328,6 +345,7 @@ public class MainWindowViewModelTests /// /// Verifies that subscribing selected nodes adds subscriptions and switches the shell to the subscriptions tab. /// + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeSelectedNodesCommand_SubscribesAndSwitchesToTab() { @@ -348,6 +366,7 @@ public class MainWindowViewModelTests /// /// Verifies that subscribing selected nodes is a no-op when nothing is selected. /// + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeSelectedNodesCommand_DoesNothing_WhenNoSelection() { @@ -361,6 +380,7 @@ public class MainWindowViewModelTests /// /// Verifies that the history command targets the selected node and switches the shell to the history tab. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ViewHistoryForSelectedNodeCommand_SetsNodeAndSwitchesToTab() { @@ -378,6 +398,7 @@ public class MainWindowViewModelTests /// /// Verifies that history actions are enabled when a variable node is selected. /// + /// A task that represents the asynchronous operation. [Fact] public async Task UpdateHistoryEnabledForSelection_TrueForVariableNode() { @@ -395,6 +416,7 @@ public class MainWindowViewModelTests /// /// Verifies that history actions stay disabled when an object node rather than a variable is selected. /// + /// A task that represents the asynchronous operation. [Fact] public async Task UpdateHistoryEnabledForSelection_FalseForObjectNode() { @@ -470,6 +492,7 @@ public class MainWindowViewModelTests /// /// Verifies that successful connections persist the current connection settings. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectCommand_SavesSettingsOnSuccess() { @@ -487,6 +510,7 @@ public class MainWindowViewModelTests /// /// Verifies that failed connection attempts do not overwrite saved settings. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectCommand_DoesNotSaveOnFailure() { @@ -500,6 +524,7 @@ public class MainWindowViewModelTests /// /// Verifies that active subscriptions are persisted when the shell disconnects. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectCommand_SavesSubscribedNodes() { @@ -522,6 +547,7 @@ public class MainWindowViewModelTests /// view model must leave RedundancyInfo null without crashing or hiding the diagnostic. /// The Status text is expected to remain "Connected" (redundancy is optional). /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectCommand_RedundancyFailure_DoesNotBreakConnection() { @@ -538,6 +564,7 @@ public class MainWindowViewModelTests /// /// Verifies that saved subscriptions are restored after reconnecting the shell. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ConnectCommand_RestoresSavedSubscriptions() { diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/ReadWriteViewModelTests.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/ReadWriteViewModelTests.cs index 24caa13c..1a276d2d 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/ReadWriteViewModelTests.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/ReadWriteViewModelTests.cs @@ -51,6 +51,7 @@ public class ReadWriteViewModelTests } /// Verifies that the read command updates value and status. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadCommand_UpdatesValueAndStatus() { @@ -91,6 +92,7 @@ public class ReadWriteViewModelTests } /// Verifies that the write command updates write status. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteCommand_UpdatesWriteStatus() { @@ -118,6 +120,7 @@ public class ReadWriteViewModelTests } /// Verifies that read command error sets error status. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadCommand_Error_SetsErrorStatus() { diff --git a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/SubscriptionsViewModelTests.cs b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/SubscriptionsViewModelTests.cs index 8c4fd3f5..4a35d498 100644 --- a/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/SubscriptionsViewModelTests.cs +++ b/tests/Client/ZB.MOM.WW.OtOpcUa.Client.UI.Tests/SubscriptionsViewModelTests.cs @@ -41,6 +41,7 @@ public class SubscriptionsViewModelTests } /// Verifies that AddSubscriptionCommand adds a new subscription to the active list. + /// A task that represents the asynchronous test operation. [Fact] public async Task AddSubscriptionCommand_AddsItem() { @@ -58,6 +59,7 @@ public class SubscriptionsViewModelTests } /// Verifies that RemoveSubscriptionCommand removes selected subscription. + /// A task that represents the asynchronous test operation. [Fact] public async Task RemoveSubscriptionCommand_RemovesItem() { @@ -83,6 +85,7 @@ public class SubscriptionsViewModelTests } /// Verifies that DataChanged event updates the matching subscription row. + /// A task that represents the asynchronous test operation. [Fact] public async Task DataChanged_UpdatesMatchingRow() { @@ -98,6 +101,7 @@ public class SubscriptionsViewModelTests } /// Verifies that DataChanged event does not update non-matching subscription rows. + /// A task that represents the asynchronous test operation. [Fact] public async Task DataChanged_DoesNotUpdateNonMatchingRow() { @@ -146,6 +150,7 @@ public class SubscriptionsViewModelTests } /// Verifies that AddSubscriptionForNodeAsync adds a subscription. + /// A task that represents the asynchronous test operation. [Fact] public async Task AddSubscriptionForNodeAsync_AddsSubscription() { @@ -160,6 +165,7 @@ public class SubscriptionsViewModelTests } /// Verifies that AddSubscriptionForNodeAsync skips duplicate subscriptions. + /// A task that represents the asynchronous test operation. [Fact] public async Task AddSubscriptionForNodeAsync_SkipsDuplicate() { @@ -173,6 +179,7 @@ public class SubscriptionsViewModelTests } /// Verifies that AddSubscriptionForNodeAsync does nothing when disconnected. + /// A task that represents the asynchronous test operation. [Fact] public async Task AddSubscriptionForNodeAsync_DoesNothing_WhenDisconnected() { @@ -185,6 +192,7 @@ public class SubscriptionsViewModelTests } /// Verifies that GetSubscribedNodeIds returns all active subscription node IDs. + /// A task that represents the asynchronous test operation. [Fact] public async Task GetSubscribedNodeIds_ReturnsActiveNodeIds() { @@ -200,6 +208,7 @@ public class SubscriptionsViewModelTests } /// Verifies that RestoreSubscriptionsAsync subscribes to all provided node IDs. + /// A task that represents the asynchronous test operation. [Fact] public async Task RestoreSubscriptionsAsync_SubscribesAllNodes() { @@ -212,6 +221,7 @@ public class SubscriptionsViewModelTests } /// Verifies that ValidateAndWriteAsync returns true on successful write. + /// A task that represents the asynchronous test operation. [Fact] public async Task ValidateAndWriteAsync_SuccessReturnsTrue() { @@ -227,6 +237,7 @@ public class SubscriptionsViewModelTests } /// Verifies that ValidateAndWriteAsync returns false when value parsing fails. + /// A task that represents the asynchronous test operation. [Fact] public async Task ValidateAndWriteAsync_ParseFailureReturnsFalse() { @@ -242,6 +253,7 @@ public class SubscriptionsViewModelTests } /// Verifies that ValidateAndWriteAsync returns false when write fails. + /// A task that represents the asynchronous test operation. [Fact] public async Task ValidateAndWriteAsync_WriteFailureReturnsFalse() { @@ -256,6 +268,7 @@ public class SubscriptionsViewModelTests } /// Verifies that ValidateAndWriteAsync returns false when status is bad. + /// A task that represents the asynchronous test operation. [Fact] public async Task ValidateAndWriteAsync_BadStatusReturnsFalse() { @@ -270,6 +283,7 @@ public class SubscriptionsViewModelTests } /// Verifies that AddSubscriptionRecursiveAsync subscribes a variable directly. + /// A task that represents the asynchronous test operation. [Fact] public async Task AddSubscriptionRecursiveAsync_SubscribesVariableDirectly() { @@ -282,6 +296,7 @@ public class SubscriptionsViewModelTests } /// Verifies that AddSubscriptionRecursiveAsync browses objects and subscribes variable children. + /// A task that represents the asynchronous test operation. [Fact] public async Task AddSubscriptionRecursiveAsync_BrowsesObjectAndSubscribesVariableChildren() { @@ -302,6 +317,7 @@ public class SubscriptionsViewModelTests /// Regression test for Client.UI-006 — when SubscribeAsync throws, the failure must be surfaced /// to the operator via the view model's StatusMessage rather than silently swallowed. /// + /// A task that represents the asynchronous test operation. [Fact] public async Task AddSubscription_OnFailure_SurfacesStatusMessage() { @@ -320,6 +336,7 @@ public class SubscriptionsViewModelTests /// Regression test for Client.UI-006 — silent swallow when adding a subscription for a node /// (the context-menu helper) must also surface a status to the operator. /// + /// A task that represents the asynchronous test operation. [Fact] public async Task AddSubscriptionForNodeAsync_OnFailure_SurfacesStatusMessage() { @@ -334,6 +351,7 @@ public class SubscriptionsViewModelTests } /// Verifies that AddSubscriptionRecursiveAsync recurses through nested objects. + /// A task that represents the asynchronous test operation. [Fact] public async Task AddSubscriptionRecursiveAsync_RecursesNestedObjects() { diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/ClusterAuditQueryTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/ClusterAuditQueryTests.cs index ee121383..bc87e6d8 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/ClusterAuditQueryTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/ClusterAuditQueryTests.cs @@ -51,6 +51,7 @@ public sealed class ClusterAuditQueryTests : IDisposable /// Structured rows (ClusterId null, NodeId set) for a node in the cluster are now /// visible, alongside the SP-path rows that stamp ClusterId directly. + /// A task that represents the asynchronous operation. [Fact] public async Task Surfaces_both_clusterId_rows_and_structured_nodeId_rows() { @@ -77,6 +78,7 @@ public sealed class ClusterAuditQueryTests : IDisposable } /// An audit row stamped with another cluster's ClusterId never appears. + /// A task that represents the asynchronous operation. [Fact] public async Task Does_not_surface_other_cluster_rows() { @@ -91,6 +93,7 @@ public sealed class ClusterAuditQueryTests : IDisposable } /// Respects the page-size cap, newest first. + /// A task that represents the asynchronous operation. [Fact] public async Task Caps_to_page_size_newest_first() { diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/DriverHostStatusTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/DriverHostStatusTests.cs index a3f9619a..4f62ee7e 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/DriverHostStatusTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/DriverHostStatusTests.cs @@ -17,6 +17,7 @@ namespace ZB.MOM.WW.OtOpcUa.Configuration.Tests; public sealed class DriverHostStatusTests(SchemaComplianceFixture fixture) { /// Verifies that the composite key allows the same host across different nodes or drivers. + /// A task that represents the asynchronous operation. [Fact] public async Task Composite_key_allows_same_host_across_different_nodes_or_drivers() { @@ -58,6 +59,7 @@ public sealed class DriverHostStatusTests(SchemaComplianceFixture fixture) } /// Verifies that the upsert pattern updates existing records in place. + /// A task that represents the asynchronous operation. [Fact] public async Task Upsert_pattern_for_same_key_updates_in_place() { @@ -97,6 +99,7 @@ public sealed class DriverHostStatusTests(SchemaComplianceFixture fixture) } /// Verifies that the State enum is persisted as a string, not an integer. + /// A task that represents the asynchronous operation. [Fact] public async Task Enum_persists_as_string_not_int() { diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/GenerationSealedCacheTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/GenerationSealedCacheTests.cs index d7ed3121..f5ea53b9 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/GenerationSealedCacheTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/GenerationSealedCacheTests.cs @@ -33,6 +33,7 @@ public sealed class GenerationSealedCacheTests : IDisposable }; /// Verifies that reading a snapshot on first boot with no existing snapshot throws. + /// A task that represents the asynchronous operation. [Fact] public async Task FirstBoot_NoSnapshot_ReadThrows() { @@ -43,6 +44,7 @@ public sealed class GenerationSealedCacheTests : IDisposable } /// Verifies that sealed snapshots can be read back correctly. + /// A task that represents the asynchronous operation. [Fact] public async Task SealThenRead_RoundTrips() { @@ -58,6 +60,7 @@ public sealed class GenerationSealedCacheTests : IDisposable } /// Verifies that sealed files are marked read-only on disk. + /// A task that represents the asynchronous operation. [Fact] public async Task SealedFile_IsReadOnly_OnDisk() { @@ -71,6 +74,7 @@ public sealed class GenerationSealedCacheTests : IDisposable } /// Verifies that the current generation pointer advances when a new generation is sealed. + /// A task that represents the asynchronous operation. [Fact] public async Task SealingTwoGenerations_PointerAdvances_ToLatest() { @@ -84,6 +88,7 @@ public sealed class GenerationSealedCacheTests : IDisposable } /// Verifies that prior generation files are preserved after a new seal. + /// A task that represents the asynchronous operation. [Fact] public async Task PriorGenerationFile_Survives_AfterNewSeal() { @@ -97,6 +102,7 @@ public sealed class GenerationSealedCacheTests : IDisposable } /// Verifies that reading a corrupt sealed file fails safely. + /// A task that represents the asynchronous operation. [Fact] public async Task CorruptSealedFile_ReadFailsClosed() { @@ -113,6 +119,7 @@ public sealed class GenerationSealedCacheTests : IDisposable } /// Verifies that reading with a missing sealed file fails safely. + /// A task that represents the asynchronous operation. [Fact] public async Task MissingSealedFile_ReadFailsClosed() { @@ -129,6 +136,7 @@ public sealed class GenerationSealedCacheTests : IDisposable } /// Verifies that reading with a corrupt pointer file fails safely. + /// A task that represents the asynchronous operation. [Fact] public async Task CorruptPointerFile_ReadFailsClosed() { @@ -143,6 +151,7 @@ public sealed class GenerationSealedCacheTests : IDisposable } /// Verifies that sealing the same generation twice is idempotent. + /// A task that represents the asynchronous operation. [Fact] public async Task SealSameGenerationTwice_IsIdempotent() { @@ -155,6 +164,7 @@ public sealed class GenerationSealedCacheTests : IDisposable } /// Verifies that independent clusters do not interfere with each other. + /// A task that represents the asynchronous operation. [Fact] public async Task IndependentClusters_DoNotInterfere() { diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/LdapGroupRoleMappingServiceTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/LdapGroupRoleMappingServiceTests.cs index c9ce8e21..afaff2c6 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/LdapGroupRoleMappingServiceTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/LdapGroupRoleMappingServiceTests.cs @@ -34,6 +34,7 @@ public sealed class LdapGroupRoleMappingServiceTests : IDisposable }; /// Verifies that Create sets Id and CreatedAtUtc. + /// A task that represents the asynchronous test operation. [Fact] public async Task Create_SetsId_AndCreatedAtUtc() { @@ -47,6 +48,7 @@ public sealed class LdapGroupRoleMappingServiceTests : IDisposable } /// Verifies that Create rejects empty LDAP group. + /// A task that represents the asynchronous test operation. [Fact] public async Task Create_Rejects_EmptyLdapGroup() { @@ -58,6 +60,7 @@ public sealed class LdapGroupRoleMappingServiceTests : IDisposable } /// Verifies that Create rejects system-wide mapping with ClusterId. + /// A task that represents the asynchronous test operation. [Fact] public async Task Create_Rejects_SystemWide_With_ClusterId() { @@ -69,6 +72,7 @@ public sealed class LdapGroupRoleMappingServiceTests : IDisposable } /// Verifies that Create rejects non-system-wide mapping without ClusterId. + /// A task that represents the asynchronous test operation. [Fact] public async Task Create_Rejects_NonSystemWide_WithoutClusterId() { @@ -80,6 +84,7 @@ public sealed class LdapGroupRoleMappingServiceTests : IDisposable } /// Verifies that GetByGroups returns only matching grants. + /// A task that represents the asynchronous test operation. [Fact] public async Task GetByGroups_Returns_MatchingGrants_Only() { @@ -96,6 +101,7 @@ public sealed class LdapGroupRoleMappingServiceTests : IDisposable } /// Verifies that GetByGroups returns empty when input is empty. + /// A task that represents the asynchronous test operation. [Fact] public async Task GetByGroups_Empty_Input_ReturnsEmpty() { @@ -108,6 +114,7 @@ public sealed class LdapGroupRoleMappingServiceTests : IDisposable } /// Verifies that ListAll orders results by group then cluster. + /// A task that represents the asynchronous test operation. [Fact] public async Task ListAll_Orders_ByGroupThenCluster() { @@ -125,6 +132,7 @@ public sealed class LdapGroupRoleMappingServiceTests : IDisposable } /// Verifies that Delete removes the matching row. + /// A task that represents the asynchronous test operation. [Fact] public async Task Delete_Removes_Matching_Row() { @@ -138,6 +146,7 @@ public sealed class LdapGroupRoleMappingServiceTests : IDisposable } /// Verifies that Delete with unknown Id is a no-op. + /// A task that represents the asynchronous test operation. [Fact] public async Task Delete_Unknown_Id_IsNoOp() { @@ -148,6 +157,7 @@ public sealed class LdapGroupRoleMappingServiceTests : IDisposable } /// Verifies that a system-wide row (IsSystemWide=true, ClusterId=null) appears in both ListAllAsync and GetByGroupsAsync. + /// A task that represents the asynchronous test operation. [Fact] public async Task SystemWide_Row_AppearsIn_ListAll_And_GetByGroups() { diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/LiteDbConfigCacheTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/LiteDbConfigCacheTests.cs index 527c5b66..c90bb84f 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/LiteDbConfigCacheTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/LiteDbConfigCacheTests.cs @@ -24,6 +24,7 @@ public sealed class LiteDbConfigCacheTests : IDisposable }; /// Verifies that payload is preserved through a write-then-read cycle. + /// A task that represents the asynchronous test operation. [Fact] public async Task Roundtrip_preserves_payload() { @@ -38,6 +39,7 @@ public sealed class LiteDbConfigCacheTests : IDisposable } /// Verifies that GetMostRecentAsync returns the latest generation when multiple exist. + /// A task that represents the asynchronous test operation. [Fact] public async Task GetMostRecent_returns_latest_when_multiple_generations_present() { @@ -50,6 +52,7 @@ public sealed class LiteDbConfigCacheTests : IDisposable } /// Verifies that GetMostRecentAsync returns null for an unknown cluster. + /// A task that represents the asynchronous test operation. [Fact] public async Task GetMostRecent_returns_null_for_unknown_cluster() { @@ -58,6 +61,7 @@ public sealed class LiteDbConfigCacheTests : IDisposable } /// Verifies that Prune keeps the latest N generations and drops older ones. + /// A task that represents the asynchronous test operation. [Fact] public async Task Prune_keeps_latest_N_and_drops_older() { @@ -81,6 +85,7 @@ public sealed class LiteDbConfigCacheTests : IDisposable } /// Verifies that writing the same cluster/generation twice replaces rather than duplicates. + /// A task that represents the asynchronous test operation. [Fact] public async Task Put_same_cluster_generation_twice_replaces_not_duplicates() { @@ -102,6 +107,7 @@ public sealed class LiteDbConfigCacheTests : IDisposable // callers could both observe `existing is null` and both Insert. // ------------------------------------------------------------------------------------ /// Verifies that concurrent PutAsync calls for the same cluster and generation do not create duplicates. + /// A task that represents the asynchronous test operation. [Fact] public async Task PutAsync_concurrent_for_same_cluster_and_generation_does_not_duplicate() { diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/ResilientConfigReaderTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/ResilientConfigReaderTests.cs index fd24bdbd..0dad3b99 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/ResilientConfigReaderTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/ResilientConfigReaderTests.cs @@ -26,6 +26,7 @@ public sealed class ResilientConfigReaderTests : IDisposable } /// Verifies that successful central DB reads return value and mark fresh. + /// A task that represents the asynchronous test. [Fact] public async Task CentralDbSucceeds_ReturnsValue_MarksFresh() { @@ -45,6 +46,7 @@ public sealed class ResilientConfigReaderTests : IDisposable } /// Verifies that exhausted retries fall back to cache and mark stale. + /// A task that represents the asynchronous test. [Fact] public async Task CentralDbFails_ExhaustsRetries_FallsBackToCache_MarksStale() { @@ -78,6 +80,7 @@ public sealed class ResilientConfigReaderTests : IDisposable } /// Verifies that DB failure with unavailable cache throws. + /// A task that represents the asynchronous test. [Fact] public async Task CentralDbFails_AndCacheAlsoUnavailable_Throws() { @@ -99,6 +102,7 @@ public sealed class ResilientConfigReaderTests : IDisposable } /// Verifies that cancellation is not retried. + /// A task that represents the asynchronous test. [Fact] public async Task Cancellation_NotRetried() { @@ -133,6 +137,7 @@ public sealed class ResilientConfigReaderTests : IDisposable // ------------------------------------------------------------------------------------ /// Verifies that command timeout TaskCanceledException falls back to cache. + /// A task that represents the asynchronous test. [Fact] public async Task CommandTimeout_TaskCanceledException_FallsBackToCache() { @@ -163,6 +168,7 @@ public sealed class ResilientConfigReaderTests : IDisposable } /// Verifies that Polly timeout rejection falls back to cache. + /// A task that represents the asynchronous test. [Fact] public async Task PollyTimeout_TimeoutRejectedException_FallsBackToCache() { @@ -201,6 +207,7 @@ public sealed class ResilientConfigReaderTests : IDisposable // ------------------------------------------------------------------------------------ /// Verifies that fallback warnings do not log exceptions or password fragments. + /// A task that represents the asynchronous test. [Fact] public async Task FallbackWarning_does_not_log_full_exception_object_or_password_fragment() { @@ -242,6 +249,7 @@ public sealed class ResilientConfigReaderTests : IDisposable } /// Verifies that caller cancellation propagates rather than falling back. + /// A task that represents the asynchronous test. [Fact] public async Task CallerCancellation_Propagates_NotFallback() { diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions.Tests/PollGroupEngineTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions.Tests/PollGroupEngineTests.cs index 1a0af37f..41ad6eeb 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions.Tests/PollGroupEngineTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions.Tests/PollGroupEngineTests.cs @@ -35,6 +35,7 @@ public sealed class PollGroupEngineTests } /// Verifies that the initial poll forces an event for every subscribed tag. + /// A task that represents the asynchronous operation. [Fact] public async Task Initial_poll_force_raises_every_subscribed_tag() { @@ -54,6 +55,7 @@ public sealed class PollGroupEngineTests } /// Verifies that unchanged values are only raised once. + /// A task that represents the asynchronous operation. [Fact] public async Task Unchanged_value_raises_only_once() { @@ -72,6 +74,7 @@ public sealed class PollGroupEngineTests } /// Verifies that value changes raise new events. + /// A task that represents the asynchronous operation. [Fact] public async Task Value_change_raises_new_event() { @@ -92,6 +95,7 @@ public sealed class PollGroupEngineTests } /// Verifies that unsubscribe halts the polling loop. + /// A task that represents the asynchronous operation. [Fact] public async Task Unsubscribe_halts_the_loop() { @@ -113,6 +117,7 @@ public sealed class PollGroupEngineTests } /// Verifies that intervals below the configured floor are clamped. + /// A task that represents the asynchronous operation. [Fact] public async Task Interval_below_floor_is_clamped() { @@ -134,6 +139,7 @@ public sealed class PollGroupEngineTests } /// Verifies that multiple subscriptions operate independently. + /// A task that represents the asynchronous operation. [Fact] public async Task Multiple_subscriptions_are_independent() { @@ -165,6 +171,7 @@ public sealed class PollGroupEngineTests } /// Verifies that reader exceptions do not crash the polling loop. + /// A task that represents the asynchronous operation. [Fact] public async Task Reader_exception_does_not_crash_loop() { @@ -195,6 +202,7 @@ public sealed class PollGroupEngineTests } /// Verifies that unsubscribing an unknown handle returns false. + /// A task that represents the asynchronous operation. [Fact] public async Task Unsubscribe_unknown_handle_returns_false() { @@ -206,6 +214,7 @@ public sealed class PollGroupEngineTests } /// Verifies that the active subscription count tracks lifecycle changes. + /// A task that represents the asynchronous operation. [Fact] public async Task ActiveSubscriptionCount_tracks_lifecycle() { @@ -225,6 +234,7 @@ public sealed class PollGroupEngineTests } /// Verifies that disposing the engine cancels all active subscriptions. + /// A task that represents the asynchronous operation. [Fact] public async Task DisposeAsync_cancels_all_subscriptions() { @@ -253,6 +263,7 @@ public sealed class PollGroupEngineTests /// must fire only the initial change event, not a spurious event on every tick, even /// when the driver produces a fresh array instance on each read. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Array_valued_tag_unchanged_contents_raises_only_once() { @@ -289,6 +300,7 @@ public sealed class PollGroupEngineTests /// Core.Abstractions-001: an array-valued tag whose contents change between polls /// must fire a change event for each distinct set of contents. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Array_valued_tag_changed_contents_raises_event() { @@ -320,6 +332,7 @@ public sealed class PollGroupEngineTests /// violates the documented contract. The engine must throw a descriptive exception /// rather than silently stalling. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Reader_short_result_list_raises_descriptive_exception_and_loop_continues() { @@ -366,6 +379,7 @@ public sealed class PollGroupEngineTests /// must continue to swallow exceptions (backward compatible). When an error callback IS /// supplied, every exception caught during a poll cycle must be routed to it. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Reader_exception_is_reported_to_onError_callback() { @@ -401,6 +415,7 @@ public sealed class PollGroupEngineTests /// must also be routed to the error callback so the driver health surface can observe /// repeated contract violations. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Reader_contract_violation_routes_to_onError_callback() { @@ -433,6 +448,7 @@ public sealed class PollGroupEngineTests /// that itself throws — otherwise a buggy health-surface forwarder would crash the poll /// loop and silently stall the subscription, defeating the whole point of the callback. /// + /// A task that represents the asynchronous operation. [Fact] public async Task OnError_handler_that_throws_does_not_crash_loop() { @@ -463,7 +479,7 @@ public sealed class PollGroupEngineTests private sealed record DummyHandle : ISubscriptionHandle { - /// Gets a diagnostic identifier for this handle. + /// public string DiagnosticId => "dummy"; } diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian.Tests/SqliteStoreAndForwardSinkTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian.Tests/SqliteStoreAndForwardSinkTests.cs index 0ae583d6..aeb78420 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian.Tests/SqliteStoreAndForwardSinkTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian.Tests/SqliteStoreAndForwardSinkTests.cs @@ -43,10 +43,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable /// Gets or sets an exception to throw once. public Exception? ThrowOnce { get; set; } - /// Writes a batch of events. - /// Events to write. - /// Cancellation token. - /// A task returning the write outcomes. + /// public Task> WriteBatchAsync( IReadOnlyList batch, CancellationToken ct) { @@ -77,6 +74,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable TimestampUtc: ts ?? DateTime.UtcNow); /// Verifies that acknowledged events are removed from the queue. + /// A task that represents the asynchronous operation. [Fact] public async Task EnqueueThenDrain_Ack_removes_row() { @@ -98,6 +96,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable } /// Verifies that draining an empty queue is a no-op. + /// A task that represents the asynchronous operation. [Fact] public async Task Drain_with_empty_queue_is_noop() { @@ -111,6 +110,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable } /// Verifies that RetryPlease outcome bumps backoff and keeps the row queued. + /// A task that represents the asynchronous operation. [Fact] public async Task RetryPlease_bumps_backoff_and_keeps_row() { @@ -128,6 +128,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable } /// Verifies that an Ack after RetryPlease resets backoff to the floor. + /// A task that represents the asynchronous operation. [Fact] public async Task Ack_after_Retry_resets_backoff() { @@ -147,6 +148,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable } /// Verifies that PermanentFail outcome dead-letters only the failed event. + /// A task that represents the asynchronous operation. [Fact] public async Task PermanentFail_dead_letters_one_row_only() { @@ -165,6 +167,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable } /// Verifies that writer exceptions trigger retry for the entire batch. + /// A task that represents the asynchronous operation. [Fact] public async Task Writer_exception_treated_as_retry_for_whole_batch() { @@ -185,6 +188,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable } /// Verifies that capacity eviction drops the oldest non-dead-lettered row. + /// A task that represents the asynchronous operation. [Fact] public async Task Capacity_eviction_drops_oldest_nondeadlettered_row() { @@ -209,6 +213,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable } /// Verifies that dead-lettered rows are purged after retention period expires. + /// A task that represents the asynchronous operation. [Fact] public async Task Deadlettered_rows_are_purged_past_retention() { @@ -233,6 +238,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable } /// Verifies that RetryDeadLettered requeues dead-lettered rows for retry. + /// A task that represents the asynchronous operation. [Fact] public async Task RetryDeadLettered_requeues_for_retry() { @@ -253,6 +259,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable } /// Verifies that the exponential backoff ladder caps at 60 seconds. + /// A task that represents the asynchronous operation. [Fact] public async Task Backoff_ladder_caps_at_60s() { @@ -278,6 +285,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable } /// Verifies that NullAlarmHistorianSink silently swallows enqueue calls. + /// A task that represents the asynchronous operation. [Fact] public async Task NullAlarmHistorianSink_swallows_enqueue() { @@ -298,6 +306,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable } /// Verifies that a disposed sink rejects enqueue operations. + /// A task that represents the asynchronous operation. [Fact] public async Task Disposed_sink_rejects_enqueue() { @@ -316,6 +325,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable /// forever), and every good row's outcome is applied to the CORRECT RowId — /// no good event is silently lost. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Drain_with_corrupt_payload_row_deadletters_it_and_keeps_good_rows_aligned() { @@ -347,6 +357,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable /// A corrupt row at the very head of the queue must be dead-lettered and not /// prevent the good rows behind it from draining. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Drain_with_corrupt_head_row_does_not_stall_queue() { @@ -373,6 +384,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable /// hammer the writer. We assert that after the backoff ladder advances, the /// observed inter-batch gap actually grows beyond the bare tick interval. /// + /// A task that represents the asynchronous operation. [Fact] public async Task StartDrainLoop_honors_backoff_and_slows_cadence_under_retry() { @@ -400,6 +412,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable /// steady cadence (backoff stays at the floor) — confirms the reschedule path /// does not get stuck after a successful tick. /// + /// A task that represents the asynchronous operation. [Fact] public async Task StartDrainLoop_keeps_steady_cadence_when_writer_is_healthy() { @@ -425,6 +438,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable /// be recorded into the status surface (LastError) and the drain loop must /// keep rescheduling rather than silently dying. /// + /// A task that represents the asynchronous operation. [Fact] public async Task StartDrainLoop_records_drain_fault_and_keeps_running() { @@ -454,6 +468,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable /// the busy_timeout + WAL pragmas in place the loser of the file-lock race /// waits the lock out instead of failing fast. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Concurrent_enqueue_and_drain_do_not_throw_sqlite_busy() { @@ -501,10 +516,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable /// Gets the number of times WriteBatchAsync has been called. public int CallCount { get; private set; } - /// Writes a batch of events, throwing once then recovering. - /// Events to write. - /// Cancellation token. - /// A task returning the write outcomes. + /// public Task> WriteBatchAsync( IReadOnlyList batch, CancellationToken ct) { @@ -525,6 +537,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable /// by the timer callback, but still left the rows stranded on the first /// cardinality-mismatched tick. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Writer_returning_wrong_cardinality_outcomes_sets_backing_off_and_keeps_rows() { @@ -555,6 +568,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable /// evicted row count must be surfaced in /// so operators can detect bounded-durability overflow without log scraping. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Capacity_eviction_increments_evicted_count_on_status() { @@ -580,6 +594,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable /// snapshot of all status fields — no torn DateTime? or stale DrainState. /// Drive status writes from one thread and reads from another concurrently. /// + /// A task that represents the asynchronous operation. [Fact] public async Task GetStatus_snapshot_is_consistent_under_concurrent_drain() { @@ -632,10 +647,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable /// Fixes the writer to return correct cardinality. public void FixWriter() => _returnExtra = false; - /// Writes a batch of events, returning wrong cardinality until fixed. - /// Events to write. - /// Cancellation token. - /// A task returning the write outcomes. + /// public Task> WriteBatchAsync( IReadOnlyList batch, CancellationToken ct) { @@ -654,6 +666,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable /// capacity-probe count must stay bounded — not grow proportionally to the /// enqueue count as the un-optimised path did. /// + /// A task that represents the asynchronous operation. [Fact] public async Task EnqueueAsync_does_not_count_all_rows_on_every_call_below_capacity() { @@ -677,6 +690,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable /// stay aligned with a fresh COUNT(*) against the database. Catches drift /// bugs in the in-memory counter introduced by the perf optimisation. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Enqueue_and_drain_keep_queue_depth_consistent_with_storage() { @@ -724,6 +738,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable /// with storage. Catches drift bugs in the optimised path that would only show /// up under contention. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Counter_remains_consistent_under_concurrent_enqueue_and_drain() { diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms.Tests/FakeUpstream.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms.Tests/FakeUpstream.cs index 6ad0a3fc..26ae23d3 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms.Tests/FakeUpstream.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms.Tests/FakeUpstream.cs @@ -38,15 +38,12 @@ public sealed class FakeUpstream : ITagUpstreamSource } } - /// Reads the current value of a tag, or returns a bad-status snapshot if not set. - /// The tag path to read. + /// public DataValueSnapshot ReadTag(string path) => _values.TryGetValue(path, out var v) ? v : new DataValueSnapshot(null, 0x80340000u, null, DateTime.UtcNow); - /// Subscribes an observer to tag changes for the given path. - /// The tag path to subscribe to. - /// The observer callback to invoke on tag changes. + /// public IDisposable SubscribeTag(string path, Action observer) { var list = _subs.GetOrAdd(path, _ => []); diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms.Tests/ScriptedAlarmEngineTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms.Tests/ScriptedAlarmEngineTests.cs index 217e7944..60437333 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms.Tests/ScriptedAlarmEngineTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms.Tests/ScriptedAlarmEngineTests.cs @@ -32,6 +32,7 @@ public sealed class ScriptedAlarmEngineTests PredicateScriptSource: predicate); /// Verifies that LoadAsync compiles the alarm predicate and subscribes to all referenced upstream tags. + /// A task that represents the asynchronous operation. [Fact] public async Task Load_compiles_and_subscribes_to_referenced_upstreams() { @@ -47,6 +48,7 @@ public sealed class ScriptedAlarmEngineTests } /// Verifies that compile failures across multiple alarms are aggregated into a single error. + /// A task that represents the asynchronous operation. [Fact] public async Task Compile_failures_aggregated_into_one_error() { @@ -63,6 +65,7 @@ public sealed class ScriptedAlarmEngineTests } /// Verifies that an upstream tag change triggers predicate re-evaluation and emits an Activated event. + /// A task that represents the asynchronous operation. [Fact] public async Task Upstream_change_re_evaluates_predicate_and_emits_Activated() { @@ -84,6 +87,7 @@ public sealed class ScriptedAlarmEngineTests } /// Verifies that clearing an upstream tag value emits a Cleared event and transitions the alarm to Inactive. + /// A task that represents the asynchronous operation. [Fact] public async Task Clearing_upstream_emits_Cleared_event() { @@ -105,6 +109,7 @@ public sealed class ScriptedAlarmEngineTests } /// Verifies that the message template resolves current tag values at the moment of alarm emission. + /// A task that represents the asynchronous operation. [Fact] public async Task Message_template_resolves_tag_values_at_emission() { @@ -130,6 +135,7 @@ public sealed class ScriptedAlarmEngineTests } /// Verifies that AcknowledgeAsync records the operator user and persists the ack state to the store. + /// A task that represents the asynchronous operation. [Fact] public async Task Ack_records_user_and_persists_to_store() { @@ -150,6 +156,7 @@ public sealed class ScriptedAlarmEngineTests } /// Verifies that startup recovery restores the persisted ack state but re-derives the active state from the live predicate. + /// A task that represents the asynchronous operation. [Fact] public async Task Startup_recovery_preserves_ack_but_rederives_active_from_predicate() { @@ -198,6 +205,7 @@ public sealed class ScriptedAlarmEngineTests } /// Verifies that a shelved alarm transitions its internal state on activation but suppresses the Activated emission. + /// A task that represents the asynchronous operation. [Fact] public async Task Shelved_active_transitions_state_but_suppresses_emission() { @@ -222,6 +230,7 @@ public sealed class ScriptedAlarmEngineTests } /// Verifies that a runtime exception thrown by a predicate script leaves the alarm state unchanged and does not affect other alarms. + /// A task that represents the asynchronous operation. [Fact] public async Task Predicate_runtime_exception_does_not_transition_state() { @@ -239,6 +248,7 @@ public sealed class ScriptedAlarmEngineTests } /// Verifies that a disabled alarm does not activate on predicate change and resumes normally after being re-enabled. + /// A task that represents the asynchronous operation. [Fact] public async Task Disable_prevents_activation_until_re_enabled() { @@ -260,6 +270,7 @@ public sealed class ScriptedAlarmEngineTests } /// Verifies that AddCommentAsync appends to the audit trail without changing the alarm's active or ack state. + /// A task that represents the asynchronous operation. [Fact] public async Task AddComment_appends_to_audit_without_state_change() { @@ -278,6 +289,7 @@ public sealed class ScriptedAlarmEngineTests } /// Verifies that predicate scripts are forbidden from calling SetVirtualTag, and that the exception is isolated without state change. + /// A task that represents the asynchronous operation. [Fact] public async Task Predicate_scripts_cannot_SetVirtualTag() { @@ -302,6 +314,7 @@ public sealed class ScriptedAlarmEngineTests } /// Verifies that disposing the engine releases all upstream tag subscriptions. + /// A task that represents the asynchronous operation. [Fact] public async Task Dispose_releases_upstream_subscriptions() { @@ -317,6 +330,7 @@ public sealed class ScriptedAlarmEngineTests } /// Verifies that concurrent reads of alarm state during dictionary mutations do not throw (regression for Core.ScriptedAlarms-001). + /// A task that represents the asynchronous operation. [Fact] public async Task Concurrent_reads_during_mutation_do_not_throw(/* Core.ScriptedAlarms-001 */) { @@ -386,6 +400,7 @@ public sealed class ScriptedAlarmEngineTests // injectable clock — the clock and scriptTimeout constructor parameters // exist for exactly this. /// Verifies that a timed shelve automatically expires when the engine's shelving check runs past the unshelve time. + /// A task that represents the asynchronous operation. [Fact] public async Task TimedShelve_auto_expires_when_engine_shelving_check_runs(/* -012 (1) */) { @@ -423,6 +438,7 @@ public sealed class ScriptedAlarmEngineTests // (2a) ConfirmAsync end-to-end through the engine. /// Verifies that ConfirmAsync records the confirming user and emits a Confirmed event persisted to the store. + /// A task that represents the asynchronous operation. [Fact] public async Task ConfirmAsync_records_user_and_emits_Confirmed(/* -012 (2) */) { @@ -450,6 +466,7 @@ public sealed class ScriptedAlarmEngineTests // (2b) TimedShelveAsync / UnshelveAsync end-to-end through the engine. /// Verifies that TimedShelveAsync shelves with a deadline and UnshelveAsync removes the shelve before the timer expires. + /// A task that represents the asynchronous operation. [Fact] public async Task TimedShelveAsync_and_UnshelveAsync_round_trip(/* -012 (2) */) { @@ -478,6 +495,7 @@ public sealed class ScriptedAlarmEngineTests // (2c) EnableAsync end-to-end through the engine. /// Verifies that EnableAsync transitions the alarm back to Enabled state and emits an Enabled event. + /// A task that represents the asynchronous operation. [Fact] public async Task EnableAsync_re_enables_after_disable(/* -012 (2) */) { @@ -501,6 +519,7 @@ public sealed class ScriptedAlarmEngineTests // the engine or prevent subsequent alarm state transitions. The engine logs // the exception and continues operating; any later alarm changes still work. /// Verifies that an exception thrown by an OnEvent subscriber is isolated and does not crash the engine or prevent further state transitions. + /// A task that represents the asynchronous operation. [Fact] public async Task OnEvent_subscriber_exception_does_not_crash_engine(/* -012 (3) */) { @@ -534,6 +553,7 @@ public sealed class ScriptedAlarmEngineTests // (4) IAlarmStateStore.SaveAsync failure — in-memory state must remain at the // prior value after finding -007 fix (persist-before-update). /// Verifies that a store SaveAsync failure leaves the in-memory alarm state at its prior value (persist-before-update invariant, finding -007). + /// A task that represents the asynchronous operation. [Fact] public async Task Store_save_failure_leaves_in_memory_state_unchanged(/* -012 (4) */) { @@ -565,6 +585,7 @@ public sealed class ScriptedAlarmEngineTests // (5) Re-entrant LoadAsync — the old timer must not keep firing after a second // call (regression for finding -002: _shelvingTimer?.Dispose() fix). /// Verifies that a second LoadAsync call disposes the prior shelving timer so it does not keep firing after reload (regression for finding -002). + /// A task that represents the asynchronous operation. [Fact] public async Task Second_LoadAsync_does_not_leak_old_timer(/* -012 (5) */) { @@ -598,6 +619,7 @@ public sealed class ScriptedAlarmEngineTests // (6) Cold-start AreInputsReady guard — null value, Bad status, and Uncertain // status inputs are all handled correctly. /// Verifies that AreInputsReady blocks predicate evaluation when inputs have null values or Bad status codes, while Uncertain quality is accepted. + /// A task that represents the asynchronous operation. [Fact] public async Task AreInputsReady_blocks_evaluation_for_null_and_bad_inputs(/* -012 (6) */) { @@ -635,6 +657,7 @@ public sealed class ScriptedAlarmEngineTests // not deadlock against _evalGate. Both regressions are covered here. // ------------------------------------------------------------------------- /// Verifies that an OnEvent subscriber can call engine methods (e.g. AcknowledgeAsync) without deadlocking against the evaluation gate (regression for Core.ScriptedAlarms-003). + /// A task that represents the asynchronous operation. [Fact] public async Task OnEvent_subscriber_can_call_back_into_engine_without_deadlock(/* -003 */) { @@ -748,6 +771,7 @@ public sealed class ScriptedAlarmEngineTests // to a (possibly disposed) store after the engine has returned. // ------------------------------------------------------------------------- /// Verifies that Dispose blocks until in-flight background re-evaluation tasks complete, preventing the engine from outliving its store (regression for Core.ScriptedAlarms-006). + /// A task that represents the asynchronous operation. [Fact] public async Task Dispose_drains_in_flight_reevaluation_tasks(/* -006 */) { @@ -794,6 +818,7 @@ public sealed class ScriptedAlarmEngineTests // explicitly. The two policies are documented in docs/ScriptedAlarms.md. // ------------------------------------------------------------------------- /// Verifies that Uncertain-quality inputs are accepted by the predicate but rendered as "{?}" in the operator-facing message template (Core.ScriptedAlarms-010). + /// A task that represents the asynchronous operation. [Fact] public async Task Uncertain_quality_drives_predicate_but_renders_question_mark_in_message(/* -010 */) { @@ -842,6 +867,7 @@ public sealed class ScriptedAlarmEngineTests // consumers). // ------------------------------------------------------------------------- /// Verifies that the Comments collection is an ImmutableList, enabling O(log n) append and satisfying IReadOnlyList consumers (Core.ScriptedAlarms-008). + /// A task that represents the asynchronous operation. [Fact] public async Task Comments_collection_uses_ImmutableList_for_efficient_append(/* -008 */) { @@ -907,29 +933,22 @@ public sealed class ScriptedAlarmEngineTests /// Gets or sets a value indicating whether the next SaveAsync call should throw a simulated failure. public bool FailSave { get; set; } - /// Loads an alarm condition state by ID from the inner store. - /// The ID of the alarm condition state to load. - /// A cancellation token. + /// public Task LoadAsync(string alarmId, CancellationToken ct) => _inner.LoadAsync(alarmId, ct); - /// Loads all alarm condition states from the inner store. - /// A cancellation token. + /// public Task> LoadAllAsync(CancellationToken ct) => _inner.LoadAllAsync(ct); - /// Saves an alarm condition state, optionally throwing if FailSave is set. - /// The alarm condition state to save. - /// A cancellation token. + /// public Task SaveAsync(AlarmConditionState state, CancellationToken ct) { if (FailSave) throw new InvalidOperationException("Simulated store failure"); return _inner.SaveAsync(state, ct); } - /// Removes an alarm condition state by ID from the inner store. - /// The ID of the alarm condition state to remove. - /// A cancellation token. + /// public Task RemoveAsync(string alarmId, CancellationToken ct) => _inner.RemoveAsync(alarmId, ct); } @@ -946,20 +965,15 @@ public sealed class ScriptedAlarmEngineTests /// Gets a value indicating whether a SaveAsync call is currently blocked waiting on BlockNextSave. public bool SaveInProgress { get; private set; } - /// Loads an alarm condition state by ID from the inner store. - /// The ID of the alarm condition state to load. - /// A cancellation token. + /// public Task LoadAsync(string alarmId, CancellationToken ct) => _inner.LoadAsync(alarmId, ct); - /// Loads all alarm condition states from the inner store. - /// A cancellation token. + /// public Task> LoadAllAsync(CancellationToken ct) => _inner.LoadAllAsync(ct); - /// Saves an alarm condition state, optionally blocking on BlockNextSave gate. - /// The alarm condition state to save. - /// A cancellation token. + /// public async Task SaveAsync(AlarmConditionState state, CancellationToken ct) { var gate = BlockNextSave; @@ -973,9 +987,7 @@ public sealed class ScriptedAlarmEngineTests await _inner.SaveAsync(state, ct).ConfigureAwait(false); } - /// Removes an alarm condition state by ID from the inner store. - /// The ID of the alarm condition state to remove. - /// A cancellation token. + /// public Task RemoveAsync(string alarmId, CancellationToken ct) => _inner.RemoveAsync(alarmId, ct); } @@ -983,6 +995,7 @@ public sealed class ScriptedAlarmEngineTests // --- Core.ScriptedAlarms-009: per-alarm evaluation-scratch reuse --- /// Verifies that re-evaluations reuse the same read cache dictionary instance instead of allocating a new one. + /// A task that represents the asynchronous operation. [Fact] public async Task Reevaluation_reuses_the_same_read_cache_dictionary() { @@ -1016,6 +1029,7 @@ public sealed class ScriptedAlarmEngineTests } /// Verifies that re-evaluations reuse the same predicate context instance across evaluations. + /// A task that represents the asynchronous operation. [Fact] public async Task Reevaluation_reuses_the_same_predicate_context() { @@ -1042,6 +1056,7 @@ public sealed class ScriptedAlarmEngineTests } /// Verifies that LoadAsync clears prior evaluation scratch so new alarms use fresh scratch. + /// A task that represents the asynchronous operation. [Fact] public async Task LoadAsync_drops_the_prior_generations_scratch() { diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms.Tests/ScriptedAlarmSourceTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms.Tests/ScriptedAlarmSourceTests.cs index ce499c52..1db4c605 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms.Tests/ScriptedAlarmSourceTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms.Tests/ScriptedAlarmSourceTests.cs @@ -41,6 +41,7 @@ public sealed class ScriptedAlarmSourceTests } /// Verifies that subscribing with an empty filter receives every alarm emission. + /// A task that represents the asynchronous test. [Fact] public async Task Subscribe_with_empty_filter_receives_every_alarm_emission() { @@ -66,6 +67,7 @@ public sealed class ScriptedAlarmSourceTests } /// Verifies that subscribing with an equipment prefix filters alarms by that prefix. + /// A task that represents the asynchronous test. [Fact] public async Task Subscribe_with_equipment_prefix_filters_by_that_prefix() { @@ -89,6 +91,7 @@ public sealed class ScriptedAlarmSourceTests } /// Verifies that unsubscribing stops further alarm events. + /// A task that represents the asynchronous test. [Fact] public async Task Unsubscribe_stops_further_events() { @@ -108,6 +111,7 @@ public sealed class ScriptedAlarmSourceTests } /// Verifies that AcknowledgeAsync routes to the engine with a default user. + /// A task that represents the asynchronous test. [Fact] public async Task AcknowledgeAsync_routes_to_engine_with_default_user() { @@ -130,6 +134,7 @@ public sealed class ScriptedAlarmSourceTests } /// Verifies that null arguments are rejected. + /// A task that represents the asynchronous test. [Fact] public async Task Null_arguments_rejected() { diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Tests/CompiledScriptCacheTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Tests/CompiledScriptCacheTests.cs index 3841d6ff..ba67daea 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Tests/CompiledScriptCacheTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Tests/CompiledScriptCacheTests.cs @@ -66,6 +66,7 @@ public sealed class CompiledScriptCacheTests } /// Verifies that a cached evaluator produces correct results when executed. + /// A task that represents the asynchronous test. [Fact] public async Task Cached_evaluator_still_runs_correctly() { diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Tests/ScriptSandboxTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Tests/ScriptSandboxTests.cs index 7aecd405..654cd9d9 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Tests/ScriptSandboxTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Tests/ScriptSandboxTests.cs @@ -27,6 +27,7 @@ public sealed class ScriptSandboxTests } /// Verifies that a script can compile, run, and read a seeded tag. + /// A task that represents the asynchronous test operation. [Fact] public async Task Happy_path_script_runs_and_reads_seeded_tag() { @@ -39,6 +40,7 @@ public sealed class ScriptSandboxTests } /// Verifies that SetVirtualTag records write operations. + /// A task that represents the asynchronous test operation. [Fact] public async Task SetVirtualTag_records_the_write() { @@ -316,6 +318,7 @@ public sealed class ScriptSandboxTests } /// Verifies that an allowed generic type argument still compiles. + /// A task that represents the asynchronous test operation. [Fact] public async Task Allowed_generic_type_argument_still_compiles() { @@ -331,6 +334,7 @@ public sealed class ScriptSandboxTests } /// Verifies that typeof an allowed type still compiles. + /// A task that represents the asynchronous test operation. [Fact] public async Task Allowed_typeof_still_compiles() { @@ -342,6 +346,7 @@ public sealed class ScriptSandboxTests } /// Verifies that script exceptions propagate unwrapped. + /// A task that represents the asynchronous test operation. [Fact] public async Task Script_exception_propagates_unwrapped() { @@ -370,6 +375,7 @@ public sealed class ScriptSandboxTests } /// Verifies that LINQ Enumerable is available from scripts. + /// A task that represents the asynchronous test operation. [Fact] public async Task Linq_Enumerable_is_available_from_scripts() { @@ -385,6 +391,7 @@ public sealed class ScriptSandboxTests } /// Verifies that DataValueSnapshot is usable in scripts. + /// A task that represents the asynchronous test operation. [Fact] public async Task DataValueSnapshot_is_usable_in_scripts() { diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Tests/TimedScriptEvaluatorTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Tests/TimedScriptEvaluatorTests.cs index f9feeeac..f281fbf6 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Tests/TimedScriptEvaluatorTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Tests/TimedScriptEvaluatorTests.cs @@ -14,6 +14,7 @@ namespace ZB.MOM.WW.OtOpcUa.Core.Scripting.Tests; public sealed class TimedScriptEvaluatorTests { /// Verifies that fast scripts complete under timeout and return value. + /// A task that represents the asynchronous operation. [Fact] public async Task Fast_script_completes_under_timeout_and_returns_value() { @@ -28,6 +29,7 @@ public sealed class TimedScriptEvaluatorTests } /// Verifies that scripts longer than timeout throw ScriptTimeoutException. + /// A task that represents the asynchronous operation. [Fact] public async Task Script_longer_than_timeout_throws_ScriptTimeoutException() { @@ -50,6 +52,7 @@ public sealed class TimedScriptEvaluatorTests } /// Verifies that caller cancellation takes precedence over timeout. + /// A task that represents the asynchronous operation. [Fact] public async Task Caller_cancellation_takes_precedence_over_timeout() { @@ -108,6 +111,7 @@ public sealed class TimedScriptEvaluatorTests } /// Verifies that script exceptions propagate unwrapped. + /// A task that represents the asynchronous operation. [Fact] public async Task Script_exception_propagates_unwrapped() { @@ -124,6 +128,7 @@ public sealed class TimedScriptEvaluatorTests } /// Verifies that ScriptTimeoutException message points at diagnostic path. + /// A task that represents the asynchronous operation. [Fact] public async Task ScriptTimeoutException_message_points_at_diagnostic_path() { @@ -143,6 +148,7 @@ public sealed class TimedScriptEvaluatorTests } /// Verifies that caller cancellation wins even when timeout fires first. + /// A task that represents the asynchronous operation. [Fact] public async Task Caller_cancellation_wins_even_when_timeout_fires_first() { diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/DriverHostTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/DriverHostTests.cs index 1f6ff319..036aa3ee 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/DriverHostTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/DriverHostTests.cs @@ -10,10 +10,10 @@ public sealed class DriverHostTests { private sealed class StubDriver(string id, bool failInit = false) : IDriver { - /// Gets the driver instance identifier. + /// public string DriverInstanceId { get; } = id; - /// Gets the driver type name. + /// public string DriverType => "Stub"; /// Gets a value indicating whether the driver has been initialized. @@ -22,9 +22,7 @@ public sealed class DriverHostTests /// Gets a value indicating whether the driver has been shut down. public bool ShutDown { get; private set; } - /// Initializes the driver asynchronously. - /// Configuration data (unused in stub). - /// The cancellation token. + /// public Task InitializeAsync(string _, CancellationToken ct) { if (failInit) throw new InvalidOperationException("boom"); @@ -32,28 +30,25 @@ public sealed class DriverHostTests return Task.CompletedTask; } - /// Reinitializes the driver asynchronously. - /// Configuration data (unused in stub). - /// The cancellation token. + /// public Task ReinitializeAsync(string _, CancellationToken ct) => Task.CompletedTask; - /// Shuts down the driver asynchronously. - /// The cancellation token. + /// public Task ShutdownAsync(CancellationToken ct) { ShutDown = true; return Task.CompletedTask; } - /// Gets the current health status of the driver. + /// public DriverHealth GetHealth() => new(Initialized ? DriverState.Healthy : DriverState.Unknown, null, null); - /// Gets the memory footprint of the driver. + /// public long GetMemoryFootprint() => 0; - /// Flushes optional caches asynchronously. - /// The cancellation token. + /// public Task FlushOptionalCachesAsync(CancellationToken ct) => Task.CompletedTask; } /// Verifies that registering a driver initializes it and tracks its health. + /// A task that represents the asynchronous test operation. [Fact] public async Task Register_initializes_driver_and_tracks_health() { @@ -68,6 +63,7 @@ public sealed class DriverHostTests } /// Verifies that registration rethrows initialization failures but keeps the driver registered. + /// A task that represents the asynchronous test operation. [Fact] public async Task Register_rethrows_init_failure_but_keeps_driver_registered() { @@ -81,6 +77,7 @@ public sealed class DriverHostTests } /// Verifies that duplicate driver registration throws an exception. + /// A task that represents the asynchronous test operation. [Fact] public async Task Duplicate_registration_throws() { @@ -92,6 +89,7 @@ public sealed class DriverHostTests } /// Verifies that unregistering a driver shuts it down and removes it. + /// A task that represents the asynchronous test operation. [Fact] public async Task Unregister_shuts_down_and_removes() { @@ -113,6 +111,7 @@ public sealed class DriverHostTests /// The driver awaits an unsettled TaskCompletionSource so it does not introduce its /// own capture — only DriverHost's await of the returned Task can drive a post. /// + /// A task that represents the asynchronous test operation. [Fact] public async Task RegisterAsync_Does_Not_Capture_SynchronizationContext() { @@ -137,6 +136,7 @@ public sealed class DriverHostTests } /// Verifies that UnregisterAsync does not capture the synchronization context. + /// A task that represents the asynchronous test operation. [Fact] public async Task UnregisterAsync_Does_Not_Capture_SynchronizationContext() { @@ -165,6 +165,7 @@ public sealed class DriverHostTests } /// Verifies that DisposeAsync does not capture the synchronization context. + /// A task that represents the asynchronous test operation. [Fact] public async Task DisposeAsync_Does_Not_Capture_SynchronizationContext() { @@ -225,34 +226,28 @@ public sealed class DriverHostTests /// Driver whose Initialize / Shutdown completions are caller-controlled via TCS. private sealed class TcsDriver(string id, TaskCompletionSource initTcs, TaskCompletionSource? shutdownTcs = null) : IDriver { - /// Gets the driver instance identifier. + /// public string DriverInstanceId { get; } = id; - /// Gets the driver type name. + /// public string DriverType => "Tcs"; - /// Initializes the driver asynchronously. - /// Configuration data (unused in TCS driver). - /// The cancellation token. + /// public Task InitializeAsync(string _, CancellationToken ct) => initTcs.Task; - /// Reinitializes the driver asynchronously. - /// Configuration data (unused in TCS driver). - /// The cancellation token. + /// public Task ReinitializeAsync(string _, CancellationToken ct) => Task.CompletedTask; - /// Shuts down the driver asynchronously. - /// The cancellation token. + /// public Task ShutdownAsync(CancellationToken ct) => (shutdownTcs ?? CompletedTcs).Task; - /// Gets the current health status of the driver. + /// public DriverHealth GetHealth() => new(DriverState.Healthy, null, null); - /// Gets the memory footprint of the driver. + /// public long GetMemoryFootprint() => 0; - /// Flushes optional caches asynchronously. - /// The cancellation token. + /// public Task FlushOptionalCachesAsync(CancellationToken ct) => Task.CompletedTask; private static readonly TaskCompletionSource CompletedTcs = MakeCompleted(); @@ -271,7 +266,6 @@ public sealed class DriverHostTests public int PostCount; public int SendCount; - /// Posts a callback to the work queue. /// public override void Post(SendOrPostCallback d, object? state) { @@ -279,7 +273,6 @@ public sealed class DriverHostTests _queue.Enqueue(() => d(state)); } - /// Sends a callback synchronously. /// public override void Send(SendOrPostCallback d, object? state) { diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/GenericDriverNodeManagerTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/GenericDriverNodeManagerTests.cs index f2d71510..05d6cce4 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/GenericDriverNodeManagerTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/GenericDriverNodeManagerTests.cs @@ -15,6 +15,7 @@ public sealed class GenericDriverNodeManagerTests /// This is the plumbing that PR 16's concrete OPC UA builder will use to update the actual /// AlarmConditionState nodes. /// + /// A task that represents the asynchronous test operation. [Fact] public async Task Alarm_events_are_routed_to_the_sink_registered_for_the_matching_source_node_id() { @@ -45,6 +46,7 @@ public sealed class GenericDriverNodeManagerTests } /// Verifies that non-alarm variables do not register sinks in the alarm tracker. + /// A task that represents the asynchronous test operation. [Fact] public async Task Non_alarm_variables_do_not_register_sinks() { @@ -59,6 +61,7 @@ public sealed class GenericDriverNodeManagerTests } /// Verifies that alarm events with unknown source node IDs are silently dropped. + /// A task that represents the asynchronous test operation. [Fact] public async Task Unknown_source_node_id_is_dropped_silently() { @@ -74,6 +77,7 @@ public sealed class GenericDriverNodeManagerTests } /// Verifies that disposing the node manager unsubscribes from alarm events. + /// A task that represents the asynchronous test operation. [Fact] public async Task Dispose_unsubscribes_from_OnAlarmEvent() { @@ -96,6 +100,7 @@ public sealed class GenericDriverNodeManagerTests /// must unsubscribe the old alarm forwarder and clear the sink registry before re-walking, /// so alarm transitions are not delivered twice. /// + /// A task that represents the asynchronous test operation. [Fact] public async Task Second_BuildAddressSpaceAsync_Does_Not_Double_Fire_Alarms() { @@ -121,6 +126,7 @@ public sealed class GenericDriverNodeManagerTests } /// Verifies that a second call to BuildAddressSpaceAsync clears the old sink registry. + /// A task that represents the asynchronous test operation. [Fact] public async Task Second_BuildAddressSpaceAsync_Clears_Old_Sink_Registry() { @@ -137,6 +143,7 @@ public sealed class GenericDriverNodeManagerTests } /// Verifies that calling BuildAddressSpaceAsync after disposal throws ObjectDisposedException. + /// A task that represents the asynchronous test operation. [Fact] public async Task BuildAddressSpaceAsync_After_Dispose_Throws_ObjectDisposedException() { @@ -154,6 +161,7 @@ public sealed class GenericDriverNodeManagerTests /// out of BuildAddressSpaceAsync unhandled so the Server layer's per-driver try/catch /// (OpcUaApplicationHost.PopulateAddressSpaces) can mark the subtree Faulted. /// + /// A task that represents the asynchronous test operation. [Fact] public async Task BuildAddressSpaceAsync_Propagates_Discovery_Exceptions_To_Caller() { @@ -169,33 +177,25 @@ public sealed class GenericDriverNodeManagerTests /// Driver whose DiscoverAsync throws — exercises the exception-isolation boundary. private sealed class ThrowingDiscoveryDriver : IDriver, ITagDiscovery { - /// Gets the driver instance identifier. + /// public string DriverInstanceId => "throwing"; - /// Gets the driver type name. + /// public string DriverType => "Throwing"; - /// Initializes the driver with configuration. - /// Configuration JSON (unused in test double). - /// Cancellation token (unused in test double). + /// public Task InitializeAsync(string _, CancellationToken __) => Task.CompletedTask; - /// Reinitializes the driver with new configuration. - /// Configuration JSON (unused in test double). - /// Cancellation token (unused in test double). + /// public Task ReinitializeAsync(string _, CancellationToken __) => Task.CompletedTask; - /// Shuts down the driver. - /// Cancellation token (unused in test double). + /// public Task ShutdownAsync(CancellationToken _) => Task.CompletedTask; - /// Gets the current health status of the driver. + /// public DriverHealth GetHealth() => new(DriverState.Healthy, null, null); - /// Gets the memory footprint of the driver. + /// public long GetMemoryFootprint() => 0; - /// Flushes optional caches in the driver. - /// Cancellation token (unused in test double). + /// public Task FlushOptionalCachesAsync(CancellationToken _) => Task.CompletedTask; - /// Discovers the address space by throwing an exception. - /// The builder used to construct the address space. - /// Cancellation token. + /// public Task DiscoverAsync(IAddressSpaceBuilder builder, CancellationToken ct) => throw new InvalidOperationException("discovery boom"); } @@ -204,35 +204,27 @@ public sealed class GenericDriverNodeManagerTests private sealed class FakeDriver : IDriver, ITagDiscovery, IAlarmSource { - /// Gets the driver instance identifier. + /// public string DriverInstanceId => "fake"; - /// Gets the driver type name. + /// public string DriverType => "Fake"; /// Occurs when an alarm event is raised. public event EventHandler? OnAlarmEvent; - /// Initializes the driver with configuration. - /// Configuration JSON. - /// Cancellation token. + /// public Task InitializeAsync(string driverConfigJson, CancellationToken ct) => Task.CompletedTask; - /// Reinitializes the driver with new configuration. - /// Configuration JSON. - /// Cancellation token. + /// public Task ReinitializeAsync(string driverConfigJson, CancellationToken ct) => Task.CompletedTask; - /// Shuts down the driver. - /// Cancellation token. + /// public Task ShutdownAsync(CancellationToken ct) => Task.CompletedTask; - /// Gets the current health status of the driver. + /// public DriverHealth GetHealth() => new(DriverState.Healthy, DateTime.UtcNow, null); - /// Gets the memory footprint of the driver. + /// public long GetMemoryFootprint() => 0; - /// Flushes optional caches in the driver. - /// Cancellation token. + /// public Task FlushOptionalCachesAsync(CancellationToken ct) => Task.CompletedTask; - /// Discovers the address space and registers alarm conditions. - /// The builder used to construct the address space. - /// Cancellation token. + /// public Task DiscoverAsync(IAddressSpaceBuilder builder, CancellationToken ct) { var folder = builder.Folder("Tank", "Tank"); @@ -253,25 +245,19 @@ public sealed class GenericDriverNodeManagerTests /// The alarm event arguments. public void RaiseAlarm(AlarmEventArgs args) => OnAlarmEvent?.Invoke(this, args); - /// Subscribes to alarm events. - /// Tag references to subscribe to (unused in test double). - /// Cancellation token (unused in test double). + /// public Task SubscribeAlarmsAsync(IReadOnlyList _, CancellationToken __) => Task.FromResult(new FakeHandle("sub")); - /// Unsubscribes from alarm events. - /// The subscription handle (unused in test double). - /// Cancellation token (unused in test double). + /// public Task UnsubscribeAlarmsAsync(IAlarmSubscriptionHandle _, CancellationToken __) => Task.CompletedTask; - /// Acknowledges alarm notifications. - /// Alarm acknowledgement requests (unused in test double). - /// Cancellation token (unused in test double). + /// public Task AcknowledgeAsync(IReadOnlyList _, CancellationToken __) => Task.CompletedTask; } /// Test double for IAlarmSubscriptionHandle. private sealed class FakeHandle(string diagnosticId) : IAlarmSubscriptionHandle { - /// Gets the diagnostic identifier for this subscription. + /// public string DiagnosticId { get; } = diagnosticId; } @@ -281,31 +267,22 @@ public sealed class GenericDriverNodeManagerTests /// Gets the map of alarm sources to their sinks. public Dictionary Alarms { get; } = new(StringComparer.OrdinalIgnoreCase); - /// Creates a folder in the address space. - /// The contained name (unused in test double). - /// The display name (unused in test double). + /// public IAddressSpaceBuilder Folder(string _, string __) => this; - /// Creates a variable in the address space. - /// The contained name (unused in test double). - /// The display name (unused in test double). - /// The driver attribute information. + /// public IVariableHandle Variable(string _, string __, DriverAttributeInfo info) => new Handle(info.FullName, Alarms); - /// Adds a property to the current variable. - /// The property name (unused in test double). - /// The data type (unused in test double). - /// The initial value (unused in test double). + /// public void AddProperty(string _, DriverDataType __, object? ___) { } /// Test double for IVariableHandle. public sealed class Handle(string fullRef, Dictionary alarms) : IVariableHandle { - /// Gets the full reference name for this variable. + /// public string FullReference { get; } = fullRef; - /// Marks this variable as an alarm condition and registers its sink. - /// The alarm condition info (unused in test double). + /// public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo _) { var sink = new RecordingSink(); @@ -319,8 +296,7 @@ public sealed class GenericDriverNodeManagerTests { /// Gets the list of alarm transitions received by this sink. public List Received { get; } = new(); - /// Records an alarm transition. - /// The alarm event arguments. + /// public void OnTransition(AlarmEventArgs args) => Received.Add(args); } } diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Observability/CapabilityInvokerEnrichmentTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Observability/CapabilityInvokerEnrichmentTests.cs index a6870799..4b74006a 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Observability/CapabilityInvokerEnrichmentTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Observability/CapabilityInvokerEnrichmentTests.cs @@ -12,6 +12,7 @@ namespace ZB.MOM.WW.OtOpcUa.Core.Tests.Observability; public sealed class CapabilityInvokerEnrichmentTests { /// Verifies that InvokerExecute logs inside call site with structured properties. + /// A task that represents the asynchronous test. [Fact] public async Task InvokerExecute_LogsInsideCallSite_CarryStructuredProperties() { @@ -45,6 +46,7 @@ public sealed class CapabilityInvokerEnrichmentTests } /// Verifies that InvokerExecute does not leak context outside the call site. + /// A task that represents the asynchronous test. [Fact] public async Task InvokerExecute_DoesNotLeak_ContextOutsideCallSite() { diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/OpcUa/EquipmentNodeWalkerTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/OpcUa/EquipmentNodeWalkerTests.cs index 1b22a9d7..0ae74a9e 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/OpcUa/EquipmentNodeWalkerTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/OpcUa/EquipmentNodeWalkerTests.cs @@ -377,9 +377,7 @@ public sealed class EquipmentNodeWalkerTests /// Gets the list of properties. public List Properties { get; } = new(); - /// Creates a folder child node. - /// The browse name of the folder. - /// The display name (unused). + /// public IAddressSpaceBuilder Folder(string name, string _) { var child = new RecordingBuilder(name); @@ -387,10 +385,7 @@ public sealed class EquipmentNodeWalkerTests return child; } - /// Creates a variable node. - /// The browse name of the variable. - /// The display name (unused). - /// The attribute information for the variable. + /// public IVariableHandle Variable(string name, string _, DriverAttributeInfo attr) { var v = new RecordingVariable(name, attr); @@ -398,10 +393,7 @@ public sealed class EquipmentNodeWalkerTests return v; } - /// Adds a property to the node. - /// The browse name of the property. - /// The data type (unused). - /// The value of the property. + /// public void AddProperty(string name, DriverDataType _, object? value) => Properties.Add(new RecordingProperty(name, value)); } @@ -412,10 +404,9 @@ public sealed class EquipmentNodeWalkerTests /// Recorded variable for test verification. private sealed record RecordingVariable(string BrowseName, DriverAttributeInfo AttributeInfo) : IVariableHandle { - /// Gets the full reference of the variable. + /// public string FullReference => AttributeInfo.FullName; - /// Marks the variable as an alarm condition. - /// The alarm condition information. + /// public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) => throw new NotSupportedException(); } } diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/OpcUa/IdentificationFolderBuilderTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/OpcUa/IdentificationFolderBuilderTests.cs index a997d70d..2c29285b 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/OpcUa/IdentificationFolderBuilderTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/OpcUa/IdentificationFolderBuilderTests.cs @@ -18,26 +18,18 @@ public sealed class IdentificationFolderBuilderTests /// Gets or sets the list of added properties. public List<(string BrowseName, DriverDataType DataType, object? Value)> Properties { get; } = []; - /// Records a folder and returns this builder for chaining. - /// The browse name of the folder. - /// The display name of the folder. + /// public IAddressSpaceBuilder Folder(string browseName, string displayName) { Folders.Add((browseName, displayName)); return this; // flat recording — identification fields land in the same bucket } - /// Not supported in test context. - /// The browse name of the variable. - /// The display name of the variable. - /// The attribute information. + /// public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo attributeInfo) => throw new NotSupportedException("Identification fields use AddProperty, not Variable"); - /// Records a property addition. - /// The browse name of the property. - /// The data type of the property. - /// The property value. + /// public void AddProperty(string browseName, DriverDataType dataType, object? value) => Properties.Add((browseName, dataType, value)); } diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/AlarmSurfaceInvokerTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/AlarmSurfaceInvokerTests.cs index 9a883afb..41ff5998 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/AlarmSurfaceInvokerTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/AlarmSurfaceInvokerTests.cs @@ -11,6 +11,7 @@ public sealed class AlarmSurfaceInvokerTests private static readonly DriverResilienceOptions TierAOptions = new() { Tier = DriverTier.A }; /// Verifies SubscribeAsync on an empty list returns empty without calling the driver. + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeAsync_EmptyList_ReturnsEmpty_WithoutDriverCall() { @@ -24,6 +25,7 @@ public sealed class AlarmSurfaceInvokerTests } /// Verifies SubscribeAsync with no resolver routes through the default host. + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeAsync_SingleHost_RoutesThroughDefaultHost() { @@ -38,6 +40,7 @@ public sealed class AlarmSurfaceInvokerTests } /// Verifies SubscribeAsync fans out correctly to multiple hosts based on resolver. + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeAsync_MultiHost_FansOutByResolvedHost() { @@ -57,6 +60,7 @@ public sealed class AlarmSurfaceInvokerTests } /// Verifies AcknowledgeAsync does not retry on failure. + /// A task that represents the asynchronous operation. [Fact] public async Task AcknowledgeAsync_DoesNotRetry_OnFailure() { @@ -70,6 +74,7 @@ public sealed class AlarmSurfaceInvokerTests } /// Verifies SubscribeAsync retries on transient failures. + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeAsync_Retries_Transient_Failures() { @@ -87,6 +92,7 @@ public sealed class AlarmSurfaceInvokerTests /// Verify by using a per-call resolver with two distinct hosts and checking which host /// name reaches the driver's UnsubscribeAlarmsAsync. /// + /// A task that represents the asynchronous operation. [Fact] public async Task UnsubscribeAsync_Routes_Through_Same_Host_As_Subscribe() { @@ -112,6 +118,7 @@ public sealed class AlarmSurfaceInvokerTests } /// Verifies UnsubscribeAsync with no resolver uses the default host. + /// A task that represents the asynchronous operation. [Fact] public async Task UnsubscribeAsync_SingleHost_UsesDefaultHost() { @@ -158,10 +165,7 @@ public sealed class AlarmSurfaceInvokerTests /// Gets the source node IDs from the most recent SubscribeAlarmsAsync call. public IReadOnlyList LastSubscribedIds { get; private set; } = []; - /// Subscribes to alarms. - /// The source node IDs to subscribe to. - /// Cancellation token. - /// An alarm subscription handle. + /// public Task SubscribeAlarmsAsync( IReadOnlyList sourceNodeIds, CancellationToken cancellationToken) { @@ -172,20 +176,14 @@ public sealed class AlarmSurfaceInvokerTests return Task.FromResult(new StubHandle($"h-{SubscribeCallCount}")); } - /// Unsubscribes from alarms. - /// The subscription handle to unsubscribe. - /// Cancellation token. - /// A completed task. + /// public Task UnsubscribeAlarmsAsync(IAlarmSubscriptionHandle handle, CancellationToken cancellationToken) { UnsubscribeCallCount++; return Task.CompletedTask; } - /// Acknowledges alarms. - /// The alarm acknowledgements to process. - /// Cancellation token. - /// A completed task. + /// public Task AcknowledgeAsync( IReadOnlyList acknowledgements, CancellationToken cancellationToken) { @@ -194,7 +192,7 @@ public sealed class AlarmSurfaceInvokerTests return Task.CompletedTask; } - /// Occurs when an alarm event is raised. + /// public event EventHandler? OnAlarmEvent { add { } remove { } } } @@ -206,9 +204,7 @@ public sealed class AlarmSurfaceInvokerTests /// The map of source node IDs to host names. private sealed class StubResolver(Dictionary map) : IPerCallHostResolver { - /// Resolves the host for the given full reference. - /// The full reference to resolve. - /// The resolved host name. + /// public string ResolveHost(string fullReference) => map[fullReference]; } } diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/CapabilityInvokerTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/CapabilityInvokerTests.cs index 277a2307..3b5a9bb3 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/CapabilityInvokerTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/CapabilityInvokerTests.cs @@ -14,6 +14,7 @@ public sealed class CapabilityInvokerTests new(builder, "drv-test", () => options); /// Verifies that the capability invoker returns the value from the call site. + /// A task that represents the asynchronous test operation. [Fact] public async Task Read_ReturnsValue_FromCallSite() { @@ -29,6 +30,7 @@ public sealed class CapabilityInvokerTests } /// Verifies that the capability invoker retries on transient failures. + /// A task that represents the asynchronous test operation. [Fact] public async Task Read_Retries_OnTransientFailure() { @@ -52,6 +54,7 @@ public sealed class CapabilityInvokerTests } /// Verifies that non-idempotent writes do not retry even when the policy has retries configured. + /// A task that represents the asynchronous test operation. [Fact] public async Task Write_NonIdempotent_DoesNotRetry_EvenWhenPolicyHasRetries() { @@ -85,6 +88,7 @@ public sealed class CapabilityInvokerTests } /// Verifies that idempotent writes retry when the policy has retries configured. + /// A task that represents the asynchronous test operation. [Fact] public async Task Write_Idempotent_Retries_WhenPolicyHasRetries() { @@ -116,6 +120,7 @@ public sealed class CapabilityInvokerTests } /// Verifies that writes do not retry when the policy has zero retries configured. + /// A task that represents the asynchronous test operation. [Fact] public async Task Write_Default_DoesNotRetry_WhenPolicyHasZeroRetries() { @@ -143,6 +148,7 @@ public sealed class CapabilityInvokerTests } /// Verifies that different hosts are honored independently in the resilience pipeline. + /// A task that represents the asynchronous test operation. [Fact] public async Task Execute_HonorsDifferentHosts_Independently() { @@ -161,6 +167,7 @@ public sealed class CapabilityInvokerTests /// redundant options objects on the per-write hot path and creates a consistency hazard /// where an Admin edit mid-call could observe two different snapshots. /// + /// A task that represents the asynchronous test operation. [Fact] public async Task ExecuteWriteAsync_NonIdempotent_Snapshots_Options_Once_Per_Call() { @@ -195,6 +202,7 @@ public sealed class CapabilityInvokerTests /// two derived values (with base + Resolve(Write)) come from the same options /// instance. /// + /// A task that represents the asynchronous test operation. [Fact] public async Task ExecuteWriteAsync_NonIdempotent_Uses_Consistent_Options_Snapshot() { diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/DriverResiliencePipelineBuilderTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/DriverResiliencePipelineBuilderTests.cs index 2a78f3b8..4d64d5e6 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/DriverResiliencePipelineBuilderTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/DriverResiliencePipelineBuilderTests.cs @@ -13,6 +13,7 @@ public sealed class DriverResiliencePipelineBuilderTests private static readonly DriverResilienceOptions TierAOptions = new() { Tier = DriverTier.A }; /// Verifies that read operations retry transient failures. + /// A task that represents the asynchronous test operation. [Fact] public async Task Read_Retries_Transient_Failures() { @@ -31,6 +32,7 @@ public sealed class DriverResiliencePipelineBuilderTests } /// Verifies that write operations do not retry on failure. + /// A task that represents the asynchronous test operation. [Fact] public async Task Write_DoesNotRetry_OnFailure() { @@ -53,6 +55,7 @@ public sealed class DriverResiliencePipelineBuilderTests } /// Verifies that alarm acknowledge operations do not retry on failure. + /// A task that represents the asynchronous test operation. [Fact] public async Task AlarmAcknowledge_DoesNotRetry_OnFailure() { @@ -115,6 +118,7 @@ public sealed class DriverResiliencePipelineBuilderTests } /// Verifies that a dead host does not open the breaker for a sibling host. + /// A task that represents the asynchronous test operation. [Fact] public async Task DeadHost_DoesNotOpenBreaker_ForSiblingHost() { @@ -146,6 +150,7 @@ public sealed class DriverResiliencePipelineBuilderTests } /// Verifies that the circuit breaker opens after the failure threshold on tier A. + /// A task that represents the asynchronous test operation. [Fact] public async Task CircuitBreaker_Opens_AfterFailureThreshold_OnTierA() { @@ -171,6 +176,7 @@ public sealed class DriverResiliencePipelineBuilderTests } /// Verifies that timeout cancels slow operations. + /// A task that represents the asynchronous test operation. [Fact] public async Task Timeout_Cancels_SlowOperation() { @@ -211,6 +217,7 @@ public sealed class DriverResiliencePipelineBuilderTests } /// Verifies that cancellation is not retried. + /// A task that represents the asynchronous test operation. [Fact] public async Task Cancellation_IsNot_Retried() { @@ -232,6 +239,7 @@ public sealed class DriverResiliencePipelineBuilderTests } /// Verifies that the tracker records failure on every retry. + /// A task that represents the asynchronous test operation. [Fact] public async Task Tracker_RecordsFailure_OnEveryRetry() { @@ -253,6 +261,7 @@ public sealed class DriverResiliencePipelineBuilderTests } /// Verifies that the tracker stamps the breaker open when it trips. + /// A task that represents the asynchronous test operation. [Fact] public async Task Tracker_StampsBreakerOpen_WhenBreakerTrips() { @@ -277,6 +286,7 @@ public sealed class DriverResiliencePipelineBuilderTests } /// Verifies that the tracker isolates counters per host. + /// A task that represents the asynchronous test operation. [Fact] public async Task Tracker_IsolatesCounters_PerHost() { diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/FlakeyDriverIntegrationTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/FlakeyDriverIntegrationTests.cs index 03433d32..db817511 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/FlakeyDriverIntegrationTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/FlakeyDriverIntegrationTests.cs @@ -15,6 +15,7 @@ namespace ZB.MOM.WW.OtOpcUa.Core.Tests.Resilience; public sealed class FlakeyDriverIntegrationTests { /// Verifies read succeeds after transient failures with retries. + /// A task that represents the asynchronous test operation. [Fact] public async Task Read_SurfacesSuccess_AfterTransientFailures() { @@ -43,6 +44,7 @@ public sealed class FlakeyDriverIntegrationTests } /// Verifies non-idempotent write fails on first failure without replay. + /// A task that represents the asynchronous test operation. [Fact] public async Task Write_NonIdempotent_FailsOnFirstFailure_NoReplay() { @@ -68,6 +70,7 @@ public sealed class FlakeyDriverIntegrationTests } /// Verifies idempotent write retries until success. + /// A task that represents the asynchronous test operation. [Fact] public async Task Write_Idempotent_RetriesUntilSuccess() { @@ -93,6 +96,7 @@ public sealed class FlakeyDriverIntegrationTests } /// Verifies multiple hosts have independent failure counts and circuit breakers. + /// A task that represents the asynchronous test operation. [Fact] public async Task MultipleHosts_OnOneDriver_HaveIndependentFailureCounts() { @@ -141,10 +145,7 @@ public sealed class FlakeyDriverIntegrationTests _failWritesBeforeIndex = failWritesBeforeIndex; } - /// Reads values, failing transiently until the threshold. - /// Full references to read. - /// Cancellation token. - /// Data value snapshots. + /// public Task> ReadAsync( IReadOnlyList fullReferences, CancellationToken cancellationToken) @@ -160,10 +161,7 @@ public sealed class FlakeyDriverIntegrationTests return Task.FromResult(result); } - /// Writes values, failing transiently until the threshold. - /// The write requests. - /// Cancellation token. - /// Write results. + /// public Task> WriteAsync( IReadOnlyList writes, CancellationToken cancellationToken) diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/InFlightCounterTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/InFlightCounterTests.cs index 20c288e4..295d5534 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/InFlightCounterTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/InFlightCounterTests.cs @@ -69,6 +69,7 @@ public sealed class InFlightCounterTests } /// Verifies that CapabilityInvoker increments the tracker during execution. + /// A task that represents the asynchronous test operation. [Fact] public async Task CapabilityInvoker_IncrementsTracker_DuringExecution() { @@ -97,6 +98,7 @@ public sealed class InFlightCounterTests } /// Verifies that CapabilityInvoker decrements the counter on exception. + /// A task that represents the asynchronous test operation. [Fact] public async Task CapabilityInvoker_ExceptionPath_DecrementsCounter() { @@ -119,6 +121,7 @@ public sealed class InFlightCounterTests } /// Verifies that CapabilityInvoker without a tracker does not throw. + /// A task that represents the asynchronous test operation. [Fact] public async Task CapabilityInvoker_WithoutTracker_DoesNotThrow() { diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/PerCallHostResolverDispatchTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/PerCallHostResolverDispatchTests.cs index d1acd6d2..b8cda91a 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/PerCallHostResolverDispatchTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Resilience/PerCallHostResolverDispatchTests.cs @@ -22,13 +22,13 @@ public sealed class PerCallHostResolverDispatchTests /// The mapping of full references to host names. public StaticResolver(Dictionary map) => _map = map; - /// Resolves a host name from the static mapping. - /// The full reference to resolve. + /// public string ResolveHost(string fullReference) => _map.TryGetValue(fullReference, out var host) ? host : string.Empty; } /// Verifies that a dead PLC does not open the breaker for healthy PLCs when using a per-call resolver. + /// A task that represents the asynchronous test operation. [Fact] public async Task DeadPlc_DoesNotOpenBreaker_For_HealthyPlc_With_Resolver() { @@ -80,6 +80,7 @@ public sealed class PerCallHostResolverDispatchTests } /// Verifies that without a resolver, the same host shares one resilience pipeline. + /// A task that represents the asynchronous test operation. [Fact] public async Task WithoutResolver_SameHost_Shares_One_Pipeline() { @@ -98,6 +99,7 @@ public sealed class PerCallHostResolverDispatchTests } /// Verifies that with a resolver, different hosts get separate resilience pipelines. + /// A task that represents the asynchronous test operation. [Fact] public async Task WithResolver_TwoHosts_Get_Two_Pipelines() { diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Stability/MemoryRecycleTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Stability/MemoryRecycleTests.cs index 6f17ea9c..dec3e187 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Stability/MemoryRecycleTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Stability/MemoryRecycleTests.cs @@ -10,6 +10,7 @@ namespace ZB.MOM.WW.OtOpcUa.Core.Tests.Stability; public sealed class MemoryRecycleTests { /// Verifies that Tier C hard memory breach requests supervisor recycle. + /// A task that represents the asynchronous test operation. [Fact] public async Task TierC_HardBreach_RequestsSupervisorRecycle() { @@ -25,6 +26,7 @@ public sealed class MemoryRecycleTests /// Verifies that Tier A and B hard memory breach never request recycle. /// The driver tier to test. + /// A task that represents the asynchronous test operation. [Theory] [InlineData(DriverTier.A)] [InlineData(DriverTier.B)] @@ -40,6 +42,7 @@ public sealed class MemoryRecycleTests } /// Verifies that Tier C without supervisor hard breach is a no-op. + /// A task that represents the asynchronous test operation. [Fact] public async Task TierC_WithoutSupervisor_HardBreach_NoOp() { @@ -52,6 +55,7 @@ public sealed class MemoryRecycleTests /// Verifies that soft memory breach never requests recycle at any tier. /// The driver tier to test. + /// A task that represents the asynchronous test operation. [Theory] [InlineData(DriverTier.A)] [InlineData(DriverTier.B)] @@ -69,6 +73,7 @@ public sealed class MemoryRecycleTests /// Verifies that non-breach memory actions are no-ops. /// The non-breach memory tracking action to test. + /// A task that represents the asynchronous test operation. [Theory] [InlineData(MemoryTrackingAction.None)] [InlineData(MemoryTrackingAction.Warming)] @@ -85,16 +90,14 @@ public sealed class MemoryRecycleTests private sealed class FakeSupervisor : IDriverSupervisor { - /// Gets the driver instance identifier. + /// public string DriverInstanceId => "fake-tier-c"; /// Gets the count of recycle operations. public int RecycleCount { get; private set; } /// Gets the reason from the last recycle operation. public string? LastReason { get; private set; } - /// Recycles the driver asynchronously. - /// The reason for recycling. - /// The cancellation token. + /// public Task RecycleAsync(string reason, CancellationToken cancellationToken) { RecycleCount++; diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Stability/ScheduledRecycleSchedulerTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Stability/ScheduledRecycleSchedulerTests.cs index 0e4f3f83..fbcb977d 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Stability/ScheduledRecycleSchedulerTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Stability/ScheduledRecycleSchedulerTests.cs @@ -36,6 +36,7 @@ public sealed class ScheduledRecycleSchedulerTests } /// Verifies Tick before the next recycle time is a no-op. + /// A task that represents the asynchronous operation. [Fact] public async Task Tick_BeforeNextRecycle_NoOp() { @@ -49,6 +50,7 @@ public sealed class ScheduledRecycleSchedulerTests } /// Verifies Tick at or after the next recycle time fires once and advances. + /// A task that represents the asynchronous operation. [Fact] public async Task Tick_AtOrAfterNextRecycle_FiresOnce_AndAdvances() { @@ -63,6 +65,7 @@ public sealed class ScheduledRecycleSchedulerTests } /// Verifies RequestRecycleNow fires immediately without advancing the schedule. + /// A task that represents the asynchronous operation. [Fact] public async Task RequestRecycleNow_Fires_Immediately_WithoutAdvancingSchedule() { @@ -78,6 +81,7 @@ public sealed class ScheduledRecycleSchedulerTests } /// Verifies multiple ticks across the recycle interval each advance by one interval. + /// A task that represents the asynchronous operation. [Fact] public async Task MultipleFires_AcrossTicks_AdvanceOneIntervalEach() { @@ -95,7 +99,7 @@ public sealed class ScheduledRecycleSchedulerTests /// Fake driver supervisor for testing. private sealed class FakeSupervisor : IDriverSupervisor { - /// Gets the driver instance ID. + /// public string DriverInstanceId => "tier-c-fake"; /// Gets the number of times RecycleAsync was called. @@ -104,10 +108,7 @@ public sealed class ScheduledRecycleSchedulerTests /// Gets the reason from the most recent recycle call. public string? LastReason { get; private set; } - /// Simulates a driver recycle operation. - /// The reason for the recycle. - /// Cancellation token. - /// A completed task. + /// public Task RecycleAsync(string reason, CancellationToken cancellationToken) { RecycleCount++; diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.Tests/FakeUpstream.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.Tests/FakeUpstream.cs index b19aea0d..8529e678 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.Tests/FakeUpstream.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.Tests/FakeUpstream.cs @@ -42,16 +42,13 @@ public sealed class FakeUpstream : ITagUpstreamSource } } - /// Reads the current value of a tag. - /// The path to the tag. + /// public DataValueSnapshot ReadTag(string path) => _values.TryGetValue(path, out var v) ? v : new DataValueSnapshot(null, 0x80340000u, null, DateTime.UtcNow); - /// Subscribes to tag value changes. - /// The path to the tag. - /// The callback to invoke when the tag value changes. + /// public IDisposable SubscribeTag(string path, Action observer) { var list = _subs.GetOrAdd(path, _ => []); diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.Tests/TimerTriggerSchedulerTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.Tests/TimerTriggerSchedulerTests.cs index 40323374..ee2a21fb 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.Tests/TimerTriggerSchedulerTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.Tests/TimerTriggerSchedulerTests.cs @@ -11,6 +11,7 @@ namespace ZB.MOM.WW.OtOpcUa.Core.VirtualTags.Tests; public sealed class TimerTriggerSchedulerTests { /// Verifies that timer interval causes periodic reevaluation of virtual tags. + /// A task that represents the asynchronous operation. [Fact] public async Task Timer_interval_causes_periodic_reevaluation() { @@ -46,6 +47,7 @@ public sealed class TimerTriggerSchedulerTests } /// Verifies that tags without TimerInterval are not scheduled. + /// A task that represents the asynchronous operation. [Fact] public async Task Tags_without_TimerInterval_not_scheduled() { @@ -98,6 +100,7 @@ public sealed class TimerTriggerSchedulerTests // ----- Core.VirtualTags-007: timer ticks must not block pool threads and must skip when prior tick is still running ----- /// Verifies that tick is skipped when the prior tick for the same group is still running. + /// A task that represents the asynchronous operation. [Fact] public async Task Tick_skips_when_prior_tick_for_the_same_group_is_still_running() { diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.Tests/VirtualTagEngineTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.Tests/VirtualTagEngineTests.cs index c2598f3d..9674535e 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.Tests/VirtualTagEngineTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.Tests/VirtualTagEngineTests.cs @@ -32,6 +32,7 @@ public sealed class VirtualTagEngineTests } /// Verifies that a simple virtual tag script can read an upstream tag and return a coerced value. + /// A task that represents the asynchronous operation. [Fact] public async Task Simple_script_reads_upstream_and_returns_coerced_value() { @@ -52,6 +53,7 @@ public sealed class VirtualTagEngineTests } /// Verifies that an upstream tag change triggers a cascade re-evaluation through two levels of dependent virtual tags. + /// A task that represents the asynchronous operation. [Fact] public async Task Upstream_change_triggers_cascade_through_two_levels() { @@ -84,6 +86,7 @@ public sealed class VirtualTagEngineTests } /// Verifies that a circular dependency among virtual tags is rejected at load time. + /// A task that represents the asynchronous operation. [Fact] public async Task Cycle_in_virtual_tags_rejected_at_Load() { @@ -98,6 +101,7 @@ public sealed class VirtualTagEngineTests } /// Verifies that script compilation errors surface at load time with all failures aggregated. + /// A task that represents the asynchronous operation. [Fact] public async Task Script_compile_error_surfaces_at_Load_with_all_failures() { @@ -116,6 +120,7 @@ public sealed class VirtualTagEngineTests } /// Verifies that a runtime exception in one virtual tag's script is isolated and does not affect other tags. + /// A task that represents the asynchronous operation. [Fact] public async Task Script_runtime_exception_isolates_to_owning_tag() { @@ -138,6 +143,7 @@ public sealed class VirtualTagEngineTests } /// Verifies that script timeout is mapped to BadInternalError status without killing the engine. + /// A task that represents the asynchronous operation. [Fact] public async Task Timeout_maps_to_BadInternalError_without_killing_the_engine() { @@ -159,6 +165,7 @@ public sealed class VirtualTagEngineTests } /// Verifies that subscribers receive notifications when the engine emits value changes. + /// A task that represents the asynchronous operation. [Fact] public async Task Subscribers_receive_engine_emitted_changes() { @@ -180,6 +187,7 @@ public sealed class VirtualTagEngineTests } /// Verifies that the historize flag routes virtual tag values to the history writer. + /// A task that represents the asynchronous operation. [Fact] public async Task Historize_flag_routes_to_history_writer() { @@ -202,6 +210,7 @@ public sealed class VirtualTagEngineTests } /// Verifies that upstream pushes are ignored when change-driven is false. + /// A task that represents the asynchronous operation. [Fact] public async Task Change_driven_false_ignores_upstream_push() { @@ -224,6 +233,7 @@ public sealed class VirtualTagEngineTests } /// Verifies that reloading the engine replaces existing tags and resubscribes to upstream sources cleanly. + /// A task that represents the asynchronous operation. [Fact] public async Task Reload_replaces_existing_tags_and_resubscribes_cleanly() { @@ -248,6 +258,7 @@ public sealed class VirtualTagEngineTests } /// Verifies that disposing the engine releases all upstream subscriptions. + /// A task that represents the asynchronous operation. [Fact] public async Task Dispose_releases_upstream_subscriptions() { @@ -264,6 +275,7 @@ public sealed class VirtualTagEngineTests } /// Verifies that SetVirtualTag called within a script updates the target and triggers observers. + /// A task that represents the asynchronous operation. [Fact] public async Task SetVirtualTag_within_script_updates_target_and_triggers_observers() { @@ -288,6 +300,7 @@ public sealed class VirtualTagEngineTests } /// Verifies that SetVirtualTag within a script cascades to change-triggered dependents of the written tag. + /// A task that represents the asynchronous operation. [Fact] public async Task SetVirtualTag_within_script_cascades_to_dependents_of_the_written_tag() { @@ -321,6 +334,7 @@ public sealed class VirtualTagEngineTests } /// Verifies that type coercion from script double to configured int32 works correctly. + /// A task that represents the asynchronous operation. [Fact] public async Task Type_coercion_from_script_double_to_config_int32() { @@ -339,6 +353,7 @@ public sealed class VirtualTagEngineTests // ----- Core.VirtualTags-012: previously-missing coverage ----- /// Verifies that the AreInputsReady guard publishes BadWaitingForInitialData when upstream tags have bad status. + /// A task that represents the asynchronous operation. [Fact] public async Task AreInputsReady_guard_publishes_BadWaitingForInitialData_when_upstream_is_bad() { @@ -361,6 +376,7 @@ public sealed class VirtualTagEngineTests } /// Verifies that the AreInputsReady guard recovers when an upstream tag transitions from bad to good status. + /// A task that represents the asynchronous operation. [Fact] public async Task AreInputsReady_guard_publishes_BadWaitingForInitialData_then_recovers_when_upstream_becomes_good() { @@ -386,6 +402,7 @@ public sealed class VirtualTagEngineTests } /// Verifies that SetVirtualTag cascades to change-triggered dependents. + /// A task that represents the asynchronous operation. [Fact] public async Task SetVirtualTag_cascades_to_change_triggered_dependent() { @@ -417,6 +434,7 @@ public sealed class VirtualTagEngineTests } /// Verifies that SetVirtualTag calls targeting unregistered paths are caught at load time. + /// A task that represents the asynchronous operation. [Fact] public async Task SetVirtualTag_on_non_registered_path_is_caught_at_Load() { @@ -441,6 +459,7 @@ public sealed class VirtualTagEngineTests } /// Verifies that EvaluateOneAsync throws ArgumentException when called for an unregistered path. + /// A task that represents the asynchronous operation. [Fact] public async Task EvaluateOneAsync_throws_ArgumentException_for_unregistered_path() { @@ -453,6 +472,7 @@ public sealed class VirtualTagEngineTests } /// Verifies that a type coercion failure maps to BadInternalError status. + /// A task that represents the asynchronous operation. [Fact] public async Task CoerceResult_failure_maps_to_BadInternalError() { @@ -475,6 +495,7 @@ public sealed class VirtualTagEngineTests // ----- Core.VirtualTags-011: Writes target validation at Load time ----- /// Verifies that Load rejects scripts that write to unregistered virtual tag paths. + /// A task that represents the asynchronous operation. [Fact] public async Task Load_rejects_script_writing_to_unregistered_virtual_tag_path() { @@ -499,6 +520,7 @@ public sealed class VirtualTagEngineTests } /// Verifies that Load accepts scripts that write to registered virtual tag paths. + /// A task that represents the asynchronous operation. [Fact] public async Task Load_accepts_script_writing_to_registered_virtual_tag_path() { @@ -559,6 +581,7 @@ public sealed class VirtualTagEngineTests // ----- Core.VirtualTags-004: CoerceResult default arm leaks uncoerced values ----- /// Verifies that CoerceResult correctly handles Int16, UInt16, UInt32, and UInt64 types. + /// A task that represents the asynchronous operation. [Fact] public async Task CoerceResult_handles_Int16_UInt16_UInt32_UInt64() { @@ -588,6 +611,7 @@ public sealed class VirtualTagEngineTests } /// Verifies that Load rejects virtual tag definitions with unsupported DriverDataType values. + /// A task that represents the asynchronous operation. [Fact] public async Task Load_rejects_definition_with_unsupported_DriverDataType() { @@ -608,6 +632,7 @@ public sealed class VirtualTagEngineTests } /// Verifies that Load rejects duplicate virtual tag paths with an aggregated error message. + /// A task that represents the asynchronous operation. [Fact] public async Task Load_rejects_duplicate_path_with_aggregated_error() { @@ -684,9 +709,7 @@ public sealed class VirtualTagEngineTests /// The list to store recorded history entries. public TestHistory(List<(string, DataValueSnapshot)> buf) => _buf = buf; - /// Records a virtual tag path and value snapshot to the history buffer. - /// The virtual tag path. - /// The data value snapshot to record. + /// public void Record(string path, DataValueSnapshot value) { lock (_buf) { _buf.Add((path, value)); } diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.Tests/VirtualTagSourceTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.Tests/VirtualTagSourceTests.cs index c5bbd6aa..87ad4adb 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.Tests/VirtualTagSourceTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Core.VirtualTags.Tests/VirtualTagSourceTests.cs @@ -28,6 +28,7 @@ public sealed class VirtualTagSourceTests } /// Verifies that ReadAsync returns cached engine values. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadAsync_returns_engine_cached_values() { @@ -42,6 +43,7 @@ public sealed class VirtualTagSourceTests } /// Verifies that unknown paths return Bad status quality. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadAsync_unknown_path_returns_Bad_quality() { @@ -52,6 +54,7 @@ public sealed class VirtualTagSourceTests } /// Verifies that subscribe fires the initial data callback immediately. + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeAsync_fires_initial_data_callback() { @@ -75,6 +78,7 @@ public sealed class VirtualTagSourceTests } /// Verifies that subscription fires on upstream changes via engine cascade. + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeAsync_fires_on_upstream_change_via_engine_cascade() { @@ -101,6 +105,7 @@ public sealed class VirtualTagSourceTests } /// Verifies that unsubscribe stops further event emissions. + /// A task that represents the asynchronous operation. [Fact] public async Task UnsubscribeAsync_stops_further_events() { @@ -123,6 +128,7 @@ public sealed class VirtualTagSourceTests } /// Verifies that null arguments are rejected. + /// A task that represents the asynchronous operation. [Fact] public async Task Null_arguments_rejected() { diff --git a/tests/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli.Tests/CommandCancellationTests.cs b/tests/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli.Tests/CommandCancellationTests.cs index af5bd63d..74bd87f6 100644 --- a/tests/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli.Tests/CommandCancellationTests.cs +++ b/tests/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli.Tests/CommandCancellationTests.cs @@ -19,6 +19,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli.Tests; public sealed class CommandCancellationTests { /// Verifies that probe command gracefully handles cancellation during initialization. + /// A task that represents the asynchronous test. [Fact] public async Task ProbeCommand_swallows_cancellation_during_initialize() { @@ -31,6 +32,7 @@ public sealed class CommandCancellationTests } /// Verifies that read command gracefully handles cancellation during initialization. + /// A task that represents the asynchronous test. [Fact] public async Task ReadCommand_swallows_cancellation_during_initialize() { @@ -49,6 +51,7 @@ public sealed class CommandCancellationTests } /// Verifies that write command gracefully handles cancellation during initialization. + /// A task that represents the asynchronous test. [Fact] public async Task WriteCommand_swallows_cancellation_during_initialize() { diff --git a/tests/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli.Tests/ModbusCommandBaseTests.cs b/tests/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli.Tests/ModbusCommandBaseTests.cs index c83b5c04..960cb1d3 100644 --- a/tests/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli.Tests/ModbusCommandBaseTests.cs +++ b/tests/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli.Tests/ModbusCommandBaseTests.cs @@ -28,6 +28,7 @@ public sealed class ModbusCommandBaseTests /// Invokes BuildOptions with the given tags. /// The list of tag definitions to build options for. + /// The produced by . public ModbusDriverOptions Invoke(IReadOnlyList tags) => BuildOptions(tags); /// Invokes ValidateEndpoint. diff --git a/tests/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli.Tests/WriteCommandRegionValidationTests.cs b/tests/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli.Tests/WriteCommandRegionValidationTests.cs index 99e5176d..b20b24d2 100644 --- a/tests/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli.Tests/WriteCommandRegionValidationTests.cs +++ b/tests/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli.Tests/WriteCommandRegionValidationTests.cs @@ -21,6 +21,7 @@ public sealed class WriteCommandRegionValidationTests /// The read-only Modbus region to attempt a write against. /// The data type used in the write attempt. /// The raw string value supplied to the write command. + /// A task that represents the asynchronous test operation. [Theory] [InlineData(ModbusRegion.DiscreteInputs, ModbusDataType.Bool, "0")] [InlineData(ModbusRegion.InputRegisters, ModbusDataType.UInt16, "1")] @@ -44,6 +45,7 @@ public sealed class WriteCommandRegionValidationTests /// Verifies that Coils region requires Bool data type (Driver.Modbus.Cli-002). /// The non-Bool data type that should be rejected for the Coils region. + /// A task that represents the asynchronous test operation. [Theory] [InlineData(ModbusDataType.UInt16)] [InlineData(ModbusDataType.Int16)] diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/AbCipReadSmokeTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/AbCipReadSmokeTests.cs index a6b57fa5..7ccfa6b8 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/AbCipReadSmokeTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/AbCipReadSmokeTests.cs @@ -21,6 +21,7 @@ public sealed class AbCipReadSmokeTests /// Verifies that the driver can read a seeded DInt value from an AB server. /// The AB server profile to test against. + /// A task that represents the asynchronous operation. [AbServerTheory] [MemberData(nameof(Profiles))] public async Task Driver_reads_seeded_DInt_from_ab_server(AbServerProfile profile) diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/AbServerFixture.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/AbServerFixture.cs index f9b23d61..8cf87854 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/AbServerFixture.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/AbServerFixture.cs @@ -55,9 +55,11 @@ public sealed class AbServerFixture : IAsyncLifetime } } - /// + /// Initializes the fixture asynchronously (no-op for this fixture). + /// A completed value task. public ValueTask InitializeAsync() => ValueTask.CompletedTask; - /// + /// Disposes the fixture asynchronously (no-op for this fixture). + /// A completed value task. public ValueTask DisposeAsync() => ValueTask.CompletedTask; /// @@ -65,6 +67,7 @@ public sealed class AbServerFixture : IAsyncLifetime /// / /// to decide whether to skip tests on a fresh clone without a running container. /// + /// if the server is reachable; otherwise. public static bool IsServerAvailable() => TcpProbe(ResolveHost(), ResolvePort()); diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateAlmdTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateAlmdTests.cs index 193c475d..9ca940cf 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateAlmdTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateAlmdTests.cs @@ -40,6 +40,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests.Emulate; public sealed class AbCipEmulateAlmdTests { /// Verifies that real ALMD raise fires OnAlarmEvent through the driver projection. + /// A task that represents the asynchronous operation. [AbServerFact] public async Task Real_ALMD_raise_fires_OnAlarmEvent_through_the_driver_projection() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateUdtReadTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateUdtReadTests.cs index e36919bd..6d7302f8 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateUdtReadTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateUdtReadTests.cs @@ -38,6 +38,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests.Emulate; public sealed class AbCipEmulateUdtReadTests { /// Verifies that reading a whole UDT decodes each member at its template object offset. + /// A task that represents the asynchronous operation. [AbServerFact] public async Task WholeUdt_read_decodes_each_member_at_its_Template_Object_offset() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipAlarmProjectionTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipAlarmProjectionTests.cs index 12180d34..6ede3807 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipAlarmProjectionTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipAlarmProjectionTests.cs @@ -49,6 +49,7 @@ public sealed class AbCipAlarmProjectionTests } /// Verifies that disabled alarm projection returns a valid handle but does not poll. + /// A task that represents the asynchronous test. [Fact] public async Task FeatureFlag_Off_SubscribeAlarms_Returns_Handle_But_Never_Polls() { @@ -76,6 +77,7 @@ public sealed class AbCipAlarmProjectionTests } /// Verifies that enabled alarm projection starts polling and fires raise event on 0-to-1 transition. + /// A task that represents the asynchronous test. [Fact] public async Task FeatureFlag_On_Subscribe_Starts_Polling_And_Fires_Raise_On_0_to_1() { @@ -120,6 +122,7 @@ public sealed class AbCipAlarmProjectionTests } /// Verifies that alarm clear event fires on 1-to-0 transition. + /// A task that represents the asynchronous test. [Fact] public async Task Clear_Event_Fires_On_1_to_0_Transition() { @@ -161,6 +164,7 @@ public sealed class AbCipAlarmProjectionTests } /// Verifies that unsubscribing stops the alarm poll loop. + /// A task that represents the asynchronous test. [Fact] public async Task Unsubscribe_Stops_The_Poll_Loop() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipBoolInDIntRmwTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipBoolInDIntRmwTests.cs index ad0f73c2..40fdcfb7 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipBoolInDIntRmwTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipBoolInDIntRmwTests.cs @@ -19,6 +19,7 @@ public sealed class AbCipBoolInDIntRmwTests } /// Verifies that bit set reads parent, ORs bit, and writes back. + /// A task that represents the asynchronous test operation. [Fact] public async Task Bit_set_reads_parent_ORs_bit_writes_back() { @@ -50,6 +51,7 @@ public sealed class AbCipBoolInDIntRmwTests } /// Verifies that bit clear preserves other bits. + /// A task that represents the asynchronous test operation. [Fact] public async Task Bit_clear_preserves_other_bits() { @@ -73,6 +75,7 @@ public sealed class AbCipBoolInDIntRmwTests } /// Verifies that concurrent bit writes to same parent compose correctly. + /// A task that represents the asynchronous test operation. [Fact] public async Task Concurrent_bit_writes_to_same_parent_compose_correctly() { @@ -98,6 +101,7 @@ public sealed class AbCipBoolInDIntRmwTests } /// Verifies that bit writes to different parents each get their own runtime. + /// A task that represents the asynchronous test operation. [Fact] public async Task Bit_writes_to_different_parents_each_get_own_runtime() { @@ -125,6 +129,7 @@ public sealed class AbCipBoolInDIntRmwTests } /// Verifies that repeat bit writes reuse one parent runtime. + /// A task that represents the asynchronous test operation. [Fact] public async Task Repeat_bit_writes_reuse_one_parent_runtime() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverCodeReviewRegressionTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverCodeReviewRegressionTests.cs index 477a6bb6..6397ae44 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverCodeReviewRegressionTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverCodeReviewRegressionTests.cs @@ -19,6 +19,7 @@ public sealed class AbCipDriverCodeReviewRegressionTests // ---- Driver.AbCip-001 — ReinitializeAsync must apply a changed config JSON ---- /// Tests that InitializeAsync applies devices and tags from the config JSON. + /// A task that represents the asynchronous operation. [Fact] public async Task InitializeAsync_applies_devices_and_tags_from_the_config_json() { @@ -40,6 +41,7 @@ public sealed class AbCipDriverCodeReviewRegressionTests } /// Tests that ReinitializeAsync with changed config JSON picks up the new device. + /// A task that represents the asynchronous operation. [Fact] public async Task ReinitializeAsync_with_a_changed_config_json_picks_up_the_new_device() { @@ -63,6 +65,7 @@ public sealed class AbCipDriverCodeReviewRegressionTests } /// Tests that InitializeAsync with blank JSON keeps construction-time options. + /// A task that represents the asynchronous operation. [Fact] public async Task InitializeAsync_with_blank_json_keeps_construction_time_options() { @@ -82,6 +85,7 @@ public sealed class AbCipDriverCodeReviewRegressionTests // ---- Driver.AbCip-003 — declaration-only whole-UDT grouping is opt-in ---- /// Tests that whole UDT grouping is off by default so members read per tag. + /// A task that represents the asynchronous operation. [Fact] public async Task Whole_udt_grouping_is_off_by_default_so_members_read_per_tag() { @@ -137,6 +141,7 @@ public sealed class AbCipDriverCodeReviewRegressionTests // ---- Driver.AbCip-008 — ShutdownAsync awaits probe loops; reads are concurrency-safe ---- /// Tests that ShutdownAsync awaits the probe loop before returning. + /// A task that represents the asynchronous operation. [Fact] public async Task ShutdownAsync_awaits_the_probe_loop_before_returning() { @@ -162,6 +167,7 @@ public sealed class AbCipDriverCodeReviewRegressionTests } /// Tests that ShutdownAsync is idempotent. + /// A task that represents the asynchronous operation. [Fact] public async Task ShutdownAsync_is_idempotent() { @@ -176,6 +182,7 @@ public sealed class AbCipDriverCodeReviewRegressionTests } /// Tests that concurrent first reads of the same tag do not corrupt the runtime cache. + /// A task that represents the asynchronous operation. [Fact] public async Task Concurrent_first_reads_of_the_same_tag_do_not_corrupt_the_runtime_cache() { @@ -217,6 +224,7 @@ public sealed class AbCipDriverCodeReviewRegressionTests } /// Tests that read UDInt tag returns uint value not negative-wrapped int. + /// A task that represents the asynchronous operation. [Fact] public async Task Read_UDInt_tag_returns_uint_value_not_negative_wrapped_int() { @@ -243,6 +251,7 @@ public sealed class AbCipDriverCodeReviewRegressionTests // ---- Driver.AbCip-005 — Structure parent not registered; duplicate key check ---- /// Tests that structure parent tag read returns BadNotSupported not Good null. + /// A task that represents the asynchronous operation. [Fact] public async Task Structure_parent_tag_read_returns_BadNotSupported_not_Good_null() { @@ -318,6 +327,7 @@ public sealed class AbCipDriverCodeReviewRegressionTests // ---- Driver.AbCip-010 — stale runtime evicted on failure ---- /// Tests that read failure evicts runtime so next read creates fresh handle. + /// A task that represents the asynchronous operation. [Fact] public async Task Read_failure_evicts_runtime_so_next_read_creates_fresh_handle() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverDiscoveryTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverDiscoveryTests.cs index 7a1b3ac5..9d222743 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverDiscoveryTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverDiscoveryTests.cs @@ -10,6 +10,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests; public sealed class AbCipDriverDiscoveryTests { /// Verifies that pre-declared tags emit as variables under device folder. + /// A task that represents the asynchronous test operation. [Fact] public async Task PreDeclared_tags_emit_as_variables_under_device_folder() { @@ -35,6 +36,7 @@ public sealed class AbCipDriverDiscoveryTests } /// Verifies that device folder display name falls back to host address when not provided. + /// A task that represents the asynchronous test operation. [Fact] public async Task Device_folder_displayname_falls_back_to_host_address() { @@ -52,6 +54,7 @@ public sealed class AbCipDriverDiscoveryTests } /// Verifies that pre-declared system tags are filtered out. + /// A task that represents the asynchronous test operation. [Fact] public async Task PreDeclared_system_tags_are_filtered_out() { @@ -74,6 +77,7 @@ public sealed class AbCipDriverDiscoveryTests } /// Verifies that tags for mismatched devices are ignored. + /// A task that represents the asynchronous test operation. [Fact] public async Task Tags_for_mismatched_device_are_ignored() { @@ -91,6 +95,7 @@ public sealed class AbCipDriverDiscoveryTests } /// Verifies that controller enumeration adds tags under Discovered folder. + /// A task that represents the asynchronous test operation. [Fact] public async Task Controller_enumeration_adds_tags_under_Discovered_folder() { @@ -114,6 +119,7 @@ public sealed class AbCipDriverDiscoveryTests } /// Verifies that controller enumeration honours system tag hint and filter. + /// A task that represents the asynchronous test operation. [Fact] public async Task Controller_enumeration_honours_system_tag_hint_and_filter() { @@ -136,6 +142,7 @@ public sealed class AbCipDriverDiscoveryTests } /// Verifies that controller enumeration ReadOnly flag surfaces ViewOnly classification. + /// A task that represents the asynchronous test operation. [Fact] public async Task Controller_enumeration_ReadOnly_surfaces_ViewOnly_classification() { @@ -156,6 +163,7 @@ public sealed class AbCipDriverDiscoveryTests } /// Verifies that controller enumeration receives correct device parameters. + /// A task that represents the asynchronous test operation. [Fact] public async Task Controller_enumeration_receives_correct_device_params() { @@ -236,6 +244,7 @@ public sealed class AbCipDriverDiscoveryTests } /// Verifies that FlushOptionalCachesAsync clears the template cache. + /// A task that represents the asynchronous test operation. [Fact] public async Task FlushOptionalCachesAsync_clears_template_cache() { @@ -257,39 +266,29 @@ public sealed class AbCipDriverDiscoveryTests /// Gets the list of recorded variables. public List<(string BrowseName, DriverAttributeInfo Info)> Variables { get; } = new(); - /// Records a folder node. - /// The browse name of the folder. - /// The display name of the folder. + /// public IAddressSpaceBuilder Folder(string browseName, string displayName) { Folders.Add((browseName, displayName)); return this; } - /// Records a variable node. - /// The browse name of the variable. - /// The display name of the variable. - /// The attribute information for the variable. + /// public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo info) { Variables.Add((browseName, info)); return new Handle(info.FullName); } - /// Adds a property (no-op in test). - /// Property name (unused in test). - /// Property data type (unused in test). - /// Property value (unused in test). + /// public void AddProperty(string _, DriverDataType __, object? ___) { } /// Test variable handle. private sealed class Handle(string fullRef) : IVariableHandle { - /// Gets the full reference of the variable. + /// public string FullReference => fullRef; - /// Marks the variable as an alarm condition. - /// The alarm condition information. + /// public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) => new NullSink(); } /// Null sink for alarm conditions. private sealed class NullSink : IAlarmConditionSink { - /// Handles alarm transition (no-op). - /// The alarm event arguments. + /// public void OnTransition(AlarmEventArgs args) { } } } @@ -303,15 +302,13 @@ public sealed class AbCipDriverDiscoveryTests /// Initializes a new instance of the FakeEnumeratorFactory. /// The tags to enumerate. public FakeEnumeratorFactory(params AbCipDiscoveredTag[] tags) => _tags = tags; - /// Creates a new fake enumerator. + /// public IAbCipTagEnumerator Create() => new FakeEnumerator(this); /// Fake tag enumerator for testing. private sealed class FakeEnumerator(FakeEnumeratorFactory outer) : IAbCipTagEnumerator { - /// Enumerates discovered tags asynchronously. - /// The device parameters for enumeration. - /// The cancellation token. + /// public async IAsyncEnumerable EnumerateAsync( AbCipTagCreateParams deviceParams, [EnumeratorCancellation] CancellationToken cancellationToken) diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverReadTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverReadTests.cs index e37b1a0f..79506f79 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverReadTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverReadTests.cs @@ -22,6 +22,7 @@ public sealed class AbCipDriverReadTests } /// Verifies that an unknown reference maps to BadNodeIdUnknown status. + /// A task that represents the asynchronous test operation. [Fact] public async Task Unknown_reference_maps_to_BadNodeIdUnknown() { @@ -35,6 +36,7 @@ public sealed class AbCipDriverReadTests } /// Verifies that a tag on an unknown device maps to BadNodeIdUnknown status. + /// A task that represents the asynchronous test operation. [Fact] public async Task Tag_on_unknown_device_maps_to_BadNodeIdUnknown() { @@ -52,6 +54,7 @@ public sealed class AbCipDriverReadTests } /// Verifies that a successful DInt read returns Good status with the correct value. + /// A task that represents the asynchronous test operation. [Fact] public async Task Successful_DInt_read_returns_Good_with_value() { @@ -71,6 +74,7 @@ public sealed class AbCipDriverReadTests } /// Verifies that repeated reads reuse the runtime without reinitializing. + /// A task that represents the asynchronous test operation. [Fact] public async Task Repeat_read_reuses_runtime_without_reinitialise() { @@ -88,6 +92,7 @@ public sealed class AbCipDriverReadTests } /// Verifies that non-zero libplctag status is mapped via AbCipStatusMapper. + /// A task that represents the asynchronous test operation. [Fact] public async Task NonZero_libplctag_status_maps_via_AbCipStatusMapper() { @@ -103,6 +108,7 @@ public sealed class AbCipDriverReadTests } /// Verifies that an exception during read surfaces BadCommunicationError status. + /// A task that represents the asynchronous test operation. [Fact] public async Task Exception_during_read_surfaces_BadCommunicationError() { @@ -119,6 +125,7 @@ public sealed class AbCipDriverReadTests } /// Verifies that batched reads preserve order and per-tag status. + /// A task that represents the asynchronous test operation. [Fact] public async Task Batched_reads_preserve_order_and_per_tag_status() { @@ -144,6 +151,7 @@ public sealed class AbCipDriverReadTests } /// Verifies that a successful read marks health as Healthy. + /// A task that represents the asynchronous test operation. [Fact] public async Task Successful_read_marks_health_Healthy() { @@ -158,6 +166,7 @@ public sealed class AbCipDriverReadTests } /// Verifies that tag creation parameters are built correctly from device and profile. + /// A task that represents the asynchronous test operation. [Fact] public async Task TagCreateParams_are_built_from_device_and_profile() { @@ -176,6 +185,7 @@ public sealed class AbCipDriverReadTests } /// Verifies that cancellation propagates from read operations. + /// A task that represents the asynchronous test operation. [Fact] public async Task Cancellation_propagates_from_read() { @@ -194,6 +204,7 @@ public sealed class AbCipDriverReadTests } /// Verifies that ShutdownAsync disposes each tag runtime. + /// A task that represents the asynchronous test operation. [Fact] public async Task ShutdownAsync_disposes_each_tag_runtime() { @@ -211,6 +222,7 @@ public sealed class AbCipDriverReadTests } /// Verifies that initialization failure disposes the tag and surfaces communication error. + /// A task that represents the asynchronous test operation. [Fact] public async Task Initialize_failure_disposes_tag_and_surfaces_communication_error() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverTests.cs index 235e4390..3f52ad9a 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverTests.cs @@ -19,6 +19,7 @@ public sealed class AbCipDriverTests } /// Verifies InitializeAsync with no devices succeeds and marks driver healthy. + /// A task that represents the asynchronous operation. [Fact] public async Task InitializeAsync_with_empty_devices_succeeds_and_marks_healthy() { @@ -28,6 +29,7 @@ public sealed class AbCipDriverTests } /// Verifies InitializeAsync registers devices with their respective PLC family profiles. + /// A task that represents the asynchronous operation. [Fact] public async Task InitializeAsync_registers_each_device_with_its_family_profile() { @@ -48,6 +50,7 @@ public sealed class AbCipDriverTests } /// Verifies InitializeAsync rejects malformed host addresses and faults the driver. + /// A task that represents the asynchronous operation. [Fact] public async Task InitializeAsync_with_malformed_host_address_faults() { @@ -62,6 +65,7 @@ public sealed class AbCipDriverTests } /// Verifies ShutdownAsync clears devices and marks driver state unknown. + /// A task that represents the asynchronous operation. [Fact] public async Task ShutdownAsync_clears_devices_and_marks_unknown() { @@ -79,6 +83,7 @@ public sealed class AbCipDriverTests } /// Verifies ReinitializeAsync stops and restarts all devices. + /// A task that represents the asynchronous operation. [Fact] public async Task ReinitializeAsync_cycles_devices() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverWholeUdtReadTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverWholeUdtReadTests.cs index 56f48150..d3918307 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverWholeUdtReadTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverWholeUdtReadTests.cs @@ -35,6 +35,7 @@ public sealed class AbCipDriverWholeUdtReadTests ]); /// Verifies that multiple members of the same UDT trigger only one parent read. + /// A task that represents the asynchronous operation. [Fact] public async Task Two_members_of_same_udt_trigger_one_parent_read() { @@ -55,6 +56,7 @@ public sealed class AbCipDriverWholeUdtReadTests } /// Verifies that each UDT member is decoded at its correct offset. + /// A task that represents the asynchronous operation. [Fact] public async Task Each_member_decodes_at_its_own_offset() { @@ -81,6 +83,7 @@ public sealed class AbCipDriverWholeUdtReadTests } /// Verifies that parent read failure marks all grouped members as Bad. + /// A task that represents the asynchronous operation. [Fact] public async Task Parent_read_failure_stamps_every_grouped_member_Bad() { @@ -101,6 +104,7 @@ public sealed class AbCipDriverWholeUdtReadTests } /// Verifies that mixed batches group UDT members and fall back to atomic reads. + /// A task that represents the asynchronous operation. [Fact] public async Task Mixed_batch_groups_udt_and_falls_back_atomics() { @@ -121,6 +125,7 @@ public sealed class AbCipDriverWholeUdtReadTests } /// Verifies that a single UDT member uses the per-tag read path rather than grouping. + /// A task that represents the asynchronous operation. [Fact] public async Task Single_member_of_Udt_uses_per_tag_read_path() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverWriteTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverWriteTests.cs index 9340dec3..d3610ba7 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverWriteTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipDriverWriteTests.cs @@ -21,6 +21,7 @@ public sealed class AbCipDriverWriteTests } /// Verifies that unknown reference maps to BadNodeIdUnknown status. + /// A task that represents the asynchronous operation. [Fact] public async Task Unknown_reference_maps_to_BadNodeIdUnknown() { @@ -34,6 +35,7 @@ public sealed class AbCipDriverWriteTests } /// Verifies that non-writable tags map to BadNotWritable status. + /// A task that represents the asynchronous operation. [Fact] public async Task Non_writable_tag_maps_to_BadNotWritable() { @@ -48,6 +50,7 @@ public sealed class AbCipDriverWriteTests } /// Verifies that successful DInt writes encode and flush values. + /// A task that represents the asynchronous operation. [Fact] public async Task Successful_DInt_write_encodes_and_flushes() { @@ -64,6 +67,7 @@ public sealed class AbCipDriverWriteTests } /// Verifies that bit-in-DInt writes succeed via read-modify-write. + /// A task that represents the asynchronous operation. [Fact] public async Task Bit_in_dint_write_now_succeeds_via_RMW() { @@ -85,6 +89,7 @@ public sealed class AbCipDriverWriteTests } /// Verifies that non-zero libplctag status after write maps correctly. + /// A task that represents the asynchronous operation. [Fact] public async Task Non_zero_libplctag_status_after_write_maps_via_AbCipStatusMapper() { @@ -100,6 +105,7 @@ public sealed class AbCipDriverWriteTests } /// Verifies that type mismatch surfaces BadTypeMismatch status. + /// A task that represents the asynchronous operation. [Fact] public async Task Type_mismatch_surfaces_BadTypeMismatch() { @@ -126,6 +132,7 @@ public sealed class AbCipDriverWriteTests } /// Verifies that overflow surfaces BadOutOfRange status. + /// A task that represents the asynchronous operation. [Fact] public async Task Overflow_surfaces_BadOutOfRange() { @@ -144,6 +151,7 @@ public sealed class AbCipDriverWriteTests } /// Verifies that exceptions during write surface BadCommunicationError. + /// A task that represents the asynchronous operation. [Fact] public async Task Exception_during_write_surfaces_BadCommunicationError() { @@ -160,6 +168,7 @@ public sealed class AbCipDriverWriteTests } /// Verifies that batch write preserves order across success and failure. + /// A task that represents the asynchronous operation. [Fact] public async Task Batch_preserves_order_across_success_and_failure() { @@ -192,6 +201,7 @@ public sealed class AbCipDriverWriteTests } /// Verifies that cancellation propagates from write operations. + /// A task that represents the asynchronous operation. [Fact] public async Task Cancellation_propagates_from_write() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipFetchUdtShapeTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipFetchUdtShapeTests.cs index 2d0554a8..03327917 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipFetchUdtShapeTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipFetchUdtShapeTests.cs @@ -25,11 +25,7 @@ public sealed class AbCipFetchUdtShapeTests /// Gets the last template ID read. public uint LastTemplateId { get; private set; } - /// Reads the template data for the specified device and template ID. - /// The device parameters. - /// The template instance ID. - /// The cancellation token. - /// A task that returns the template response bytes. + /// public Task ReadAsync(AbCipTagCreateParams deviceParams, uint templateInstanceId, CancellationToken ct) { ReadCount++; @@ -50,8 +46,7 @@ public sealed class AbCipFetchUdtShapeTests /// Gets or sets an optional customization function for reader creation. public Func? Customise { get; set; } - /// Creates a new template reader. - /// The created reader. + /// public IAbCipTemplateReader Create() { var r = Customise?.Invoke() ?? new FakeTemplateReader(); @@ -93,6 +88,7 @@ public sealed class AbCipFetchUdtShapeTests } /// Verifies that FetchUdtShapeAsync decodes a blob and caches the result. + /// A task that represents the asynchronous test operation. [Fact] public async Task FetchUdtShapeAsync_decodes_blob_and_caches_result() { @@ -123,6 +119,7 @@ public sealed class AbCipFetchUdtShapeTests } /// Verifies that different template IDs result in separate fetch operations. + /// A task that represents the asynchronous test operation. [Fact] public async Task FetchUdtShapeAsync_different_templateIds_each_fetch() { @@ -154,6 +151,7 @@ public sealed class AbCipFetchUdtShapeTests } /// Verifies that FetchUdtShapeAsync returns null for an unknown device. + /// A task that represents the asynchronous test operation. [Fact] public async Task FetchUdtShapeAsync_unknown_device_returns_null() { @@ -170,6 +168,7 @@ public sealed class AbCipFetchUdtShapeTests } /// Verifies that a decode failure returns null and does not cache the result. + /// A task that represents the asynchronous test operation. [Fact] public async Task FetchUdtShapeAsync_decode_failure_returns_null_and_does_not_cache() { @@ -193,6 +192,7 @@ public sealed class AbCipFetchUdtShapeTests } /// Verifies that a reader exception returns null. + /// A task that represents the asynchronous test operation. [Fact] public async Task FetchUdtShapeAsync_reader_exception_returns_null() { @@ -211,6 +211,7 @@ public sealed class AbCipFetchUdtShapeTests } /// Verifies that FlushOptionalCachesAsync empties the template cache. + /// A task that represents the asynchronous test operation. [Fact] public async Task FlushOptionalCachesAsync_empties_template_cache() { @@ -241,11 +242,7 @@ public sealed class AbCipFetchUdtShapeTests /// Test implementation of IAbCipTemplateReader that throws on read. private sealed class ThrowingTemplateReader : IAbCipTemplateReader { - /// Throws an exception when read is attempted. - /// The device parameters. - /// The template ID. - /// The cancellation token. - /// Never returns; throws instead. + /// public Task ReadAsync(AbCipTagCreateParams p, uint id, CancellationToken ct) => throw new InvalidOperationException("fake read failure"); diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipHostProbeTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipHostProbeTests.cs index 4e8366ec..a52b9464 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipHostProbeTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipHostProbeTests.cs @@ -10,6 +10,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests; public sealed class AbCipHostProbeTests { /// Verifies that GetHostStatuses returns one entry per configured device. + /// A task that represents the asynchronous operation. [Fact] public async Task GetHostStatuses_returns_one_entry_per_device() { @@ -31,6 +32,7 @@ public sealed class AbCipHostProbeTests } /// Verifies that a successful probe read transitions the host state to Running. + /// A task that represents the asynchronous operation. [Fact] public async Task Probe_with_successful_read_transitions_to_Running() { @@ -58,6 +60,7 @@ public sealed class AbCipHostProbeTests } /// Verifies that a failed probe read transitions the host state to Stopped. + /// A task that represents the asynchronous operation. [Fact] public async Task Probe_with_read_failure_transitions_to_Stopped() { @@ -87,6 +90,7 @@ public sealed class AbCipHostProbeTests } /// Verifies that the probe is disabled when the Enabled option is false. + /// A task that represents the asynchronous operation. [Fact] public async Task Probe_disabled_when_Enabled_is_false() { @@ -108,6 +112,7 @@ public sealed class AbCipHostProbeTests } /// Verifies that the probe is skipped when ProbeTagPath is null. + /// A task that represents the asynchronous operation. [Fact] public async Task Probe_skipped_when_ProbeTagPath_is_null() { @@ -125,6 +130,7 @@ public sealed class AbCipHostProbeTests } /// Verifies that the probe loops across multiple devices independently. + /// A task that represents the asynchronous operation. [Fact] public async Task Probe_loops_across_multiple_devices_independently() { @@ -162,6 +168,7 @@ public sealed class AbCipHostProbeTests // ---- IPerCallHostResolver ---- /// Verifies that ResolveHost returns the declared device for a known tag. + /// A task that represents the asynchronous operation. [Fact] public async Task ResolveHost_returns_declared_device_for_known_tag() { @@ -186,6 +193,7 @@ public sealed class AbCipHostProbeTests } /// Verifies that ResolveHost falls back to the first device for an unknown tag reference. + /// A task that represents the asynchronous operation. [Fact] public async Task ResolveHost_falls_back_to_first_device_for_unknown_reference() { @@ -200,6 +208,7 @@ public sealed class AbCipHostProbeTests } /// Verifies that ResolveHost falls back to the driver instance ID when no devices are configured. + /// A task that represents the asynchronous operation. [Fact] public async Task ResolveHost_falls_back_to_DriverInstanceId_when_no_devices() { @@ -210,6 +219,7 @@ public sealed class AbCipHostProbeTests } /// Verifies that ResolveHost for a UDT member walks to the synthesized definition. + /// A task that represents the asynchronous operation. [Fact] public async Task ResolveHost_for_UDT_member_walks_to_synthesised_definition() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipLoggingTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipLoggingTests.cs index 73f2f5b8..2e22f620 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipLoggingTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipLoggingTests.cs @@ -36,6 +36,7 @@ public sealed class AbCipLoggingTests } /// Verifies that ProbeLoop logs when an exception is swallowed. + /// A task that represents the asynchronous operation. [Fact] public async Task ProbeLoop_logs_when_an_exception_is_swallowed() { @@ -79,6 +80,7 @@ public sealed class AbCipLoggingTests } /// Verifies that ReadFailure logs at warning level. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadFailure_logs_at_warning_level() { @@ -106,6 +108,7 @@ public sealed class AbCipLoggingTests } /// Verifies that ReadException logs at warning level. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadException_logs_at_warning_level() { @@ -137,6 +140,7 @@ public sealed class AbCipLoggingTests } /// Verifies that InitializeAsync warns when probe is enabled but ProbeTagPath is blank. + /// A task that represents the asynchronous operation. [Fact] public async Task InitializeAsync_warns_when_probe_is_enabled_but_ProbeTagPath_is_blank() { @@ -164,6 +168,7 @@ public sealed class AbCipLoggingTests } /// Verifies that InitializeAsync does not warn when probe is disabled. + /// A task that represents the asynchronous operation. [Fact] public async Task InitializeAsync_does_not_warn_when_probe_is_disabled() { @@ -191,9 +196,11 @@ public sealed class AbCipLoggingTests /// Begins a scope (stub implementation). /// The type of the scope state. /// The scope state. + /// A no-op disposable scope. public IDisposable BeginScope(TState state) where TState : notnull => NullScope.Instance; /// Checks if logging is enabled (always true). /// The log level to check. + /// true always — this capturing logger accepts every log level. public bool IsEnabled(LogLevel logLevel) => true; /// Logs an entry and captures it. /// The type of the log state. diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipPerDeviceConnectionOptionsTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipPerDeviceConnectionOptionsTests.cs index c290672f..14e2d770 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipPerDeviceConnectionOptionsTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipPerDeviceConnectionOptionsTests.cs @@ -21,6 +21,7 @@ public sealed class AbCipPerDeviceConnectionOptionsTests private const string Device = "ab://10.0.0.5/1,0"; /// Verifies that per-device AllowPacking override is forwarded to tag creation parameters. + /// A task that represents the asynchronous operation. [Fact] public async Task Device_AllowPacking_override_is_forwarded_to_tag_create_params() { @@ -40,6 +41,7 @@ public sealed class AbCipPerDeviceConnectionOptionsTests } /// Verifies that AllowPacking defaults inherit from the family profile when not overridden. + /// A task that represents the asynchronous operation. [Fact] public async Task Device_AllowPacking_default_inherits_from_family_profile() { @@ -61,6 +63,7 @@ public sealed class AbCipPerDeviceConnectionOptionsTests } /// Verifies that Micro800 devices have AllowPacking defaulting to false from the family profile. + /// A task that represents the asynchronous operation. [Fact] public async Task Micro800_default_AllowPacking_is_false_from_family_profile() { @@ -81,6 +84,7 @@ public sealed class AbCipPerDeviceConnectionOptionsTests } /// Verifies that per-device ConnectionSize override is forwarded to tag creation parameters. + /// A task that represents the asynchronous operation. [Fact] public async Task Device_ConnectionSize_override_is_forwarded_to_tag_create_params() { @@ -99,6 +103,7 @@ public sealed class AbCipPerDeviceConnectionOptionsTests } /// Verifies that ConnectionSize defaults inherit from the family profile when not overridden. + /// A task that represents the asynchronous operation. [Fact] public async Task Device_ConnectionSize_default_inherits_from_family_profile() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipPlcFamilyTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipPlcFamilyTests.cs index 78b496c2..2c00f307 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipPlcFamilyTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipPlcFamilyTests.cs @@ -25,6 +25,7 @@ public sealed class AbCipPlcFamilyTests } /// Verifies that a ControlLogix device initializes with the correct profile. + /// A task that represents the asynchronous test operation. [Fact] public async Task ControlLogix_device_initialises_with_correct_profile() { @@ -54,6 +55,7 @@ public sealed class AbCipPlcFamilyTests } /// Verifies that a CompactLogix device initializes with a narrow connection size. + /// A task that represents the asynchronous test operation. [Fact] public async Task CompactLogix_device_initialises_with_narrow_ConnectionSize() { @@ -85,6 +87,7 @@ public sealed class AbCipPlcFamilyTests } /// Verifies that a Micro800 device with an empty CIP path parses correctly. + /// A task that represents the asynchronous test operation. [Fact] public async Task Micro800_device_with_empty_cip_path_parses_correctly() { @@ -102,6 +105,7 @@ public sealed class AbCipPlcFamilyTests } /// Verifies that Micro800 read operations forward the empty path to tag creation parameters. + /// A task that represents the asynchronous test operation. [Fact] public async Task Micro800_read_forwards_empty_path_to_tag_create_params() { @@ -134,6 +138,7 @@ public sealed class AbCipPlcFamilyTests } /// Verifies that GuardLogix safety tags surface as ViewOnly in discovery. + /// A task that represents the asynchronous test operation. [Fact] public async Task GuardLogix_safety_tag_surfaces_as_ViewOnly_in_discovery() { @@ -160,6 +165,7 @@ public sealed class AbCipPlcFamilyTests } /// Verifies that GuardLogix safety tag writes are rejected even when the tag is marked Writable. + /// A task that represents the asynchronous test operation. [Fact] public async Task GuardLogix_safety_tag_writes_rejected_even_when_Writable_is_true() { @@ -206,37 +212,27 @@ public sealed class AbCipPlcFamilyTests /// Gets the list of variables recorded by this builder. public List<(string BrowseName, DriverAttributeInfo Info)> Variables { get; } = new(); - /// Adds a folder to the recorded list and returns this builder for chaining. - /// The browse name of the folder. - /// The display name of the folder. + /// public IAddressSpaceBuilder Folder(string browseName, string displayName) { Folders.Add((browseName, displayName)); return this; } - /// Adds a variable to the recorded list and returns a handle. - /// The browse name of the variable. - /// The display name of the variable. - /// The driver attribute information. + /// public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo info) { Variables.Add((browseName, info)); return new Handle(info.FullName); } - /// No-op property adding operation for test compatibility. - /// The property name. - /// The property data type. - /// The property value. + /// public void AddProperty(string _, DriverDataType __, object? ___) { } private sealed class Handle(string fullRef) : IVariableHandle { - /// Gets the full reference for this variable handle. + /// public string FullReference => fullRef; - /// Marks this variable as an alarm condition and returns a null sink. - /// The alarm condition information. + /// public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) => new NullSink(); } private sealed class NullSink : IAlarmConditionSink { - /// Called when an alarm state transitions. - /// The alarm event arguments. + /// public void OnTransition(AlarmEventArgs args) { } } } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipSubscriptionTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipSubscriptionTests.cs index 1bb8c48c..811570f4 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipSubscriptionTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipSubscriptionTests.cs @@ -21,6 +21,7 @@ public sealed class AbCipSubscriptionTests } /// Verifies that the initial poll raises OnDataChange events for every subscribed tag. + /// A task that represents the asynchronous test. [Fact] public async Task Initial_poll_raises_OnDataChange_for_every_tag() { @@ -47,6 +48,7 @@ public sealed class AbCipSubscriptionTests } /// Verifies that unchanged values raise OnDataChange only once (on initial poll). + /// A task that represents the asynchronous test. [Fact] public async Task Unchanged_value_raises_only_once() { @@ -66,6 +68,7 @@ public sealed class AbCipSubscriptionTests } /// Verifies that value changes between polls raise OnDataChange events. + /// A task that represents the asynchronous test. [Fact] public async Task Value_change_between_polls_raises_OnDataChange() { @@ -89,6 +92,7 @@ public sealed class AbCipSubscriptionTests } /// Verifies that unsubscribe halts polling and no further events are raised. + /// A task that represents the asynchronous test. [Fact] public async Task Unsubscribe_halts_polling() { @@ -112,6 +116,7 @@ public sealed class AbCipSubscriptionTests } /// Verifies that polling intervals below 100ms are floored to the minimum. + /// A task that represents the asynchronous test. [Fact] public async Task Interval_below_100ms_is_floored() { @@ -133,6 +138,7 @@ public sealed class AbCipSubscriptionTests } /// Verifies that ShutdownAsync cancels all active subscriptions. + /// A task that represents the asynchronous test. [Fact] public async Task ShutdownAsync_cancels_active_subscriptions() { @@ -154,6 +160,7 @@ public sealed class AbCipSubscriptionTests } /// Verifies that subscriptions on UDT members use the synthesized full reference. + /// A task that represents the asynchronous test. [Fact] public async Task Subscription_on_UDT_member_uses_synthesised_full_reference() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipUdtMemberTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipUdtMemberTests.cs index fea9b9f6..412f5581 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipUdtMemberTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipUdtMemberTests.cs @@ -9,6 +9,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests; public sealed class AbCipUdtMemberTests { /// Verifies that UDT with declared members expands to individual member variables. + /// A task that represents the asynchronous operation. [Fact] public async Task UDT_with_declared_members_fans_out_to_member_variables() { @@ -48,6 +49,7 @@ public sealed class AbCipUdtMemberTests } /// Verifies that UDT members can be read via synthesised full reference paths. + /// A task that represents the asynchronous operation. [Fact] public async Task UDT_members_resolvable_for_read_via_synthesised_full_reference() { @@ -84,6 +86,7 @@ public sealed class AbCipUdtMemberTests } /// Verifies that UDT member writes route through synthesised tag paths. + /// A task that represents the asynchronous operation. [Fact] public async Task UDT_member_write_routes_through_synthesised_tagpath() { @@ -110,6 +113,7 @@ public sealed class AbCipUdtMemberTests } /// Verifies that UDT member read/write operations respect the Writable flag. + /// A task that represents the asynchronous operation. [Fact] public async Task UDT_member_read_write_honours_member_Writable_flag() { @@ -135,6 +139,7 @@ public sealed class AbCipUdtMemberTests } /// Verifies that structure tags without declared members appear as single variables. + /// A task that represents the asynchronous operation. [Fact] public async Task Structure_tag_without_members_is_emitted_as_single_variable() { @@ -156,6 +161,7 @@ public sealed class AbCipUdtMemberTests } /// Verifies that empty member lists are treated the same as null. + /// A task that represents the asynchronous operation. [Fact] public async Task Empty_Members_list_is_treated_like_null() { @@ -174,6 +180,7 @@ public sealed class AbCipUdtMemberTests } /// Verifies that UDT members and flat tags can coexist in the address space. + /// A task that represents the asynchronous operation. [Fact] public async Task UDT_members_mixed_with_flat_tags_coexist() { @@ -209,39 +216,29 @@ public sealed class AbCipUdtMemberTests /// Gets the collected variables. public List<(string BrowseName, DriverAttributeInfo Info)> Variables { get; } = new(); - /// Records a folder in the address space. - /// The browse name of the folder. - /// The display name of the folder. + /// public IAddressSpaceBuilder Folder(string browseName, string displayName) { Folders.Add((browseName, displayName)); return this; } - /// Records a variable in the address space. - /// The browse name of the variable. - /// The display name of the variable. - /// The driver attribute information for the variable. + /// public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo info) { Variables.Add((browseName, info)); return new Handle(info.FullName); } - /// Records a property (stub implementation for testing). - /// The property name (unused in this stub). - /// The property data type (unused in this stub). - /// The property value (unused in this stub). + /// public void AddProperty(string _, DriverDataType __, object? ___) { } /// Variable handle implementation for testing. private sealed class Handle(string fullRef) : IVariableHandle { - /// Gets the full reference path. + /// public string FullReference => fullRef; - /// Marks this handle as an alarm condition. - /// The alarm condition information. + /// public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) => new NullSink(); } /// Null alarm condition sink for testing. private sealed class NullSink : IAlarmConditionSink { - /// Handles alarm transitions (stub). - /// The alarm event arguments. + /// public void OnTransition(AlarmEventArgs args) { } } } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/FakeAbCipTag.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/FakeAbCipTag.cs index 2e437a1d..0649294d 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/FakeAbCipTag.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/FakeAbCipTag.cs @@ -35,8 +35,7 @@ internal class FakeAbCipTag : IAbCipTagRuntime /// The tag creation parameters. public FakeAbCipTag(AbCipTagCreateParams createParams) => CreationParams = createParams; - /// Increments the initialize count and simulates initialization. - /// The cancellation token for the operation. + /// public virtual Task InitializeAsync(CancellationToken cancellationToken) { InitializeCount++; @@ -44,8 +43,7 @@ internal class FakeAbCipTag : IAbCipTagRuntime return Task.CompletedTask; } - /// Increments the read count and simulates a read operation. - /// The cancellation token for the operation. + /// public virtual Task ReadAsync(CancellationToken cancellationToken) { ReadCount++; @@ -53,20 +51,17 @@ internal class FakeAbCipTag : IAbCipTagRuntime return Task.CompletedTask; } - /// Increments the write count and simulates a write operation. - /// The cancellation token for the operation. + /// public virtual Task WriteAsync(CancellationToken cancellationToken) { WriteCount++; return Task.CompletedTask; } - /// Returns the simulated status code. + /// public virtual int GetStatus() => Status; - /// Returns the mock tag value. - /// The data type being decoded. - /// The optional bit index for bit operations. + /// public virtual object? DecodeValue(AbCipDataType type, int? bitIndex) => Value; /// @@ -78,20 +73,14 @@ internal class FakeAbCipTag : IAbCipTagRuntime /// public Dictionary ValuesByOffset { get; } = new(); - /// Returns the mock value at the specified offset. - /// The data type being decoded. - /// The byte offset into the tag storage. - /// The optional bit index for bit operations. + /// public virtual object? DecodeValueAt(AbCipDataType type, int offset, int? bitIndex) { if (ValuesByOffset.TryGetValue(offset, out var v)) return v; return offset == 0 ? Value : null; } - /// Encodes a value into the mock tag storage. - /// The data type being encoded. - /// The optional bit index for bit operations. - /// The value to encode. + /// public virtual void EncodeValue(AbCipDataType type, int? bitIndex, object? value) => Value = value; /// Marks the tag as disposed. @@ -106,8 +95,7 @@ internal sealed class FakeAbCipTagFactory : IAbCipTagFactory /// Gets or sets an optional customization function to override the tag creation. public Func? Customise { get; set; } - /// Creates a new fake tag and indexes it by name. - /// The tag creation parameters. + /// public IAbCipTagRuntime Create(AbCipTagCreateParams createParams) { var fake = Customise?.Invoke(createParams) ?? new FakeAbCipTag(createParams); diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests/AbLegacyReadSmokeTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests/AbLegacyReadSmokeTests.cs index 0aa29fc6..54443e3c 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests/AbLegacyReadSmokeTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests/AbLegacyReadSmokeTests.cs @@ -37,6 +37,7 @@ public sealed class AbLegacyReadSmokeTests(AbLegacyServerFixture sim) /// Verifies that the driver reads seeded N file from the AB server via PCCC. /// The AB Legacy server profile describing the fixture endpoint. + /// A task that represents the asynchronous operation. [AbLegacyTheory] [MemberData(nameof(Profiles))] public async Task Driver_reads_seeded_N_file_from_ab_server_PCCC(AbLegacyServerProfile profile) @@ -73,6 +74,7 @@ public sealed class AbLegacyReadSmokeTests(AbLegacyServerFixture sim) } /// Verifies that SLC500 write-then-read round trip succeeds on N7 scratch register. + /// A task that represents the asynchronous operation. [AbLegacyFact] public async Task Slc500_write_then_read_round_trip_on_N7_scratch_register() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests/AbLegacyServerFixture.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests/AbLegacyServerFixture.cs index 7faa0ca8..03452049 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests/AbLegacyServerFixture.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests/AbLegacyServerFixture.cs @@ -85,9 +85,11 @@ public sealed class AbLegacyServerFixture : IAsyncLifetime } /// Initializes the fixture asynchronously. + /// A completed value task. public ValueTask InitializeAsync() => ValueTask.CompletedTask; /// Disposes the fixture asynchronously. + /// A completed value task. public ValueTask DisposeAsync() => ValueTask.CompletedTask; /// @@ -96,6 +98,7 @@ public sealed class AbLegacyServerFixture : IAsyncLifetime /// fixture logic because attribute ctors fire before the collection fixture instance /// exists. /// + /// if the server endpoint is reachable; otherwise . public static bool IsServerAvailable() { var (host, port) = ResolveEndpoint(); @@ -169,6 +172,7 @@ public static class KnownProfiles /// Gets the profile for the specified PLC family. /// The PLC family. + /// The server profile for the specified family. public static AbLegacyServerProfile ForFamily(AbLegacyPlcFamily family) => All.FirstOrDefault(p => p.Family == family) ?? throw new ArgumentOutOfRangeException(nameof(family), family, "No integration profile for this family."); diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyBitIndexRangeTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyBitIndexRangeTests.cs index 3fde086d..1668dad5 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyBitIndexRangeTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyBitIndexRangeTests.cs @@ -68,6 +68,7 @@ public sealed class AbLegacyBitIndexRangeTests AbLegacyAddress.TryParse("N7:0/-1").ShouldBeNull(); /// Verifies that bit in word RMW against L file uses 32-bit parent and high bit. + /// A task that represents the asynchronous operation. [Fact] public async Task Bit_in_word_RMW_against_L_file_uses_32bit_parent_and_high_bit() { @@ -93,6 +94,7 @@ public sealed class AbLegacyBitIndexRangeTests } /// Verifies that bit in word RMW high bit 15 does not corrupt via sign extension. + /// A task that represents the asynchronous operation. [Fact] public async Task Bit_in_word_RMW_high_bit_15_does_not_corrupt_via_sign_extension() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyBitRmwTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyBitRmwTests.cs index b1ba8804..0ec16199 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyBitRmwTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyBitRmwTests.cs @@ -9,6 +9,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests; public sealed class AbLegacyBitRmwTests { /// Verifies that setting a bit reads the parent word, ORs the bit, and writes back. + /// A task that represents the asynchronous operation. [Fact] public async Task Bit_set_reads_parent_word_ORs_bit_writes_back() { @@ -33,6 +34,7 @@ public sealed class AbLegacyBitRmwTests } /// Verifies that clearing a bit preserves other bits in the word. + /// A task that represents the asynchronous operation. [Fact] public async Task Bit_clear_preserves_other_bits_in_N_file_word() { @@ -54,6 +56,7 @@ public sealed class AbLegacyBitRmwTests } /// Verifies that concurrent bit writes to the same word compose correctly. + /// A task that represents the asynchronous operation. [Fact] public async Task Concurrent_bit_writes_to_same_word_compose_correctly() { @@ -79,6 +82,7 @@ public sealed class AbLegacyBitRmwTests } /// Verifies that repeated bit writes reuse the parent word runtime. + /// A task that represents the asynchronous operation. [Fact] public async Task Repeat_bit_writes_reuse_parent_runtime() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyCapabilityTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyCapabilityTests.cs index d9737d32..5f19af07 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyCapabilityTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyCapabilityTests.cs @@ -12,6 +12,7 @@ public sealed class AbLegacyCapabilityTests // ---- ITagDiscovery ---- /// Verifies that DiscoverAsync emits pre-declared tags under the device folder. + /// A task that represents the asynchronous test operation. [Fact] public async Task DiscoverAsync_emits_pre_declared_tags_under_device_folder() { @@ -40,6 +41,7 @@ public sealed class AbLegacyCapabilityTests // ---- ISubscribable ---- /// Verifies that Subscribe initial poll raises OnDataChange. + /// A task that represents the asynchronous test operation. [Fact] public async Task Subscribe_initial_poll_raises_OnDataChange() { @@ -66,6 +68,7 @@ public sealed class AbLegacyCapabilityTests } /// Verifies that Unsubscribe halts polling. + /// A task that represents the asynchronous test operation. [Fact] public async Task Unsubscribe_halts_polling() { @@ -96,6 +99,7 @@ public sealed class AbLegacyCapabilityTests // ---- IHostConnectivityProbe ---- /// Verifies that GetHostStatuses returns one status per device. + /// A task that represents the asynchronous test operation. [Fact] public async Task GetHostStatuses_returns_one_per_device() { @@ -114,6 +118,7 @@ public sealed class AbLegacyCapabilityTests } /// Verifies that Probe transitions to Running on successful read. + /// A task that represents the asynchronous test operation. [Fact] public async Task Probe_transitions_to_Running_on_successful_read() { @@ -138,6 +143,7 @@ public sealed class AbLegacyCapabilityTests } /// Verifies that Probe transitions to Stopped on read failure. + /// A task that represents the asynchronous test operation. [Fact] public async Task Probe_transitions_to_Stopped_on_read_failure() { @@ -162,6 +168,7 @@ public sealed class AbLegacyCapabilityTests } /// Verifies that Probe is disabled when ProbeAddress is null. + /// A task that represents the asynchronous test operation. [Fact] public async Task Probe_disabled_when_ProbeAddress_is_null() { @@ -180,6 +187,7 @@ public sealed class AbLegacyCapabilityTests // ---- IPerCallHostResolver ---- /// Verifies that ResolveHost returns declared device for known tag. + /// A task that represents the asynchronous test operation. [Fact] public async Task ResolveHost_returns_declared_device_for_known_tag() { @@ -204,6 +212,7 @@ public sealed class AbLegacyCapabilityTests } /// Verifies that ResolveHost falls back to first device for unknown tags. + /// A task that represents the asynchronous test operation. [Fact] public async Task ResolveHost_falls_back_to_first_device_for_unknown() { @@ -218,6 +227,7 @@ public sealed class AbLegacyCapabilityTests } /// Verifies that ResolveHost falls back to DriverInstanceId when no devices exist. + /// A task that represents the asynchronous test operation. [Fact] public async Task ResolveHost_falls_back_to_DriverInstanceId_when_no_devices() { @@ -243,31 +253,22 @@ public sealed class AbLegacyCapabilityTests /// Gets list of variables created during discovery. public List<(string BrowseName, DriverAttributeInfo Info)> Variables { get; } = new(); - /// Records folder creation. - /// The browse name of the folder. - /// The display name of the folder. + /// public IAddressSpaceBuilder Folder(string browseName, string displayName) { Folders.Add((browseName, displayName)); return this; } - /// Records variable creation. - /// The browse name of the variable. - /// The display name of the variable. - /// The driver attribute information. + /// public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo info) { Variables.Add((browseName, info)); return new Handle(info.FullName); } - /// Records property addition (stub implementation). - /// The property name (unused). - /// The data type (unused). - /// The property value (unused). + /// public void AddProperty(string _, DriverDataType __, object? ___) { } private sealed class Handle(string fullRef) : IVariableHandle { - /// Gets the full reference of the variable. + /// public string FullReference => fullRef; - /// Marks the variable as an alarm condition. - /// The alarm condition information. + /// public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) => new NullSink(); } /// Null sink for alarm condition transitions. diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyDisposeAndResolveHostTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyDisposeAndResolveHostTests.cs index 1037ab08..294e5260 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyDisposeAndResolveHostTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyDisposeAndResolveHostTests.cs @@ -18,6 +18,7 @@ public sealed class AbLegacyDisposeAndResolveHostTests // ---- Driver.AbLegacy-011 ---- /// Verifies that Dispose performs teardown without blocking on async operations. + /// A task that represents the asynchronous test operation. [Fact] public async Task Dispose_runs_teardown_without_blocking_on_async_wait() { @@ -47,6 +48,7 @@ public sealed class AbLegacyDisposeAndResolveHostTests } /// Verifies that Dispose can be called multiple times without throwing. + /// A task that represents the asynchronous test operation. [Fact] public async Task Dispose_is_idempotent() { @@ -61,6 +63,7 @@ public sealed class AbLegacyDisposeAndResolveHostTests } /// Verifies that Dispose does not deadlock under a single-threaded synchronization context. + /// A task that represents the asynchronous test operation. [Fact] public async Task Dispose_under_single_threaded_sync_context_does_not_deadlock() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyDriverTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyDriverTests.cs index d684915c..600437d3 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyDriverTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyDriverTests.cs @@ -19,6 +19,7 @@ public sealed class AbLegacyDriverTests } /// Verifies that InitializeAsync with devices assigns family profiles. + /// A task that represents the asynchronous operation. [Fact] public async Task InitializeAsync_with_devices_assigns_family_profiles() { @@ -41,6 +42,7 @@ public sealed class AbLegacyDriverTests } /// Verifies that InitializeAsync with malformed host address faults. + /// A task that represents the asynchronous operation. [Fact] public async Task InitializeAsync_with_malformed_host_address_faults() { @@ -55,6 +57,7 @@ public sealed class AbLegacyDriverTests } /// Verifies that ShutdownAsync clears devices. + /// A task that represents the asynchronous operation. [Fact] public async Task ShutdownAsync_clears_devices() { @@ -115,6 +118,7 @@ public sealed class AbLegacyDriverTests // ---- Driver.AbLegacy-012: profile fields consumed ---- /// Verifies that EffectiveCipPath falls back to profile default when host path is empty. + /// A task that represents the asynchronous operation. [Fact] public async Task EffectiveCipPath_falls_back_to_profile_default_when_host_path_is_empty() { @@ -135,6 +139,7 @@ public sealed class AbLegacyDriverTests } /// Verifies that EffectiveCipPath preserves explicit host path. + /// A task that represents the asynchronous operation. [Fact] public async Task EffectiveCipPath_preserves_explicit_host_path() { @@ -154,6 +159,7 @@ public sealed class AbLegacyDriverTests } /// Verifies that long tag on MicroLogix device is rejected at initialization. + /// A task that represents the asynchronous operation. [Fact] public async Task Long_tag_on_MicroLogix_device_rejected_at_init() { @@ -170,6 +176,7 @@ public sealed class AbLegacyDriverTests } /// Verifies that long tag on SLC 500 device is accepted. + /// A task that represents the asynchronous operation. [Fact] public async Task Long_tag_on_Slc500_device_accepted() { @@ -186,6 +193,7 @@ public sealed class AbLegacyDriverTests } /// Verifies that string tag on PLC-5 device is rejected at initialization. + /// A task that represents the asynchronous operation. [Fact] public async Task String_tag_on_Plc5_device_rejected_at_init() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyLoggerInjectionTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyLoggerInjectionTests.cs index 2167da4d..2f65b3ed 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyLoggerInjectionTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyLoggerInjectionTests.cs @@ -19,13 +19,24 @@ public sealed class AbLegacyLoggerInjectionTests { public readonly List<(LogLevel Level, string Message)> Entries = new(); - /// + /// Begins a logical operation scope (returns a no-op scope). + /// The type of the state to associate with the scope. + /// The state identifier for the scope. + /// A no-op disposable scope. public IDisposable BeginScope(TState state) where TState : notnull => NullScope.Instance; - /// + /// Checks whether logging is enabled for the given level (always true). + /// The log level to check. + /// always. public bool IsEnabled(LogLevel logLevel) => true; - /// + /// Records a log entry into the captured entries list. + /// The type of the log state object. + /// The severity level of the log entry. + /// The event identifier for the log entry. + /// The state object associated with the log entry. + /// An optional exception to log. + /// A function that formats the state and exception into a message string. public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) => Entries.Add((logLevel, formatter(state, exception))); @@ -33,7 +44,7 @@ public sealed class AbLegacyLoggerInjectionTests { public static readonly NullScope Instance = new(); - /// + /// Disposes the no-op scope (no-op). public void Dispose() { } } } @@ -50,6 +61,7 @@ public sealed class AbLegacyLoggerInjectionTests } /// Verifies that driver initialization failure emits an error log. + /// A task that represents the asynchronous test operation. [Fact] public async Task InitializeAsync_failure_emits_error_log() { @@ -68,6 +80,7 @@ public sealed class AbLegacyLoggerInjectionTests } /// Verifies that the first non-zero libplctag status per device is logged. + /// A task that represents the asynchronous test operation. [Fact] public async Task First_nonzero_libplctag_status_per_device_is_logged() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyReadWriteTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyReadWriteTests.cs index 97b4c627..89670e74 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyReadWriteTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyReadWriteTests.cs @@ -23,6 +23,7 @@ public sealed class AbLegacyReadWriteTests // ---- Read ---- /// Verifies that an unknown reference maps to BadNodeIdUnknown. + /// A task that represents the asynchronous operation. [Fact] public async Task Unknown_reference_maps_to_BadNodeIdUnknown() { @@ -34,6 +35,7 @@ public sealed class AbLegacyReadWriteTests } /// Verifies that a successful N-file read returns a Good status. + /// A task that represents the asynchronous operation. [Fact] public async Task Successful_N_file_read_returns_Good_value() { @@ -51,6 +53,7 @@ public sealed class AbLegacyReadWriteTests } /// Verifies that repeated reads reuse the runtime. + /// A task that represents the asynchronous operation. [Fact] public async Task Repeat_read_reuses_runtime() { @@ -67,6 +70,7 @@ public sealed class AbLegacyReadWriteTests } /// Verifies that non-zero libplctag status values map via AbLegacyStatusMapper. + /// A task that represents the asynchronous operation. [Fact] public async Task NonZero_libplctag_status_maps_via_AbLegacyStatusMapper() { @@ -82,6 +86,7 @@ public sealed class AbLegacyReadWriteTests } /// Verifies that read exceptions surface as BadCommunicationError. + /// A task that represents the asynchronous operation. [Fact] public async Task Read_exception_surfaces_BadCommunicationError() { @@ -96,6 +101,7 @@ public sealed class AbLegacyReadWriteTests } /// Verifies that batched reads preserve order. + /// A task that represents the asynchronous operation. [Fact] public async Task Batched_reads_preserve_order() { @@ -120,6 +126,7 @@ public sealed class AbLegacyReadWriteTests } /// Verifies that read tag creation parameters are composed from device and profile. + /// A task that represents the asynchronous operation. [Fact] public async Task Read_TagCreateParams_composed_from_device_and_profile() { @@ -140,6 +147,7 @@ public sealed class AbLegacyReadWriteTests // ---- Write ---- /// Verifies that a non-writable tag rejects with BadNotWritable. + /// A task that represents the asynchronous operation. [Fact] public async Task Non_writable_tag_rejects_with_BadNotWritable() { @@ -153,6 +161,7 @@ public sealed class AbLegacyReadWriteTests } /// Verifies that a successful N-file write encodes and flushes the data. + /// A task that represents the asynchronous operation. [Fact] public async Task Successful_N_file_write_encodes_and_flushes() { @@ -169,6 +178,7 @@ public sealed class AbLegacyReadWriteTests } /// Verifies that bit-within-word write now succeeds via RMW. + /// A task that represents the asynchronous operation. [Fact] public async Task Bit_within_word_write_now_succeeds_via_RMW() { @@ -190,6 +200,7 @@ public sealed class AbLegacyReadWriteTests } /// Verifies that write exceptions surface as BadCommunicationError. + /// A task that represents the asynchronous operation. [Fact] public async Task Write_exception_surfaces_BadCommunicationError() { @@ -204,6 +215,7 @@ public sealed class AbLegacyReadWriteTests } /// Verifies that batch write preserves order across different outcomes. + /// A task that represents the asynchronous operation. [Fact] public async Task Batch_write_preserves_order_across_outcomes() { @@ -233,6 +245,7 @@ public sealed class AbLegacyReadWriteTests } /// Verifies that cancellation propagates through the driver. + /// A task that represents the asynchronous operation. [Fact] public async Task Cancellation_propagates() { @@ -250,6 +263,7 @@ public sealed class AbLegacyReadWriteTests } /// Verifies that ShutdownAsync disposes all runtimes. + /// A task that represents the asynchronous operation. [Fact] public async Task ShutdownAsync_disposes_runtimes() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyRuntimeConcurrencyTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyRuntimeConcurrencyTests.cs index e48c81d5..0aca1ef9 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyRuntimeConcurrencyTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyRuntimeConcurrencyTests.cs @@ -30,9 +30,7 @@ public sealed class AbLegacyRuntimeConcurrencyTests /// The tag creation parameters. public OverlapDetectingFake(AbLegacyTagCreateParams p) : base(p) { } - /// Reads the tag asynchronously while tracking concurrent operations. - /// The cancellation token. - /// A task representing the read operation. + /// public override async Task ReadAsync(CancellationToken ct) { EnterOp(); @@ -45,9 +43,7 @@ public sealed class AbLegacyRuntimeConcurrencyTests finally { LeaveOp(); } } - /// Writes to the tag asynchronously while tracking concurrent operations. - /// The cancellation token. - /// A task representing the write operation. + /// public override async Task WriteAsync(CancellationToken ct) { EnterOp(); @@ -69,6 +65,7 @@ public sealed class AbLegacyRuntimeConcurrencyTests } /// Verifies that concurrent reads of the same tag are serialised against the shared runtime. + /// A task that represents the asynchronous operation. [Fact] public async Task Concurrent_reads_of_same_tag_are_serialised_against_the_shared_runtime() { @@ -102,6 +99,7 @@ public sealed class AbLegacyRuntimeConcurrencyTests } /// Verifies that concurrent read and write operations on the same tag do not overlap. + /// A task that represents the asynchronous operation. [Fact] public async Task Concurrent_read_and_write_of_same_tag_do_not_overlap() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/FakeAbLegacyTag.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/FakeAbLegacyTag.cs index 42528da7..cd3dd8a7 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/FakeAbLegacyTag.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/FakeAbLegacyTag.cs @@ -41,9 +41,7 @@ internal class FakeAbLegacyTag : IAbLegacyTagRuntime /// The tag creation parameters. public FakeAbLegacyTag(AbLegacyTagCreateParams p) => CreationParams = p; - /// Initializes the tag asynchronously. - /// The cancellation token. - /// A task representing the asynchronous operation. + /// public virtual Task InitializeAsync(CancellationToken ct) { InitializeCount++; @@ -51,9 +49,7 @@ internal class FakeAbLegacyTag : IAbLegacyTagRuntime return Task.CompletedTask; } - /// Reads the tag value asynchronously. - /// The cancellation token. - /// A task representing the asynchronous operation. + /// public virtual Task ReadAsync(CancellationToken ct) { ReadCount++; @@ -61,9 +57,7 @@ internal class FakeAbLegacyTag : IAbLegacyTagRuntime return Task.CompletedTask; } - /// Writes the tag value asynchronously. - /// The cancellation token. - /// A task representing the asynchronous operation. + /// public virtual Task WriteAsync(CancellationToken ct) { WriteCount++; @@ -71,20 +65,13 @@ internal class FakeAbLegacyTag : IAbLegacyTagRuntime return Task.CompletedTask; } - /// Gets the current tag status. - /// The status code. + /// public virtual int GetStatus() => Status; - /// Decodes the tag value based on the specified data type and bit index. - /// The AbLegacy data type. - /// The bit index if applicable. - /// The decoded value. + /// public virtual object? DecodeValue(AbLegacyDataType type, int? bitIndex) => Value; - /// Encodes the tag value based on the specified data type and bit index. - /// The AbLegacy data type. - /// The bit index if applicable. - /// The value to encode. + /// public virtual void EncodeValue(AbLegacyDataType type, int? bitIndex, object? value) => Value = value; /// Disposes the tag. @@ -100,9 +87,7 @@ internal sealed class FakeAbLegacyTagFactory : IAbLegacyTagFactory /// Gets or sets an optional customization function for tag creation. public Func? Customise { get; set; } - /// Creates a new AbLegacy tag with the specified parameters. - /// The tag creation parameters. - /// The created tag. + /// public IAbLegacyTagRuntime Create(AbLegacyTagCreateParams p) { var fake = Customise?.Invoke(p) ?? new FakeAbLegacyTag(p); diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/FocasSimFixture.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/FocasSimFixture.cs index d53875b0..99a6e614 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/FocasSimFixture.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/FocasSimFixture.cs @@ -74,6 +74,7 @@ public sealed class FocasSimFixture : IAsyncDisposable } /// Disposes the fixture and releases any held resources. + /// A task that represents the asynchronous operation. public ValueTask DisposeAsync() => ValueTask.CompletedTask; // ---- Admin API helpers ---- @@ -85,23 +86,27 @@ public sealed class FocasSimFixture : IAsyncDisposable /// /// The DLL-stem name or OtOpcUa-style alias of the profile to load. /// Cancellation token for the operation. + /// A task that resolves to the JSON response from the admin endpoint. public Task LoadProfileAsync(string profileName, CancellationToken ct = default) => SendAdminAsync("mock_load_profile", new { profile = profileName }, ct); /// Deep-merge into the mock's current state. /// The state object to deep-merge into the mock's current state. /// Cancellation token for the operation. + /// A task that resolves to the JSON response from the admin endpoint. public Task PatchStateAsync(object state, CancellationToken ct = default) => SendAdminAsync("mock_patch", new { state }, ct); /// Reset the mock to the selected profile's default state. /// Cancellation token for the operation. + /// A task that resolves to the JSON response from the admin endpoint. public Task ResetAsync(CancellationToken ct = default) => SendAdminAsync("mock_reset", new { }, ct); /// Install a time-scheduled alarm raise / clear sequence. /// The alarm sequence events to schedule. /// Cancellation token for the operation. + /// A task that resolves to the JSON response from the admin endpoint. public Task ScheduleAlarmsAsync(IEnumerable sequence, CancellationToken ct = default) => SendAdminAsync("mock_schedule_alarms", new { sequence }, ct); @@ -110,6 +115,7 @@ public sealed class FocasSimFixture : IAsyncDisposable /// The admin method name to invoke. /// The parameters object to send with the request. /// Cancellation token for the operation. + /// A task that resolves to the JSON response from the admin endpoint. public async Task SendAdminAsync(string method, object @params, CancellationToken ct = default) { using var client = new TcpClient(); diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/Series/WireBackendCoverageTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/Series/WireBackendCoverageTests.cs index b45b89f5..0555a339 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/Series/WireBackendCoverageTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/Series/WireBackendCoverageTests.cs @@ -26,6 +26,7 @@ public sealed class WireBackendCoverageTests private const string DeviceHost = "focas://127.0.0.1:8193"; /// Verifies that user tag reads route via the wire backend. + /// A task that represents the asynchronous test operation. [Fact] public async Task User_tag_reads_route_via_wire_backend() { @@ -73,6 +74,7 @@ public sealed class WireBackendCoverageTests } /// Verifies that discover emits device folder and tag variables. + /// A task that represents the asynchronous test operation. [Fact] public async Task Discover_emits_device_folder_and_tag_variables() { @@ -107,6 +109,7 @@ public sealed class WireBackendCoverageTests } /// Verifies that subscribe fires OnDataChange via the wire backend. + /// A task that represents the asynchronous test operation. [Fact] public async Task Subscribe_fires_OnDataChange_via_wire_backend() { @@ -157,6 +160,7 @@ public sealed class WireBackendCoverageTests } /// Verifies that alarm raise then clear emits both events via the wire backend. + /// A task that represents the asynchronous test operation. [Fact] public async Task Alarm_raise_then_clear_emits_both_events_via_wire_backend() { @@ -210,6 +214,7 @@ public sealed class WireBackendCoverageTests } /// Verifies that the probe transitions to Running against the live mock. + /// A task that represents the asynchronous test operation. [Fact] public async Task Probe_transitions_to_Running_against_live_mock() { @@ -255,39 +260,29 @@ public sealed class WireBackendCoverageTests /// Gets the list of recorded variables. public List<(string BrowseName, DriverAttributeInfo Info)> Variables { get; } = new(); - /// Records a folder in the address space builder. - /// The browse name for the folder. - /// The display name for the folder. + /// public IAddressSpaceBuilder Folder(string browseName, string displayName) { Folders.Add((browseName, displayName)); return this; } - /// Records a variable in the address space builder. - /// The browse name for the variable. - /// The display name for the variable. - /// The driver attribute information. + /// public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo info) { Variables.Add((browseName, info)); return new Handle(info.FullName); } - /// Records an address space property (no-op in this builder). - /// The property name. - /// The property data type. - /// The property value. + /// public void AddProperty(string _, DriverDataType __, object? ___) { } private sealed class Handle(string fullRef) : IVariableHandle { - /// Gets the full OPC UA reference for the variable. + /// public string FullReference => fullRef; - /// Marks the variable as an alarm condition and returns a sink. - /// The alarm condition information. + /// public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) => new NullSink(); } private sealed class NullSink : IAlarmConditionSink { - /// Handles an alarm transition event (no-op in this sink). - /// The alarm event arguments. + /// public void OnTransition(AlarmEventArgs args) { } } } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/Series/WireBackendTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/Series/WireBackendTests.cs index 36b48d9c..f8aaf8e7 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/Series/WireBackendTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/Series/WireBackendTests.cs @@ -29,6 +29,7 @@ public sealed class WireBackendTests private const string DeviceHost = "focas://127.0.0.1:8193"; /// Verifies that identity axes and dynamic data populate via the wire backend. + /// A task that represents the asynchronous operation. [Fact] public async Task Identity_axes_and_dynamic_populate_via_wire_backend() { @@ -86,6 +87,7 @@ public sealed class WireBackendTests } /// Verifies that program and operation mode data populate via the wire backend. + /// A task that represents the asynchronous operation. [Fact] public async Task Program_and_operation_mode_populate_via_wire_backend() { @@ -151,6 +153,7 @@ public sealed class WireBackendTests } /// Verifies that timer data populates via the wire backend. + /// A task that represents the asynchronous operation. [Fact] public async Task Timers_populate_via_wire_backend() { @@ -209,6 +212,7 @@ public sealed class WireBackendTests } /// Verifies that spindle load and max RPM data populate via the wire backend. + /// A task that represents the asynchronous operation. [Fact] public async Task Spindle_load_and_max_rpm_populate_via_wire_backend() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FakeFocasClient.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FakeFocasClient.cs index d5d61351..8126bba0 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FakeFocasClient.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FakeFocasClient.cs @@ -4,7 +4,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests; internal class FakeFocasClient : IFocasClient { - /// Gets a value indicating whether the client is connected. + /// public bool IsConnected { get; private set; } /// Gets the count of connection attempts. public int ConnectCount { get; private set; } @@ -30,10 +30,7 @@ internal class FakeFocasClient : IFocasClient /// Gets the log of write operations. public List<(FocasAddress addr, FocasDataType type, object? value)> WriteLog { get; } = new(); - /// Connects to a FOCAS host asynchronously. - /// The FOCAS host address. - /// The connection timeout duration. - /// The cancellation token. + /// public virtual Task ConnectAsync(FocasHostAddress address, TimeSpan timeout, CancellationToken ct) { ConnectCount++; @@ -42,10 +39,7 @@ internal class FakeFocasClient : IFocasClient return Task.CompletedTask; } - /// Reads a value from a FOCAS address asynchronously. - /// The FOCAS address to read from. - /// The data type of the value. - /// The cancellation token. + /// public virtual Task<(object? value, uint status)> ReadAsync( FocasAddress address, FocasDataType type, CancellationToken ct) { @@ -56,11 +50,7 @@ internal class FakeFocasClient : IFocasClient return Task.FromResult((value, status)); } - /// Writes a value to a FOCAS address asynchronously. - /// The FOCAS address to write to. - /// The data type of the value. - /// The value to write. - /// The cancellation token. + /// public virtual Task WriteAsync( FocasAddress address, FocasDataType type, object? value, CancellationToken ct) { @@ -71,15 +61,13 @@ internal class FakeFocasClient : IFocasClient return Task.FromResult(status); } - /// Probes the FOCAS connection asynchronously. - /// The cancellation token. + /// public virtual Task ProbeAsync(CancellationToken ct) => Task.FromResult(ProbeResult); /// Gets the list of active alarms. public List Alarms { get; } = []; - /// Reads active alarms asynchronously. - /// The cancellation token. + /// public virtual Task> ReadAlarmsAsync(CancellationToken ct) => Task.FromResult>([.. Alarms]); @@ -93,20 +81,15 @@ internal class FakeFocasClient : IFocasClient /// Gets the dictionary of dynamic snapshots keyed by axis index. public Dictionary DynamicByAxis { get; } = []; - /// Gets system information asynchronously. - /// The cancellation token. + /// public virtual Task GetSysInfoAsync(CancellationToken ct) => Task.FromResult(SysInfo); - /// Gets axis names asynchronously. - /// The cancellation token. + /// public virtual Task> GetAxisNamesAsync(CancellationToken ct) => Task.FromResult>([.. AxisNames]); - /// Gets spindle names asynchronously. - /// The cancellation token. + /// public virtual Task> GetSpindleNamesAsync(CancellationToken ct) => Task.FromResult>([.. SpindleNames]); - /// Reads dynamic data for an axis asynchronously. - /// The zero-based axis index. - /// The cancellation token. + /// public virtual Task ReadDynamicAsync(int axisIndex, CancellationToken ct) { if (!DynamicByAxis.TryGetValue(axisIndex, out var snap)) @@ -116,16 +99,13 @@ internal class FakeFocasClient : IFocasClient /// Gets or sets the program information. public FocasProgramInfo ProgramInfo { get; set; } = new("O0001", 1, 0, 1); - /// Gets program information asynchronously. - /// The cancellation token. + /// public virtual Task GetProgramInfoAsync(CancellationToken ct) => Task.FromResult(ProgramInfo); /// Gets the dictionary of timers keyed by timer kind. public Dictionary Timers { get; } = []; - /// Gets timer data asynchronously. - /// The timer kind to retrieve. - /// The cancellation token. + /// public virtual Task GetTimerAsync(FocasTimerKind kind, CancellationToken ct) { if (!Timers.TryGetValue(kind, out var t)) @@ -135,8 +115,7 @@ internal class FakeFocasClient : IFocasClient /// Gets the list of servo loads. public List ServoLoads { get; } = []; - /// Gets servo loads asynchronously. - /// The cancellation token. + /// public virtual Task> GetServoLoadsAsync(CancellationToken ct) => Task.FromResult>([.. ServoLoads]); @@ -144,12 +123,10 @@ internal class FakeFocasClient : IFocasClient public List SpindleLoads { get; } = []; /// Gets the list of spindle maximum RPMs. public List SpindleMaxRpms { get; } = []; - /// Gets spindle loads asynchronously. - /// The cancellation token. + /// public virtual Task> GetSpindleLoadsAsync(CancellationToken ct) => Task.FromResult>([.. SpindleLoads]); - /// Gets spindle maximum RPMs asynchronously. - /// The cancellation token. + /// public virtual Task> GetSpindleMaxRpmsAsync(CancellationToken ct) => Task.FromResult>([.. SpindleMaxRpms]); @@ -169,7 +146,7 @@ internal sealed class FakeFocasClientFactory : IFocasClientFactory /// Gets or sets a customization function for creating clients. public Func? Customise { get; set; } - /// Creates a fake FOCAS client. + /// public IFocasClient Create() { var c = Customise?.Invoke() ?? new FakeFocasClient(); diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasAlarmProjectionTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasAlarmProjectionTests.cs index 712d7470..bc0dacde 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasAlarmProjectionTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasAlarmProjectionTests.cs @@ -27,6 +27,7 @@ public sealed class FocasAlarmProjectionTests } /// Verifies that subscribe without enable throws NotSupported. + /// A task that represents the asynchronous operation. [Fact] public async Task Subscribe_without_Enable_throws_NotSupported() { @@ -38,6 +39,7 @@ public sealed class FocasAlarmProjectionTests } /// Verifies that raise then clear emits both events. + /// A task that represents the asynchronous operation. [Fact] public async Task Raise_then_clear_emits_both_events() { @@ -70,6 +72,7 @@ public sealed class FocasAlarmProjectionTests } /// Verifies that tick diffs raises and clears without polling loop. + /// A task that represents the asynchronous operation. [Fact] public async Task Tick_diffs_raises_and_clears_without_polling_loop() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasCapabilityTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasCapabilityTests.cs index 2b6597d0..31e15adf 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasCapabilityTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasCapabilityTests.cs @@ -12,6 +12,7 @@ public sealed class FocasCapabilityTests // ---- ITagDiscovery ---- /// Verifies that DiscoverAsync emits pre-declared tags. + /// A task that represents the asynchronous operation. [Fact] public async Task DiscoverAsync_emits_pre_declared_tags() { @@ -41,6 +42,7 @@ public sealed class FocasCapabilityTests // ---- ISubscribable ---- /// Verifies that the initial subscription poll raises an OnDataChange event. + /// A task that represents the asynchronous operation. [Fact] public async Task Subscribe_initial_poll_raises_OnDataChange() { @@ -67,6 +69,7 @@ public sealed class FocasCapabilityTests } /// Verifies that ShutdownAsync cancels active subscriptions. + /// A task that represents the asynchronous operation. [Fact] public async Task ShutdownAsync_cancels_active_subscriptions() { @@ -97,6 +100,7 @@ public sealed class FocasCapabilityTests // ---- IHostConnectivityProbe ---- /// Verifies that GetHostStatuses returns one entry per device. + /// A task that represents the asynchronous operation. [Fact] public async Task GetHostStatuses_returns_entry_per_device() { @@ -115,6 +119,7 @@ public sealed class FocasCapabilityTests } /// Verifies that the probe transitions to Running on successful connection. + /// A task that represents the asynchronous operation. [Fact] public async Task Probe_transitions_to_Running_on_success() { @@ -142,6 +147,7 @@ public sealed class FocasCapabilityTests } /// Verifies that the probe transitions to Stopped on connection failure. + /// A task that represents the asynchronous operation. [Fact] public async Task Probe_transitions_to_Stopped_on_failure() { @@ -171,6 +177,7 @@ public sealed class FocasCapabilityTests // ---- IPerCallHostResolver ---- /// Verifies that ResolveHost returns the declared device for a known tag. + /// A task that represents the asynchronous operation. [Fact] public async Task ResolveHost_returns_declared_device_for_known_tag() { @@ -195,6 +202,7 @@ public sealed class FocasCapabilityTests } /// Verifies that ResolveHost falls back to the first device for unknown tags. + /// A task that represents the asynchronous operation. [Fact] public async Task ResolveHost_falls_back_to_first_device_for_unknown() { @@ -209,6 +217,7 @@ public sealed class FocasCapabilityTests } /// Verifies that ResolveHost falls back to the driver instance ID when no devices are configured. + /// A task that represents the asynchronous operation. [Fact] public async Task ResolveHost_falls_back_to_DriverInstanceId_when_no_devices() { @@ -235,40 +244,27 @@ public sealed class FocasCapabilityTests /// Gets the list of recorded variable calls. public List<(string BrowseName, DriverAttributeInfo Info)> Variables { get; } = new(); - /// Records a folder call. - /// The browse name of the folder. - /// The display name of the folder. - /// This builder for chaining. + /// public IAddressSpaceBuilder Folder(string browseName, string displayName) { Folders.Add((browseName, displayName)); return this; } - /// Records a variable call. - /// The browse name of the variable. - /// The display name of the variable. - /// The driver attribute information. - /// A variable handle for the recorded variable. + /// public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo info) { Variables.Add((browseName, info)); return new Handle(info.FullName); } - /// Records a property call (no-op). - /// The property name (unused). - /// The property data type (unused). - /// The property value (unused). + /// public void AddProperty(string _, DriverDataType __, object? ___) { } private sealed class Handle(string fullRef) : IVariableHandle { - /// Gets the full reference. + /// public string FullReference => fullRef; - /// Marks as alarm condition. - /// The alarm condition information. - /// An alarm condition sink. + /// public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) => new NullSink(); } /// Null alarm condition sink. private sealed class NullSink : IAlarmConditionSink { - /// Handles transition (no-op). - /// The alarm event arguments (unused). + /// public void OnTransition(AlarmEventArgs args) { } } } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasDriverMediumFindingsTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasDriverMediumFindingsTests.cs index 593a4712..47c68efc 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasDriverMediumFindingsTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasDriverMediumFindingsTests.cs @@ -21,6 +21,7 @@ public sealed class FocasDriverMediumFindingsTests // ---- Driver.FOCAS-003: unknown DeviceHostAddress fails at InitializeAsync ---- /// Verifies that initialization throws when a tag references an undeclared device. + /// A task that represents the asynchronous test operation. [Fact] public async Task InitializeAsync_throws_when_tag_DeviceHostAddress_not_in_Devices() { @@ -43,6 +44,7 @@ public sealed class FocasDriverMediumFindingsTests } /// Verifies that initialization errors name the offending tag. + /// A task that represents the asynchronous test operation. [Fact] public async Task InitializeAsync_throws_naming_the_offending_tag() { @@ -64,6 +66,7 @@ public sealed class FocasDriverMediumFindingsTests } /// Verifies that initialization succeeds when all tags reference declared devices. + /// A task that represents the asynchronous test operation. [Fact] public async Task InitializeAsync_succeeds_when_all_tags_reference_declared_devices() { @@ -90,6 +93,7 @@ public sealed class FocasDriverMediumFindingsTests // ---- Driver.FOCAS-004: all FOCAS user tags advertised as ViewOnly ---- /// Verifies that all user tags are advertised as ViewOnly regardless of Writable setting. + /// A task that represents the asynchronous test operation. [Fact] public async Task DiscoverAsync_all_user_tags_are_ViewOnly_regardless_of_Writable_field() { @@ -119,6 +123,7 @@ public sealed class FocasDriverMediumFindingsTests // ---- Driver.FOCAS-005: Volatile-guarded _health survives concurrent reads ---- /// Verifies that GetHealth reflects state updated from concurrent reads. + /// A task that represents the asynchronous test operation. [Fact] public async Task GetHealth_reflects_state_updated_from_concurrent_reads() { @@ -148,6 +153,7 @@ public sealed class FocasDriverMediumFindingsTests // ---- Driver.FOCAS-006: EnsureConnectedAsync recreates a disposed/stale client ---- /// Verifies that reads recover after client is externally disposed. + /// A task that represents the asynchronous test operation. [Fact] public async Task Read_recovers_after_client_is_externally_disposed() { @@ -184,6 +190,7 @@ public sealed class FocasDriverMediumFindingsTests } /// Verifies that reads dispose stale clients before creating fresh ones. + /// A task that represents the asynchronous test operation. [Fact] public async Task Read_disposes_stale_client_before_creating_fresh_one() { @@ -257,41 +264,31 @@ public sealed class FocasDriverMediumFindingsTests /// Gets or sets the list of added folders. public List<(string BrowseName, string DisplayName)> Folders { get; } = new(); - /// Records a folder and returns this builder for chaining. - /// The OPC UA browse name for the folder. - /// The display name for the folder. + /// public IAddressSpaceBuilder Folder(string browseName, string displayName) { Folders.Add((browseName, displayName)); return this; } - /// Records a variable and returns a handle for it. - /// The OPC UA browse name for the variable. - /// The display name for the variable. - /// The driver attribute information for the variable. + /// public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo info) { Variables.Add((browseName, info)); return new Handle(info.FullName); } - /// No-op property addition for test compatibility. - /// The property name (unused). - /// The property data type (unused). - /// The property value (unused). + /// public void AddProperty(string _, DriverDataType __, object? ___) { } /// Test variable handle implementation. private sealed class Handle(string fullRef) : IVariableHandle { - /// Gets the full reference path of this variable. + /// public string FullReference => fullRef; - /// Marks this variable as an alarm condition and returns a sink for it. - /// The alarm condition information. + /// public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) => new NullSink(); } /// No-op alarm condition sink for testing. private sealed class NullSink : IAlarmConditionSink { - /// Handles alarm condition transitions (no-op for testing). - /// The alarm event arguments. + /// public void OnTransition(AlarmEventArgs args) { } } } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasFactoryConfigTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasFactoryConfigTests.cs index 94531969..423dd456 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasFactoryConfigTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasFactoryConfigTests.cs @@ -119,6 +119,7 @@ public sealed class FocasFactoryConfigTests // ---- Driver.FOCAS-002: fixed-tree bootstrap must not declare a false ProgramInfo capability ---- /// Verifies that ProgramInfo is marked unsupported when probe throws. + /// A task that represents the asynchronous operation. [Fact] public async Task FixedTree_bootstrap_marks_ProgramInfo_unsupported_when_probe_throws() { @@ -146,6 +147,7 @@ public sealed class FocasFactoryConfigTests } /// Verifies that ProgramInfo is marked supported when probe succeeds. + /// A task that represents the asynchronous operation. [Fact] public async Task FixedTree_bootstrap_marks_ProgramInfo_supported_when_probe_succeeds() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasHandleRecycleTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasHandleRecycleTests.cs index ffb64f10..d67c8932 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasHandleRecycleTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasHandleRecycleTests.cs @@ -7,6 +7,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests; public sealed class FocasHandleRecycleTests { /// Verifies that the recycle loop disposes clients on interval and reopens fresh ones. + /// A task that represents the asynchronous test operation. [Fact] public async Task Recycle_loop_disposes_client_on_interval_reads_reopen_fresh_one() { @@ -40,6 +41,7 @@ public sealed class FocasHandleRecycleTests } /// Verifies that the recycle loop stays off when not enabled. + /// A task that represents the asynchronous test operation. [Fact] public async Task Recycle_loop_stays_off_when_not_enabled() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasLoggingTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasLoggingTests.cs index 44483c14..6aa7a520 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasLoggingTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasLoggingTests.cs @@ -31,6 +31,7 @@ public sealed class FocasLoggingTests } /// Verifies that probe loop logs when an exception is swallowed. + /// A task that represents the asynchronous operation. [Fact] public async Task ProbeLoop_logs_when_an_exception_is_swallowed() { @@ -90,9 +91,11 @@ public sealed class FocasLoggingTests /// Begins a logging scope. /// The scope state. /// The type of the state. + /// A disposable scope object. public IDisposable BeginScope(TState state) where TState : notnull => NullScope.Instance; /// Checks if logging is enabled for the specified level. /// The log level. + /// true if logging is enabled for ; otherwise, false. public bool IsEnabled(LogLevel logLevel) => true; /// Logs a message. /// The log level. diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasLowFindingsTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasLowFindingsTests.cs index 670c8ec1..c0c41535 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasLowFindingsTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasLowFindingsTests.cs @@ -23,6 +23,7 @@ public sealed class FocasLowFindingsTests /// /// Verifies that ReadAsync uses cached FocasAddress when tag definition has a malformed address after init. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ReadAsync_uses_cached_FocasAddress_when_tag_definition_has_a_malformed_address_after_init() { @@ -59,6 +60,7 @@ public sealed class FocasLowFindingsTests /// /// Verifies that WriteAsync also uses cached FocasAddress. /// + /// A task that represents the asynchronous operation. [Fact] public async Task WriteAsync_uses_cached_FocasAddress_too() { @@ -99,6 +101,7 @@ public sealed class FocasLowFindingsTests /// /// Verifies that ProbeLoop cancels a slow ProbeAsync at Probe Timeout. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ProbeLoop_cancels_a_slow_ProbeAsync_at_Probe_Timeout() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasPmcBitRmwTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasPmcBitRmwTests.cs index edd897f2..32e19e74 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasPmcBitRmwTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasPmcBitRmwTests.cs @@ -66,6 +66,7 @@ public sealed class FocasPmcBitRmwTests } /// Verifies that a bit set operation surfaces as Good status and flips the bit. + /// A task that represents the asynchronous operation. [Fact] public async Task Bit_set_surfaces_as_Good_status_and_flips_bit() { @@ -81,6 +82,7 @@ public sealed class FocasPmcBitRmwTests } /// Verifies that clearing a bit preserves other bits. + /// A task that represents the asynchronous operation. [Fact] public async Task Bit_clear_preserves_other_bits() { @@ -95,6 +97,7 @@ public sealed class FocasPmcBitRmwTests } /// Verifies that subsequent bit sets in the same byte compose correctly. + /// A task that represents the asynchronous operation. [Fact] public async Task Subsequent_bit_sets_in_same_byte_compose_correctly() { @@ -112,6 +115,7 @@ public sealed class FocasPmcBitRmwTests } /// Verifies that bit writes to different bytes do not contend. + /// A task that represents the asynchronous operation. [Fact] public async Task Bit_write_to_different_bytes_does_not_contend() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasReadWriteTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasReadWriteTests.cs index 0310ade5..f6674901 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasReadWriteTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasReadWriteTests.cs @@ -23,6 +23,7 @@ public sealed class FocasReadWriteTests // ---- Read ---- /// Verifies that an unknown reference maps to BadNodeIdUnknown. + /// A task that represents the asynchronous test operation. [Fact] public async Task Unknown_reference_maps_to_BadNodeIdUnknown() { @@ -34,6 +35,7 @@ public sealed class FocasReadWriteTests } /// Verifies that a successful PMC read returns a Good status value. + /// A task that represents the asynchronous test operation. [Fact] public async Task Successful_PMC_read_returns_Good_value() { @@ -48,6 +50,7 @@ public sealed class FocasReadWriteTests } /// Verifies that parameter reads route through the FocasAddress Parameter kind. + /// A task that represents the asynchronous test operation. [Fact] public async Task Parameter_read_routes_through_FocasAddress_Parameter_kind() { @@ -62,6 +65,7 @@ public sealed class FocasReadWriteTests } /// Verifies that macro reads route through the FocasAddress Macro kind. + /// A task that represents the asynchronous test operation. [Fact] public async Task Macro_read_routes_through_FocasAddress_Macro_kind() { @@ -75,6 +79,7 @@ public sealed class FocasReadWriteTests } /// Verifies that repeated reads reuse the connection. + /// A task that represents the asynchronous test operation. [Fact] public async Task Repeat_read_reuses_connection() { @@ -91,6 +96,7 @@ public sealed class FocasReadWriteTests } /// Verifies that FOCAS error statuses map correctly via the status mapper. + /// A task that represents the asynchronous test operation. [Fact] public async Task FOCAS_error_status_maps_via_status_mapper() { @@ -109,6 +115,7 @@ public sealed class FocasReadWriteTests } /// Verifies that a read exception surfaces BadCommunicationError. + /// A task that represents the asynchronous test operation. [Fact] public async Task Read_exception_surfaces_BadCommunicationError() { @@ -123,6 +130,7 @@ public sealed class FocasReadWriteTests } /// Verifies that a connection failure disposes the client and surfaces BadCommunicationError. + /// A task that represents the asynchronous test operation. [Fact] public async Task Connect_failure_disposes_client_and_surfaces_BadCommunicationError() { @@ -137,6 +145,7 @@ public sealed class FocasReadWriteTests } /// Verifies that batched reads preserve order across different address areas. + /// A task that represents the asynchronous test operation. [Fact] public async Task Batched_reads_preserve_order_across_areas() { @@ -164,6 +173,7 @@ public sealed class FocasReadWriteTests // ---- Write ---- /// Verifies that a non-writable tag write is rejected with BadNotWritable. + /// A task that represents the asynchronous test operation. [Fact] public async Task Non_writable_tag_rejected_with_BadNotWritable() { @@ -177,6 +187,7 @@ public sealed class FocasReadWriteTests } /// Verifies that a successful write logs the address, type, and value. + /// A task that represents the asynchronous test operation. [Fact] public async Task Successful_write_logs_address_type_value() { @@ -195,6 +206,7 @@ public sealed class FocasReadWriteTests } /// Verifies that write status codes map correctly via the FocasStatusMapper. + /// A task that represents the asynchronous test operation. [Fact] public async Task Write_status_code_maps_via_FocasStatusMapper() { @@ -214,6 +226,7 @@ public sealed class FocasReadWriteTests } /// Verifies that batched writes preserve order across different outcomes. + /// A task that represents the asynchronous test operation. [Fact] public async Task Batch_write_preserves_order_across_outcomes() { @@ -243,6 +256,7 @@ public sealed class FocasReadWriteTests } /// Verifies that cancellation signals are propagated. + /// A task that represents the asynchronous test operation. [Fact] public async Task Cancellation_propagates() { @@ -260,6 +274,7 @@ public sealed class FocasReadWriteTests } /// Verifies that ShutdownAsync disposes the client. + /// A task that represents the asynchronous test operation. [Fact] public async Task ShutdownAsync_disposes_client() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasScaffoldingTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasScaffoldingTests.cs index f1ba244d..e53893bd 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasScaffoldingTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/FocasScaffoldingTests.cs @@ -201,6 +201,7 @@ public sealed class FocasScaffoldingTests } /// Verifies InitializeAsync parses device addresses correctly. + /// A task that represents the asynchronous operation. [Fact] public async Task InitializeAsync_parses_device_addresses() { @@ -221,6 +222,7 @@ public sealed class FocasScaffoldingTests } /// Verifies InitializeAsync faults on malformed addresses. + /// A task that represents the asynchronous operation. [Fact] public async Task InitializeAsync_malformed_address_faults() { @@ -235,6 +237,7 @@ public sealed class FocasScaffoldingTests } /// Verifies ShutdownAsync clears all devices. + /// A task that represents the asynchronous operation. [Fact] public async Task ShutdownAsync_clears_devices() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser.Tests/GalaxyBrowseSessionTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser.Tests/GalaxyBrowseSessionTests.cs index a2c69819..8c32fefd 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser.Tests/GalaxyBrowseSessionTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser.Tests/GalaxyBrowseSessionTests.cs @@ -51,6 +51,7 @@ public sealed class GalaxyBrowseSessionTests /// Each session must publish a distinct /// so the AdminUI registry can disambiguate concurrent browse sessions against the /// same driver config. + /// A task that represents the asynchronous operation. [Fact] public async Task Token_is_unique_per_session() { @@ -63,6 +64,7 @@ public sealed class GalaxyBrowseSessionTests /// is primed to the /// construction time so the registry reaper has a sensible baseline before the /// first Root/Expand/Attributes call lands. + /// A task that represents the asynchronous operation. [Fact] public async Task LastUsedUtc_is_initialized_at_construction() { @@ -79,6 +81,7 @@ public sealed class GalaxyBrowseSessionTests /// registry's reaper may race a client-initiated close, so the second call must /// no-op rather than throw or hit the /// already-disposed gRPC channel. + /// A task that represents the asynchronous operation. [Fact] public async Task DisposeAsync_is_idempotent() { @@ -91,6 +94,7 @@ public sealed class GalaxyBrowseSessionTests /// After disposal, any call /// must surface — not a downstream channel /// fault — so the AdminUI sees a clean "session closed" signal. + /// A task that represents the asynchronous operation. [Fact] public async Task ExpandAsync_after_dispose_throws_ObjectDisposedException() { @@ -104,6 +108,7 @@ public sealed class GalaxyBrowseSessionTests /// hasn't been seen by a prior Root/Expand call — the cache is the source of /// truth, and silently returning [] would mask AdminUI bugs that browse with a /// stale path. + /// A task that represents the asynchronous operation. [Fact] public async Task ExpandAsync_unknown_tag_throws_ArgumentException() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser.Tests/GalaxyDriverBrowserTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser.Tests/GalaxyDriverBrowserTests.cs index daf6bb61..4bdae4d8 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser.Tests/GalaxyDriverBrowserTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browser.Tests/GalaxyDriverBrowserTests.cs @@ -23,6 +23,7 @@ public sealed class GalaxyDriverBrowserTests /// An empty Gateway.Endpoint must fail fast with a clear, endpoint-mentioning /// message rather than surfacing a downstream gRPC URI parse error. + /// A task that represents the asynchronous test operation. [Fact] public async Task OpenAsync_with_empty_endpoint_throws_InvalidOperationException() { @@ -34,6 +35,7 @@ public sealed class GalaxyDriverBrowserTests /// An empty MxAccess.ClientName must fail fast — refused so the gateway /// side doesn't see anonymous browse sessions during triage. + /// A task that represents the asynchronous test operation. [Fact] public async Task OpenAsync_with_empty_clientName_throws_InvalidOperationException() { @@ -45,6 +47,7 @@ public sealed class GalaxyDriverBrowserTests /// A JSON literal that deserializes to null must fail fast with a /// "deserialized to null" message — never a downstream NRE. + /// A task that represents the asynchronous test operation. [Fact] public async Task OpenAsync_with_null_json_throws_InvalidOperationException() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Browse/DeployWatcherTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Browse/DeployWatcherTests.cs index 942889be..86a2fa48 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Browse/DeployWatcherTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Browse/DeployWatcherTests.cs @@ -120,6 +120,7 @@ public sealed class DeployWatcherTests } /// Verifies that bootstrap deploy events are suppressed. + /// A task that represents the asynchronous operation. [Fact] public async Task BootstrapEventIsSuppressed() { @@ -143,6 +144,7 @@ public sealed class DeployWatcherTests } /// Verifies that a deployment time change fires a rediscovery event. + /// A task that represents the asynchronous operation. [Fact] public async Task DeployTimeChangeFiresRediscover() { @@ -171,6 +173,7 @@ public sealed class DeployWatcherTests } /// Verifies that the same deployment time does not fire a rediscovery event. + /// A task that represents the asynchronous operation. [Fact] public async Task SameDeployTimeDoesNotFire() { @@ -195,6 +198,7 @@ public sealed class DeployWatcherTests } /// Verifies that a deployment time presence flip fires a rediscovery event. + /// A task that represents the asynchronous operation. [Fact] public async Task TimeOfLastDeployPresentFlipFiresRediscover() { @@ -222,6 +226,7 @@ public sealed class DeployWatcherTests } /// Verifies that stop cancels the watcher loop cleanly. + /// A task that represents the asynchronous operation. [Fact] public async Task StopCancelsLoopCleanly() { @@ -243,6 +248,7 @@ public sealed class DeployWatcherTests } /// Verifies that disposing stops a running watcher. + /// A task that represents the asynchronous operation. [Fact] public async Task DisposeStopsRunningWatcher() { @@ -262,6 +268,7 @@ public sealed class DeployWatcherTests } /// Verifies that a source exception triggers retry with backoff. + /// A task that represents the asynchronous operation. [Fact] public async Task SourceExceptionTriggersRetryWithBackoff() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Browse/GalaxyDiscovererTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Browse/GalaxyDiscovererTests.cs index 11b771f7..f4d4a581 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Browse/GalaxyDiscovererTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Browse/GalaxyDiscovererTests.cs @@ -16,9 +16,7 @@ public sealed class GalaxyDiscovererTests { private sealed class FakeHierarchySource(IReadOnlyList objects) : IGalaxyHierarchySource { - /// Gets the hierarchy asynchronously from the fake source. - /// The cancellation token. - /// A task that returns the pre-built Galaxy object list. + /// public Task> GetHierarchyAsync(CancellationToken cancellationToken) => Task.FromResult(objects); } @@ -41,10 +39,7 @@ public sealed class GalaxyDiscovererTests public FakeBuilder() : this(null) { } private FakeBuilder(string? folder) { _currentFolder = folder; } - /// Adds a folder call to the recorded list. - /// The browse name for the folder. - /// The display name for the folder. - /// An IAddressSpaceBuilder scoped to the new folder. + /// public IAddressSpaceBuilder Folder(string browseName, string displayName) { Folders.Add(new FolderCall(browseName, displayName)); @@ -52,11 +47,7 @@ public sealed class GalaxyDiscovererTests return new ChildBuilder(this, browseName); } - /// Adds a variable call to the recorded list. - /// The browse name for the variable. - /// The display name for the variable. - /// The attribute metadata for the variable. - /// An IVariableHandle for further configuration. + /// public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo attributeInfo) { var folder = _currentFolder ?? ""; @@ -64,51 +55,36 @@ public sealed class GalaxyDiscovererTests return new FakeVariableHandle(this, attributeInfo.FullName); } - /// Adds a property call to the builder (not recorded in this fake). - /// The browse name for the property. - /// The driver data type of the property. - /// The property value. + /// public void AddProperty(string browseName, DriverDataType dataType, object? value) { } /// Child folder routes Variable calls back to the parent's lists with its own scope. private sealed class ChildBuilder(FakeBuilder parent, string folderBrowseName) : IAddressSpaceBuilder { - /// Adds a child folder call to the parent builder's recorded list. - /// The browse name for the folder. - /// The display name for the folder. - /// An IAddressSpaceBuilder scoped to the new child folder. + /// public IAddressSpaceBuilder Folder(string browseName, string displayName) { parent.Folders.Add(new FolderCall(browseName, displayName)); return new ChildBuilder(parent, browseName); } - /// Adds a variable call to the parent builder's recorded list, scoped to this folder. - /// The browse name for the variable. - /// The display name for the variable. - /// The attribute metadata for the variable. - /// An IVariableHandle for further configuration. + /// public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo attributeInfo) { parent.Variables.Add(new VariableCall(folderBrowseName, browseName, attributeInfo)); return new FakeVariableHandle(parent, attributeInfo.FullName); } - /// Adds a property call to the builder (not recorded in this fake). - /// The browse name for the property. - /// The driver data type of the property. - /// The property value. + /// public void AddProperty(string browseName, DriverDataType dataType, object? value) { } } private sealed class FakeVariableHandle(FakeBuilder owner, string fullRef) : IVariableHandle { - /// Gets the full reference for this variable. + /// public string FullReference { get; } = fullRef; - /// Marks this variable as an alarm condition and records it. - /// The alarm condition metadata. - /// An IAlarmConditionSink for further alarm configuration. + /// public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) { owner.AlarmDeclarations[FullReference] = info; @@ -118,8 +94,7 @@ public sealed class GalaxyDiscovererTests private sealed class NoopSink : IAlarmConditionSink { - /// Records an alarm transition event (no-op in this fake). - /// The alarm event arguments. + /// public void OnTransition(AlarmEventArgs args) { } } } @@ -156,6 +131,7 @@ public sealed class GalaxyDiscovererTests } /// Verifies that discovery creates one folder per object and one variable per attribute. + /// A task that represents the asynchronous test operation. [Fact] public async Task DiscoverAsync_BuildsOneFolderPerObject_AndOneVariablePerAttribute() { @@ -178,6 +154,7 @@ public sealed class GalaxyDiscovererTests } /// Verifies that full reference defaults to tag.attribute format when not explicitly supplied. + /// A task that represents the asynchronous test operation. [Fact] public async Task DiscoverAsync_FullReference_DefaultsToTagDotAttribute() { @@ -193,6 +170,7 @@ public sealed class GalaxyDiscovererTests } /// Verifies that full reference uses gateway-supplied value when provided. + /// A task that represents the asynchronous test operation. [Fact] public async Task DiscoverAsync_FullReference_PrefersGwSuppliedFullTagReference() { @@ -208,6 +186,7 @@ public sealed class GalaxyDiscovererTests } /// Verifies that browse name falls back to tag name when contained name is empty. + /// A task that represents the asynchronous test operation. [Fact] public async Task DiscoverAsync_BrowseName_FallsBackToTagName_WhenContainedEmpty() { @@ -223,6 +202,7 @@ public sealed class GalaxyDiscovererTests } /// Verifies that attribute metadata fields are all propagated to the discovered variable. + /// A task that represents the asynchronous test operation. [Fact] public async Task DiscoverAsync_AttributeMetadata_PropagatesEveryField() { @@ -248,6 +228,7 @@ public sealed class GalaxyDiscovererTests } /// Verifies that alarm attributes populate all five sub-attribute references. + /// A task that represents the asynchronous test operation. [Fact] public async Task DiscoverAsync_AlarmAttribute_PopulatesAllFiveSubAttributeRefs() { @@ -270,6 +251,7 @@ public sealed class GalaxyDiscovererTests } /// Verifies that non-alarm attributes are not marked as alarm conditions. + /// A task that represents the asynchronous test operation. [Fact] public async Task DiscoverAsync_NonAlarmAttribute_DoesNotMarkCondition() { @@ -287,6 +269,7 @@ public sealed class GalaxyDiscovererTests } /// Verifies that objects with empty identity are skipped during discovery. + /// A task that represents the asynchronous test operation. [Fact] public async Task DiscoverAsync_SkipsObjectsWithEmptyIdentity() { @@ -304,6 +287,7 @@ public sealed class GalaxyDiscovererTests } /// Verifies that attributes with empty names are skipped during discovery. + /// A task that represents the asynchronous test operation. [Fact] public async Task DiscoverAsync_SkipsAttributesWithEmptyName() { @@ -320,6 +304,7 @@ public sealed class GalaxyDiscovererTests } /// Verifies that driver discovery routes through the injected hierarchy source. + /// A task that represents the asynchronous test operation. [Fact] public async Task DriverDiscoverAsync_RoutesThroughInjectedSource() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverAlarmEventArgsExtensionTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverAlarmEventArgsExtensionTests.cs index 8f92166a..60ce5e51 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverAlarmEventArgsExtensionTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverAlarmEventArgsExtensionTests.cs @@ -17,6 +17,7 @@ public sealed class GalaxyDriverAlarmEventArgsExtensionTests /// /// Verifies that acknowledge transition with full payload populates extended fields. /// + /// A task that represents the asynchronous test operation. [Fact] public async Task Acknowledge_transition_with_full_payload_populates_extended_fields() { @@ -53,6 +54,7 @@ public sealed class GalaxyDriverAlarmEventArgsExtensionTests /// /// Verifies that raise transition without optional fields leaves them null. /// + /// A task that represents the asynchronous test operation. [Fact] public async Task Raise_transition_without_optional_fields_leaves_them_null() { @@ -118,9 +120,8 @@ public sealed class GalaxyDriverAlarmEventArgsExtensionTests public void Emit(GalaxyAlarmTransition transition) => OnAlarmTransition?.Invoke(this, transition); - /// - /// Disposes the alarm feed asynchronously. - /// + /// Disposes the alarm feed asynchronously. + /// A completed value task. public ValueTask DisposeAsync() => ValueTask.CompletedTask; } } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverAlarmSourceTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverAlarmSourceTests.cs index af5a0560..a601d866 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverAlarmSourceTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverAlarmSourceTests.cs @@ -17,6 +17,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests; public sealed class GalaxyDriverAlarmSourceTests { /// Verifies that SubscribeAlarmsAsync starts the alarm feed and events fire on transition. + /// A task that represents the asynchronous test operation. [Fact] public async Task SubscribeAlarmsAsync_starts_feed_and_event_fires_on_transition() { @@ -62,6 +63,7 @@ public sealed class GalaxyDriverAlarmSourceTests } /// Verifies that UnsubscribeAlarmsAsync stops event flow. + /// A task that represents the asynchronous test operation. [Fact] public async Task UnsubscribeAlarmsAsync_stops_event_flow() { @@ -84,6 +86,7 @@ public sealed class GalaxyDriverAlarmSourceTests } /// Verifies that UnsubscribeAlarmsAsync throws for a foreign handle. + /// A task that represents the asynchronous test operation. [Fact] public async Task UnsubscribeAlarmsAsync_throws_for_foreign_handle() { @@ -97,6 +100,7 @@ public sealed class GalaxyDriverAlarmSourceTests } /// Verifies that AcknowledgeAsync routes each request to the acknowledger. + /// A task that represents the asynchronous test operation. [Fact] public async Task AcknowledgeAsync_routes_each_request_to_the_acknowledger() { @@ -119,6 +123,7 @@ public sealed class GalaxyDriverAlarmSourceTests } /// Verifies that AcknowledgeAsync falls back to SourceNodeId when ConditionId is empty. + /// A task that represents the asynchronous test operation. [Fact] public async Task AcknowledgeAsync_falls_back_to_SourceNodeId_when_ConditionId_empty() { @@ -134,6 +139,7 @@ public sealed class GalaxyDriverAlarmSourceTests } /// Verifies that AcknowledgeAsync throws NotSupportedException without an acknowledger. + /// A task that represents the asynchronous test operation. [Fact] public async Task AcknowledgeAsync_throws_NotSupported_without_acknowledger() { @@ -191,7 +197,7 @@ public sealed class GalaxyDriverAlarmSourceTests /// Occurs when an alarm transition is emitted. public event EventHandler? OnAlarmTransition; - /// Marks the feed as started. + /// public void Start() => Started = true; /// Emits an alarm transition to all subscribers. @@ -210,12 +216,7 @@ public sealed class GalaxyDriverAlarmSourceTests /// Gets the list of acknowledge calls recorded. public List<(string AlarmRef, string Comment, string Operator)> Calls { get; } = []; - /// Records an acknowledge call. - /// The alarm full reference. - /// The acknowledgment comment. - /// The operator user. - /// Cancellation token. - /// A completed task. + /// public Task AcknowledgeAsync(string alarmFullReference, string comment, string operatorUser, CancellationToken cancellationToken) { Calls.Add((alarmFullReference, comment, operatorUser)); @@ -226,7 +227,7 @@ public sealed class GalaxyDriverAlarmSourceTests /// Test double that represents a foreign alarm subscription handle. private sealed class ForeignAlarmHandle : IAlarmSubscriptionHandle { - /// Gets the diagnostic ID for this handle. + /// public string DiagnosticId => "foreign"; } } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverApiKeyResolverTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverApiKeyResolverTests.cs index cef0fcfd..1fcbbe00 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverApiKeyResolverTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverApiKeyResolverTests.cs @@ -130,13 +130,24 @@ public sealed class GalaxyDriverApiKeyResolverTests /// Gets the list of captured log entries with their levels and messages. public List<(LogLevel Level, string Message)> Entries { get; } = new(); - /// + /// Begins a logging scope (returns null — no scope support in test logger). + /// The type of the state object. + /// The state object. + /// Null — test logger has no scope support. public IDisposable? BeginScope(TState state) where TState : notnull => null; - /// + /// Returns true for all log levels so all messages are captured. + /// The log level to check. + /// Always true. public bool IsEnabled(LogLevel logLevel) => true; - /// + /// Captures the log message and level into the Entries list. + /// The type of the state object. + /// The log level. + /// The event ID. + /// The state object. + /// The exception, if any. + /// The formatter function. public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) => Entries.Add((logLevel, formatter(state, exception))); } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverFactoryTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverFactoryTests.cs index e3886373..7c06b3bb 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverFactoryTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverFactoryTests.cs @@ -125,6 +125,7 @@ public sealed class GalaxyDriverFactoryTests } /// Verifies that driver lifecycle toggles health state on initialize and shutdown. + /// A task that represents the asynchronous operation. [Fact] public async Task DriverLifecycle_InitializeShutdown_ToggleHealth() { @@ -148,6 +149,7 @@ public sealed class GalaxyDriverFactoryTests } /// Verifies that reinitializing with equivalent config refreshes health. + /// A task that represents the asynchronous operation. [Fact] public async Task ReinitializeAsync_RefreshesHealth_WhenConfigIsEquivalent() { @@ -218,6 +220,7 @@ public sealed class GalaxyDriverFactoryTests } /// Verifies that initializing after dispose throws an exception. + /// A task that represents the asynchronous operation. [Fact] public async Task InitializeAfterDispose_Throws() { @@ -244,6 +247,7 @@ public sealed class GalaxyDriverFactoryTests } /// Verifies that GetHostStatuses returns an empty snapshot after initialization with seam. + /// A task that represents the asynchronous operation. [Fact] public async Task GetHostStatuses_AfterInitWithSeam_ReturnsEmptySnapshot() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverInfrastructureTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverInfrastructureTests.cs index f135218c..76aa73af 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverInfrastructureTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/GalaxyDriverInfrastructureTests.cs @@ -34,6 +34,7 @@ public sealed class GalaxyDriverInfrastructureTests } /// Verifies that memory footprint is nonzero after subscriptions are active. + /// A task that represents the asynchronous test operation. [Fact] public async Task GetMemoryFootprint_IsNonZeroAfterSubscribe() { @@ -48,6 +49,7 @@ public sealed class GalaxyDriverInfrastructureTests } /// Verifies that memory footprint decreases after unsubscribing. + /// A task that represents the asynchronous test operation. [Fact] public async Task GetMemoryFootprint_DecreasesAfterUnsubscribe() { @@ -67,6 +69,7 @@ public sealed class GalaxyDriverInfrastructureTests // ===== Driver.Galaxy-007 regression: Dispose cancels the dispose CTS ===== /// Verifies that Dispose sets disposed flag and blocks further capability calls. + /// A task that represents the asynchronous test operation. [Fact] public async Task Dispose_SetsDisposedFlag_BlockingFurtherCapabilityCalls() { @@ -81,6 +84,7 @@ public sealed class GalaxyDriverInfrastructureTests } /// Verifies that DisposeAsync can be awaited without deadlock. + /// A task that represents the asynchronous test operation. [Fact] public async Task DisposeAsync_CanBeAwaitedWithoutDeadlock() { @@ -93,6 +97,7 @@ public sealed class GalaxyDriverInfrastructureTests // ===== Driver.Galaxy-013 regression: ReplayOnSessionLost gates the replay step ===== /// Verifies that ReplayOnSessionLost=false skips resubscription on reconnect. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReplayOnSessionLost_False_SkipsResubscribeBulk() { @@ -123,6 +128,7 @@ public sealed class GalaxyDriverInfrastructureTests } /// Verifies that ReplayOnSessionLost=true runs resubscription on reconnect. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReplayOnSessionLost_True_RunsResubscribeBulk() { @@ -152,10 +158,7 @@ public sealed class GalaxyDriverInfrastructureTests /// Gets the count of subscription calls. public int SubscribeCalls; - /// Subscribes to multiple tags and counts the call. - /// List of tag addresses to subscribe to. - /// Buffered update interval in milliseconds. - /// Cancellation token. + /// public Task> SubscribeBulkAsync( IReadOnlyList fullReferences, int bufferedUpdateIntervalMs, CancellationToken cancellationToken) { @@ -169,14 +172,11 @@ public sealed class GalaxyDriverInfrastructureTests return Task.FromResult>(results); } - /// Unsubscribes from multiple tags. - /// List of subscription handles to unsubscribe. - /// Cancellation token. + /// public Task UnsubscribeBulkAsync(IReadOnlyList itemHandles, CancellationToken cancellationToken) => Task.CompletedTask; - /// Streams subscription events. - /// Cancellation token. + /// public IAsyncEnumerable StreamEventsAsync(CancellationToken cancellationToken) => _stream.Reader.ReadAllAsync(cancellationToken); } @@ -184,6 +184,7 @@ public sealed class GalaxyDriverInfrastructureTests // ===== Driver.Galaxy-013 regression: ReinitializeAsync rejects unsupported reapply ===== /// Verifies that ReinitializeAsync rejects non-equivalent config changes. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReinitializeAsync_RejectsNonEquivalentConfigChange() { @@ -203,6 +204,7 @@ public sealed class GalaxyDriverInfrastructureTests } /// Verifies that ReinitializeAsync accepts equivalent config. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReinitializeAsync_AcceptsEquivalentConfig() { @@ -227,10 +229,7 @@ public sealed class GalaxyDriverInfrastructureTests { private readonly Channel _stream = Channel.CreateUnbounded(); - /// Subscribes to multiple tags (no-op). - /// List of tag addresses to subscribe to. - /// Buffered update interval in milliseconds. - /// Cancellation token. + /// public Task> SubscribeBulkAsync( IReadOnlyList fullReferences, int bufferedUpdateIntervalMs, CancellationToken cancellationToken) { @@ -243,14 +242,11 @@ public sealed class GalaxyDriverInfrastructureTests return Task.FromResult>(results); } - /// Unsubscribes from multiple tags (no-op). - /// List of subscription handles to unsubscribe. - /// Cancellation token. + /// public Task UnsubscribeBulkAsync(IReadOnlyList itemHandles, CancellationToken cancellationToken) => Task.CompletedTask; - /// Streams subscription events (no-op). - /// Cancellation token. + /// public IAsyncEnumerable StreamEventsAsync(CancellationToken cancellationToken) => _stream.Reader.ReadAllAsync(cancellationToken); } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Health/PerPlatformProbeWatcherTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Health/PerPlatformProbeWatcherTests.cs index b59ba151..849951bd 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Health/PerPlatformProbeWatcherTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Health/PerPlatformProbeWatcherTests.cs @@ -27,10 +27,7 @@ public sealed class PerPlatformProbeWatcherTests /// Gets a mapping of tag addresses to their assigned item handles. public Dictionary HandleByAddress { get; } = new(StringComparer.OrdinalIgnoreCase); - /// Simulates a bulk subscribe operation by generating handles for each reference. - /// The list of tag addresses to subscribe to. - /// The buffered update interval in milliseconds. - /// The cancellation token for the operation. + /// public Task> SubscribeBulkAsync( IReadOnlyList fullReferences, int bufferedUpdateIntervalMs, CancellationToken cancellationToken) { @@ -51,17 +48,14 @@ public sealed class PerPlatformProbeWatcherTests return Task.FromResult>(results); } - /// Simulates a bulk unsubscribe operation by recording the handles. - /// The list of item handles to unsubscribe. - /// The cancellation token for the operation. + /// public Task UnsubscribeBulkAsync(IReadOnlyList itemHandles, CancellationToken cancellationToken) { Unsubscribes.Add([.. itemHandles]); return Task.CompletedTask; } - /// Returns an empty event stream for testing. - /// The cancellation token for the operation. + /// public IAsyncEnumerable StreamEventsAsync(CancellationToken cancellationToken) => Empty(); @@ -73,6 +67,7 @@ public sealed class PerPlatformProbeWatcherTests } /// Verifies that syncing platforms subscribes to the ScanState address for each platform. + /// A task that represents the asynchronous operation. [Fact] public async Task SyncPlatformsAsync_SubscribesScanStateAddressForEachPlatform() { @@ -88,6 +83,7 @@ public sealed class PerPlatformProbeWatcherTests } /// Verifies that the default buffered interval is zero, matching gateway cadence. + /// A task that represents the asynchronous operation. [Fact] public async Task SyncPlatformsAsync_DefaultBufferedIntervalIsZero_GwCadence() { @@ -100,6 +96,7 @@ public sealed class PerPlatformProbeWatcherTests } /// Verifies that a configured buffered interval is forwarded to the gateway. + /// A task that represents the asynchronous operation. [Fact] public async Task SyncPlatformsAsync_ConfiguredBufferedInterval_IsForwardedToGw() { @@ -125,6 +122,7 @@ public sealed class PerPlatformProbeWatcherTests } /// Verifies that syncing the same platform set twice does not resubscribe. + /// A task that represents the asynchronous operation. [Fact] public async Task SyncPlatformsAsync_SameSetTwice_DoesNotResubscribe() { @@ -139,6 +137,7 @@ public sealed class PerPlatformProbeWatcherTests } /// Verifies that removed platforms are unsubscribed and dropped from the aggregator. + /// A task that represents the asynchronous operation. [Fact] public async Task SyncPlatformsAsync_RemovedPlatforms_AreUnsubscribed_AndDroppedFromAggregator() { @@ -183,6 +182,7 @@ public sealed class PerPlatformProbeWatcherTests } /// Verifies that a running probe value is routed to the aggregator. + /// A task that represents the asynchronous operation. [Fact] public async Task OnProbeValueChanged_Running_RoutesToAggregator() { @@ -198,6 +198,7 @@ public sealed class PerPlatformProbeWatcherTests } /// Verifies that a probe value with bad quality routes as unknown state. + /// A task that represents the asynchronous operation. [Fact] public async Task OnProbeValueChanged_BadQuality_RoutesUnknown() { @@ -212,6 +213,7 @@ public sealed class PerPlatformProbeWatcherTests } /// Verifies that foreign probe references are silently dropped. + /// A task that represents the asynchronous operation. [Fact] public async Task OnProbeValueChanged_ForeignReference_IsSilentlyDropped() { @@ -231,6 +233,7 @@ public sealed class PerPlatformProbeWatcherTests } /// Verifies that dispose unsubscribes all tracked platforms. + /// A task that represents the asynchronous operation. [Fact] public async Task Dispose_UnsubscribesAllTrackedPlatforms() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/EventPumpBoundedChannelTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/EventPumpBoundedChannelTests.cs index 9790bbcd..9a76d958 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/EventPumpBoundedChannelTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/EventPumpBoundedChannelTests.cs @@ -18,6 +18,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests.Runtime; public sealed class EventPumpBoundedChannelTests { /// Verifies that the event pump drops newest events when the bounded channel fills and records metrics for dropped events. + /// A task that represents the asynchronous operation. [Fact] public async Task Drops_newest_when_channel_fills_and_records_metric() { @@ -68,6 +69,7 @@ public sealed class EventPumpBoundedChannelTests } /// Verifies that the event pump throws an exception when the channel capacity is invalid. + /// A task that represents the asynchronous operation. [Fact] public async Task Throws_when_channelCapacity_is_invalid() { @@ -81,6 +83,7 @@ public sealed class EventPumpBoundedChannelTests } /// Verifies that event pump metrics are tagged with the client name for tracking multiple driver hosts. + /// A task that represents the asynchronous operation. [Fact] public async Task Tags_metrics_with_client_name_for_multi_driver_hosts() { @@ -189,25 +192,16 @@ public sealed class EventPumpBoundedChannelTests private readonly Channel _stream = Channel.CreateUnbounded(new UnboundedChannelOptions { SingleReader = true }); - /// Subscribes to a bulk list of tag references. - /// The list of full references to subscribe to. - /// The buffered update interval in milliseconds. - /// The cancellation token. - /// An empty result list. + /// public Task> SubscribeBulkAsync( IReadOnlyList fullReferences, int bufferedUpdateIntervalMs, CancellationToken cancellationToken) => Task.FromResult>([]); - /// Unsubscribes from a bulk list of item handles. - /// The list of item handles to unsubscribe from. - /// The cancellation token. - /// A completed task. + /// public Task UnsubscribeBulkAsync(IReadOnlyList itemHandles, CancellationToken cancellationToken) => Task.CompletedTask; - /// Streams events asynchronously. - /// The cancellation token. - /// An async enumerable of MxEvent objects. + /// public IAsyncEnumerable StreamEventsAsync(CancellationToken cancellationToken) => _stream.Reader.ReadAllAsync(cancellationToken); diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/EventPumpStreamFaultTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/EventPumpStreamFaultTests.cs index cd4a5a47..caf83d51 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/EventPumpStreamFaultTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/EventPumpStreamFaultTests.cs @@ -19,6 +19,7 @@ public sealed class EventPumpStreamFaultTests private const int WaitMs = 2_000; /// Verifies that stream fault invokes the callback with the exception. + /// A task that represents the asynchronous operation. [Fact] public async Task StreamFault_InvokesOnStreamFaultCallback_WithTheCause() { @@ -42,6 +43,7 @@ public sealed class EventPumpStreamFaultTests } /// Verifies that stream fault drives the reconnect supervisor through reopen and replay. + /// A task that represents the asynchronous operation. [Fact] public async Task StreamFault_DrivesReconnectSupervisorReopenReplay() { @@ -79,6 +81,7 @@ public sealed class EventPumpStreamFaultTests } /// Verifies that a faulted pump cannot be restarted in place, but a fresh pump resumes dispatch. + /// A task that represents the asynchronous operation. [Fact] public async Task FaultedPump_IsNotRestartableInPlace_ButAFreshPumpResumesDispatch() { @@ -122,6 +125,7 @@ public sealed class EventPumpStreamFaultTests } /// Verifies that clean shutdown does not invoke the stream fault callback. + /// A task that represents the asynchronous operation. [Fact] public async Task CleanShutdown_DoesNotInvokeOnStreamFault() { @@ -150,22 +154,16 @@ public sealed class EventPumpStreamFaultTests private readonly Channel _stream = Channel.CreateUnbounded(new UnboundedChannelOptions { SingleReader = true }); - /// Subscribes to multiple tags (test stub). - /// The tag references to subscribe to. - /// The buffered update interval in milliseconds. - /// The cancellation token. + /// public Task> SubscribeBulkAsync( IReadOnlyList fullReferences, int bufferedUpdateIntervalMs, CancellationToken cancellationToken) => Task.FromResult>([]); - /// Unsubscribes from multiple tags (test stub). - /// The item handles to unsubscribe from. - /// The cancellation token. + /// public Task UnsubscribeBulkAsync(IReadOnlyList itemHandles, CancellationToken cancellationToken) => Task.CompletedTask; - /// Streams events asynchronously (test stub). - /// The cancellation token. + /// public IAsyncEnumerable StreamEventsAsync(CancellationToken cancellationToken) => _stream.Reader.ReadAllAsync(cancellationToken); @@ -183,28 +181,23 @@ public sealed class EventPumpStreamFaultTests private readonly Channel _stream = Channel.CreateUnbounded(new UnboundedChannelOptions { SingleReader = true }); - /// Subscribes to multiple tags (test stub). - /// The tag references to subscribe to. - /// The buffered update interval in milliseconds. - /// The cancellation token. + /// public Task> SubscribeBulkAsync( IReadOnlyList fullReferences, int bufferedUpdateIntervalMs, CancellationToken cancellationToken) => Task.FromResult>([]); - /// Unsubscribes from multiple tags (test stub). - /// The item handles to unsubscribe from. - /// The cancellation token. + /// public Task UnsubscribeBulkAsync(IReadOnlyList itemHandles, CancellationToken cancellationToken) => Task.CompletedTask; - /// Streams events asynchronously (test stub). - /// The cancellation token. + /// public IAsyncEnumerable StreamEventsAsync(CancellationToken cancellationToken) => _stream.Reader.ReadAllAsync(cancellationToken); /// Emits a data change event asynchronously. /// The item handle for the data change. /// The numeric value of the change. + /// A value task that represents the asynchronous operation. public ValueTask EmitAsync(int itemHandle, double value) => _stream.Writer.WriteAsync(new MxEvent { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GalaxyDriverReadTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GalaxyDriverReadTests.cs index d1b74038..0f3eac63 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GalaxyDriverReadTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GalaxyDriverReadTests.cs @@ -43,6 +43,7 @@ public sealed class GalaxyDriverReadTests } /// Verifies that ReadAsync routes through the injected reader. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReadAsync_RoutesThroughInjectedReader() { @@ -58,6 +59,7 @@ public sealed class GalaxyDriverReadTests } /// Verifies that ReadAsync returns empty without calling the reader for an empty request. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReadAsync_EmptyRequest_ReturnsEmpty_WithoutCallingReader() { @@ -71,6 +73,7 @@ public sealed class GalaxyDriverReadTests } /// Verifies that ReadAsync throws when seams and production runtime are not built. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReadAsync_NoSeams_AndNoProductionRuntime_Throws() { @@ -86,6 +89,7 @@ public sealed class GalaxyDriverReadTests } /// Verifies that ReadAsync throws after the driver is disposed. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReadAsync_AfterDispose_Throws() { @@ -96,6 +100,7 @@ public sealed class GalaxyDriverReadTests } /// Verifies that ReadAsync resolves from the first OnDataChange event on the subscribe-once path. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReadAsync_SubscribeOncePath_ResolvesFromFirstOnDataChange() { @@ -120,6 +125,7 @@ public sealed class GalaxyDriverReadTests } /// Verifies that ReadAsync surfaces rejected tags as bad status on the subscribe-once path. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReadAsync_SubscribeOncePath_RejectedTagSurfacesAsBadStatus() { @@ -140,6 +146,7 @@ public sealed class GalaxyDriverReadTests } /// Verifies that ReadAsync preserves reader status codes. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReadAsync_PreservesReaderStatusCodes() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GalaxyDriverSubscribeTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GalaxyDriverSubscribeTests.cs index 84e5df09..a4f53260 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GalaxyDriverSubscribeTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GalaxyDriverSubscribeTests.cs @@ -40,11 +40,7 @@ public sealed class GalaxyDriverSubscribeTests /// Gets or sets a function to decide whether to accept a subscription. public Func Decide { get; set; } = _ => true; - /// Subscribes to bulk updates for the specified tag references. - /// The tag references to subscribe to. - /// The buffered update interval in milliseconds. - /// Cancellation token for the operation. - /// A list of subscription results. + /// public Task> SubscribeBulkAsync( IReadOnlyList fullReferences, int bufferedUpdateIntervalMs, CancellationToken cancellationToken) { @@ -77,19 +73,14 @@ public sealed class GalaxyDriverSubscribeTests return Task.FromResult>(results); } - /// Unsubscribes from bulk updates for the specified item handles. - /// The handles to unsubscribe. - /// Cancellation token for the operation. - /// A completed task. + /// public Task UnsubscribeBulkAsync(IReadOnlyList itemHandles, CancellationToken cancellationToken) { UnsubscribedHandles.AddRange(itemHandles); return Task.CompletedTask; } - /// Streams events asynchronously. - /// Cancellation token for the operation. - /// An async enumerable of MX events. + /// public IAsyncEnumerable StreamEventsAsync(CancellationToken cancellationToken) => _events.Reader.ReadAllAsync(cancellationToken); @@ -113,6 +104,7 @@ public sealed class GalaxyDriverSubscribeTests } /// Verifies subscription allocates a handle and dispatches value changes. + /// A task that represents the asynchronous test operation. [Fact] public async Task SubscribeAsync_AllocatesHandle_AndDispatchesValueChange() { @@ -136,6 +128,7 @@ public sealed class GalaxyDriverSubscribeTests } /// Verifies two subscriptions for the same tag each receive updates. + /// A task that represents the asynchronous test operation. [Fact] public async Task SubscribeAsync_TwoSubscriptions_SameTag_FanOutOnePerSubscription() { @@ -180,6 +173,7 @@ public sealed class GalaxyDriverSubscribeTests } /// Verifies failed subscriptions do not dispatch events. + /// A task that represents the asynchronous test operation. [Fact] public async Task SubscribeAsync_FailedTag_DoesNotDispatchEvents() { @@ -201,6 +195,7 @@ public sealed class GalaxyDriverSubscribeTests } /// Verifies unsubscribe removes registration and calls gateway unsubscribe. + /// A task that represents the asynchronous test operation. [Fact] public async Task UnsubscribeAsync_RemovesRegistration_AndCallsGwUnsubscribe() { @@ -224,6 +219,7 @@ public sealed class GalaxyDriverSubscribeTests } /// Verifies unsubscribing with an unknown handle is handled. + /// A task that represents the asynchronous test operation. [Fact] public async Task UnsubscribeAsync_UnknownHandle_NoOp() { @@ -239,6 +235,7 @@ public sealed class GalaxyDriverSubscribeTests } /// Verifies subscription without a subscriber throws. + /// A task that represents the asynchronous test operation. [Fact] public async Task SubscribeAsync_NoSubscriber_Throws() { @@ -249,6 +246,7 @@ public sealed class GalaxyDriverSubscribeTests } /// Verifies subscription falls back to configured interval when zero is passed. + /// A task that represents the asynchronous test operation. [Fact] public async Task SubscribeAsync_FallsBackToConfiguredInterval_WhenCallerPassesZero() { @@ -269,6 +267,7 @@ public sealed class GalaxyDriverSubscribeTests } /// Verifies subscription respects caller's interval when non-zero. + /// A task that represents the asynchronous test operation. [Fact] public async Task SubscribeAsync_RespectsCallerInterval_WhenNonZero() { @@ -289,6 +288,7 @@ public sealed class GalaxyDriverSubscribeTests } /// Verifies subscription with empty tag list returns handle without calling gateway. + /// A task that represents the asynchronous test operation. [Fact] public async Task SubscribeAsync_EmptyTagList_ReturnsHandleWithoutCallingGw() { @@ -304,7 +304,7 @@ public sealed class GalaxyDriverSubscribeTests /// A subscription handle from a foreign source. private sealed class ForeignHandle : ISubscriptionHandle { - /// Gets the diagnostic identifier for this handle. + /// public string DiagnosticId => "foreign-x"; } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GalaxyDriverWriteTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GalaxyDriverWriteTests.cs index 7de25685..e414d729 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GalaxyDriverWriteTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GalaxyDriverWriteTests.cs @@ -26,8 +26,7 @@ public sealed class GalaxyDriverWriteTests private sealed class FakeHierarchySource(IReadOnlyList objects) : IGalaxyHierarchySource { - /// Returns the fake Galaxy object hierarchy. - /// Token to cancel the operation. + /// public Task> GetHierarchyAsync(CancellationToken cancellationToken) => Task.FromResult(objects); } @@ -37,36 +36,26 @@ public sealed class GalaxyDriverWriteTests /// Gets the list of variables added to this builder. public List Variables { get; } = []; - /// Adds a folder and returns this builder for chaining. - /// The browse name of the folder. - /// The display name of the folder. + /// public IAddressSpaceBuilder Folder(string browseName, string displayName) => this; - /// Adds a variable to the variables list and returns a handle. - /// The browse name of the variable. - /// The display name of the variable. - /// The attribute information for the variable. + /// public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo attributeInfo) { Variables.Add(attributeInfo); return new FakeHandle(attributeInfo.FullName); } - /// No-op property adding operation for test compatibility. - /// The browse name of the property. - /// The data type of the property. - /// The value of the property. + /// public void AddProperty(string browseName, DriverDataType dataType, object? value) { } private sealed class FakeHandle(string fullRef) : IVariableHandle { - /// Gets the full reference for this variable handle. + /// public string FullReference { get; } = fullRef; - /// Marks this variable as an alarm condition and returns a noop sink. - /// The alarm condition information. + /// public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) => new NoopSink(); /// No-op alarm transition handler. private sealed class NoopSink : IAlarmConditionSink { - /// Handles alarm state transition events. - /// The alarm event arguments. + /// public void OnTransition(AlarmEventArgs args) { } } } @@ -77,10 +66,7 @@ public sealed class GalaxyDriverWriteTests /// Gets the list of write calls received by this writer. public List<(string FullRef, object? Value, SecurityClassification Resolved)> Calls { get; } = []; - /// Records write requests with their resolved security classifications. - /// The list of write requests to process. - /// Function to resolve security classification for each request. - /// Token to cancel the operation. + /// public Task> WriteAsync( IReadOnlyList writes, Func securityResolver, @@ -107,6 +93,7 @@ public sealed class GalaxyDriverWriteTests } /// Verifies that WriteAsync routes through the injected writer and propagates values correctly. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteAsync_RoutesThroughInjectedWriter_AndPropagatesValues() { @@ -133,6 +120,7 @@ public sealed class GalaxyDriverWriteTests /// Verifies that WriteAsync resolves every security classification from discovery data. /// The raw MXAccess security integer from the discovery attribute. /// The expected resolved security classification. + /// A task that represents the asynchronous operation. [Theory] [InlineData(0, SecurityClassification.FreeAccess)] [InlineData(1, SecurityClassification.Operate)] @@ -157,6 +145,7 @@ public sealed class GalaxyDriverWriteTests } /// Verifies that unknown tags resolve to FreeAccess classification and writes proceed. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteAsync_UnknownTag_ResolvesToFreeAccess_DefaultsToWrite() { @@ -172,6 +161,7 @@ public sealed class GalaxyDriverWriteTests } /// Verifies that an empty write request returns empty without calling the writer. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteAsync_EmptyRequest_ReturnsEmpty_WithoutCallingWriter() { @@ -186,6 +176,7 @@ public sealed class GalaxyDriverWriteTests } /// Verifies that WriteAsync throws when no writer is configured, referencing PR 4.4. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteAsync_NoWriter_Throws_PointingAtPR44() { @@ -197,6 +188,7 @@ public sealed class GalaxyDriverWriteTests } /// Verifies that WriteAsync throws ObjectDisposedException after the driver is disposed. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteAsync_AfterDispose_Throws() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GalaxyTelemetryTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GalaxyTelemetryTests.cs index a1017e1a..afe493f5 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GalaxyTelemetryTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GalaxyTelemetryTests.cs @@ -31,6 +31,7 @@ public sealed class GalaxyTelemetryTests } /// Verifies that TracedGalaxySubscriber emits a subscribe_bulk span with tag count. + /// A task that represents the asynchronous test operation. [Fact] public async Task TracedGalaxySubscriber_emits_subscribe_bulk_span_with_tag_count() { @@ -52,6 +53,7 @@ public sealed class GalaxyTelemetryTests } /// Verifies that TracedGalaxySubscriber records error and rethrows on failure. + /// A task that represents the asynchronous test operation. [Fact] public async Task TracedGalaxySubscriber_records_error_and_rethrows_on_failure() { @@ -70,6 +72,7 @@ public sealed class GalaxyTelemetryTests } /// Verifies that TracedGalaxyDataWriter tags the secured write count. + /// A task that represents the asynchronous test operation. [Fact] public async Task TracedGalaxyDataWriter_tags_secured_write_count() { @@ -106,6 +109,7 @@ public sealed class GalaxyTelemetryTests } /// Verifies that TracedGalaxyHierarchySource tags the object count. + /// A task that represents the asynchronous test operation. [Fact] public async Task TracedGalaxyHierarchySource_tags_object_count() { @@ -182,7 +186,9 @@ public sealed class GalaxyTelemetryTests private sealed class FakeHierarchy : IGalaxyHierarchySource { - /// + /// Returns a fixed two-element hierarchy for telemetry testing. + /// The cancellation token. + /// A task that resolves to a two-element Galaxy object list. public Task> GetHierarchyAsync( CancellationToken cancellationToken) => Task.FromResult>( diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GatewayGalaxyAlarmFeedLiveTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GatewayGalaxyAlarmFeedLiveTests.cs index 44a9cb43..241b8dcf 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GatewayGalaxyAlarmFeedLiveTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GatewayGalaxyAlarmFeedLiveTests.cs @@ -21,6 +21,8 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests.Runtime; [Trait("Category", "Integration")] public sealed class GatewayGalaxyAlarmFeedLiveTests { + /// Verifies that the live gateway delivers native alarm transitions through the consumer. + /// A task that represents the asynchronous test operation. [Fact] public async Task Live_gateway_delivers_native_alarm_transitions_through_the_consumer() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GatewayGalaxyAlarmFeedTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GatewayGalaxyAlarmFeedTests.cs index 077a9efd..40b6d280 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GatewayGalaxyAlarmFeedTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GatewayGalaxyAlarmFeedTests.cs @@ -18,6 +18,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests.Runtime; public sealed class GatewayGalaxyAlarmFeedTests { /// Verifies that the feed decodes active alarm snapshots and live transitions. + /// A task that represents the asynchronous operation. [Fact] public async Task Decodes_active_alarm_snapshot_then_live_transition() { @@ -70,6 +71,7 @@ public sealed class GatewayGalaxyAlarmFeedTests } /// Verifies that the feed drops transitions with unspecified kind and empty messages. + /// A task that represents the asynchronous operation. [Fact] public async Task Drops_transition_with_unspecified_kind_and_empty_message() { @@ -107,6 +109,7 @@ public sealed class GatewayGalaxyAlarmFeedTests } /// Verifies that the feed reopens the stream after a transport fault. + /// A task that represents the asynchronous operation. [Fact] public async Task Reopens_stream_after_a_transport_fault() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/ReconnectSupervisorTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/ReconnectSupervisorTests.cs index a5008be6..b26db1e9 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/ReconnectSupervisorTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/ReconnectSupervisorTests.cs @@ -28,6 +28,7 @@ public sealed class ReconnectSupervisorTests } /// Verifies that reporting a transport failure drives the supervisor through reopen and replay cycles back to healthy. + /// A task that represents the asynchronous operation. [Fact] public async Task ReportTransportFailure_DrivesThroughReopenReplay_BackToHealthy() { @@ -57,6 +58,7 @@ public sealed class ReconnectSupervisorTests } /// Verifies that reopen failures trigger retries and the supervisor stays in reopening state between attempts. + /// A task that represents the asynchronous operation. [Fact] public async Task ReopenFailure_RetriesUntilSuccess_StaysInReopeningBetweenAttempts() { @@ -75,6 +77,7 @@ public sealed class ReconnectSupervisorTests } /// Verifies that replay failures trigger a retry of the entire reopen-replay cycle. + /// A task that represents the asynchronous operation. [Fact] public async Task ReplayFailure_RetriesEntireCycle() { @@ -95,6 +98,7 @@ public sealed class ReconnectSupervisorTests } /// Verifies that repeated failure reports during recovery do not spawn parallel recovery loops. + /// A task that represents the asynchronous operation. [Fact] public async Task RepeatedFailureReports_DuringRecovery_DoNotSpawnParallelLoops() { @@ -122,6 +126,7 @@ public sealed class ReconnectSupervisorTests } /// Verifies that the last error reflects the most recent failure cause from recovery attempts. + /// A task that represents the asynchronous operation. [Fact] public async Task LastError_ReflectsMostRecentFailureCause() { @@ -141,6 +146,7 @@ public sealed class ReconnectSupervisorTests } /// Verifies that disposing the supervisor cancels a running recovery loop cleanly. + /// A task that represents the asynchronous operation. [Fact] public async Task Dispose_CancelsRunningRecoveryLoop_Cleanly() { @@ -170,6 +176,7 @@ public sealed class ReconnectSupervisorTests } /// Verifies that waiting for healthy state returns immediately when already healthy. + /// A task that represents the asynchronous operation. [Fact] public async Task WaitForHealthy_ReturnsImmediately_WhenAlreadyHealthy() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/SubscriptionRegistryTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/SubscriptionRegistryTests.cs index 2451871f..70fded8d 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/SubscriptionRegistryTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/SubscriptionRegistryTests.cs @@ -242,6 +242,7 @@ public sealed class SubscriptionRegistryTests public int TrackedItemHandleCount => _inner.TrackedItemHandleCount; /// Gets the next subscription ID. + /// A monotonically increasing subscription identifier. public long NextSubscriptionId() => _inner.NextSubscriptionId(); /// Registers a subscription with the given bindings. diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client.Tests/FakeSidecarServer.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client.Tests/FakeSidecarServer.cs index df81d8e2..f19b7e69 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client.Tests/FakeSidecarServer.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client.Tests/FakeSidecarServer.cs @@ -68,6 +68,7 @@ internal sealed class FakeSidecarServer : IAsyncDisposable public string PipeName => _pipeName; /// Starts the fake sidecar server asynchronously. + /// A task that completes once the server listener is ready to accept connections. public Task StartAsync() { _loop = Task.Run(() => RunAsync(_cts.Token)); @@ -194,6 +195,7 @@ internal sealed class FakeSidecarServer : IAsyncDisposable } /// Releases all resources used by the fake sidecar server. + /// A value task that represents the asynchronous dispose operation. public async ValueTask DisposeAsync() { _cts.Cancel(); diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client.Tests/WonderwareHistorianClientTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client.Tests/WonderwareHistorianClientTests.cs index 0e3c178a..cea25ba5 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client.Tests/WonderwareHistorianClientTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client.Tests/WonderwareHistorianClientTests.cs @@ -29,6 +29,7 @@ public sealed class WonderwareHistorianClientTests CallTimeout: TimeSpan.FromSeconds(2)); /// Verifies that ReadRawAsync round-trips samples and maps quality bytes to OPC UA status codes. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadRawAsync_RoundTripsSamples_AndMapsQualityByteToOpcUaStatusCode() { @@ -71,6 +72,7 @@ public sealed class WonderwareHistorianClientTests } /// Verifies that ReadProcessedAsync maps null buckets to BadNoData status. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadProcessedAsync_NullBuckets_MapToBadNoData() { @@ -103,6 +105,7 @@ public sealed class WonderwareHistorianClientTests } /// Verifies that ReadAtTimeAsync preserves timestamp order. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadAtTimeAsync_PreservesTimestampOrder() { @@ -131,6 +134,7 @@ public sealed class WonderwareHistorianClientTests } /// Verifies that ReadAtTimeAsync aligns by timestamp and fills gaps with bad status. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadAtTimeAsync_PartialAndReorderedReply_AlignsByTimestamp_AndFillsGapsAsBad() { @@ -185,6 +189,7 @@ public sealed class WonderwareHistorianClientTests } /// Verifies that ReadEventsAsync preserves event field values. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadEventsAsync_PreservesEventFields() { @@ -223,6 +228,7 @@ public sealed class WonderwareHistorianClientTests } /// Verifies that ReadRawAsync throws InvalidOperationException on server errors. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadRawAsync_ServerError_ThrowsInvalidOperation() { @@ -241,6 +247,7 @@ public sealed class WonderwareHistorianClientTests } /// Verifies that WriteBatchAsync maps per-event results to acknowledge or retry outcomes. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteBatchAsync_PerEventOk_MapsToAckOrRetryPlease() { @@ -270,6 +277,7 @@ public sealed class WonderwareHistorianClientTests } /// Verifies that WriteBatchAsync returns retry outcomes for whole call failures. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteBatchAsync_WholeCallFailure_ReturnsRetryPleaseForEveryEvent() { @@ -300,6 +308,7 @@ public sealed class WonderwareHistorianClientTests } /// Verifies that Hello handshake throws UnauthorizedAccessException on secret mismatch. + /// A task that represents the asynchronous operation. [Fact] public async Task Hello_BadSecret_ThrowsUnauthorizedAccess() { @@ -315,6 +324,7 @@ public sealed class WonderwareHistorianClientTests } /// Verifies that the client retries after a transport drop. + /// A task that represents the asynchronous operation. [Fact] public async Task Reconnect_AfterTransportDrop_RetriesOnce() { @@ -345,6 +355,7 @@ public sealed class WonderwareHistorianClientTests } /// Verifies that GetHealthSnapshot tracks success and failure counts. + /// A task that represents the asynchronous operation. [Fact] public async Task GetHealthSnapshot_TracksSuccessAndFailureCounts() { @@ -381,6 +392,7 @@ public sealed class WonderwareHistorianClientTests /// (2) A transport drop during a write (the catch path in WriteBatchAsync) must return /// RetryPlease for every event in the batch — never throw, never PermanentFail. /// + /// A task that represents the asynchronous operation. [Fact] public async Task WriteBatchAsync_TransportDropDuringWrite_ReturnsRetryPleaseForEveryEvent() { @@ -413,6 +425,7 @@ public sealed class WonderwareHistorianClientTests /// (3) When both the first attempt and the single retry fail (the "second attempt also /// fails" path in InvokeAsync), the exception propagates to the caller. /// + /// A task that represents the asynchronous operation. [Fact] public async Task InvokeAsync_BothAttemptsFailTransport_PropagatesException() { @@ -436,6 +449,7 @@ public sealed class WonderwareHistorianClientTests /// (4) A stalled sidecar that never sends a reply must cause an /// within the configured CallTimeout. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ReadRawAsync_StalledSidecar_TimesOutWithOperationCanceledException() { @@ -466,6 +480,7 @@ public sealed class WonderwareHistorianClientTests /// because Wonderware AnalogSummary has no Total /// aggregate column. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ReadProcessedAsync_TotalAggregate_ThrowsNotSupported() { @@ -486,6 +501,7 @@ public sealed class WonderwareHistorianClientTests /// expect (e.g. ReadRawReply where ReadAtTimeReply was expected), the client must throw /// . /// + /// A task that represents the asynchronous operation. [Fact] public async Task ReadRawAsync_SidecarRepliesWithWrongKind_ThrowsInvalidDataException() { @@ -513,6 +529,7 @@ public sealed class WonderwareHistorianClientTests /// a future regression to the "RecordSuccess then undo via ReclassifySuccessAsFailure" /// dance is caught. /// + /// A task that represents the asynchronous operation. [Fact] public async Task GetHealthSnapshot_SidecarFailure_NeverInflatesSuccessCounter() { @@ -544,6 +561,7 @@ public sealed class WonderwareHistorianClientTests /// channel serializes calls, so the test is observable: each completed query strictly /// increments either successes or failures by one. /// + /// A task that represents the asynchronous operation. [Fact] public async Task GetHealthSnapshot_ConcurrentCallsAndReads_CountersAreInternallyConsistent() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Backend/AahClientManagedAlarmEventWriterTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Backend/AahClientManagedAlarmEventWriterTests.cs index 9bc80051..fea71e64 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Backend/AahClientManagedAlarmEventWriterTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Backend/AahClientManagedAlarmEventWriterTests.cs @@ -22,6 +22,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests public sealed class AahClientManagedAlarmEventWriterTests { /// Verifies that an empty batch returns an empty array without invoking the backend. + /// A task that represents the asynchronous operation. [Fact] public async Task Empty_batch_returns_empty_array_without_invoking_backend() { @@ -35,6 +36,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests } /// Verifies that a single acknowledgment outcome maps to true. + /// A task that represents the asynchronous operation. [Fact] public async Task Single_ack_outcome_maps_to_true() { @@ -47,6 +49,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests } /// Verifies that a mixed batch preserves per-slot outcome ordering. + /// A task that represents the asynchronous operation. [Fact] public async Task Mixed_batch_preserves_per_slot_ordering() { @@ -69,6 +72,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests } /// Verifies that backend exceptions mark the whole batch as RetryPlease. + /// A task that represents the asynchronous operation. [Fact] public async Task Backend_exception_marks_whole_batch_RetryPlease() { @@ -85,6 +89,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests } /// Verifies that cancellation propagates from the backend. + /// A task that represents the asynchronous operation. [Fact] public async Task Cancellation_propagates_from_backend() { @@ -97,6 +102,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests } /// Verifies that a backend returning the wrong outcome count degrades to RetryPlease. + /// A task that represents the asynchronous operation. [Fact] public async Task Backend_returning_wrong_count_degrades_to_RetryPlease() { @@ -116,6 +122,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests /// Verifies that a large batch with all acknowledgments returns all true outcomes. /// The batch size to test. + /// A task that represents the asynchronous operation. [Theory] [InlineData(100)] [InlineData(1000)] @@ -139,6 +146,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests /// Verifies that a large batch with alternating outcomes preserves positional ordering. /// The batch size to test. + /// A task that represents the asynchronous operation. [Theory] [InlineData(100)] [InlineData(1000)] @@ -165,6 +173,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests } /// Verifies that retry then succeed correctly simulates cluster failover. + /// A task that represents the asynchronous operation. [Fact] public async Task Backend_retry_then_succeed_simulates_cluster_failover() { @@ -255,10 +264,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests _produce = produce; } - /// Records a call and returns outcomes from the delegate. - /// The events to write. - /// Cancellation token. - /// The outcomes produced by the delegate. + /// public Task WriteBatchAsync( AlarmHistorianEventDto[] events, CancellationToken cancellationToken) { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Backend/HistorianDataSourceConnectFailoverTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Backend/HistorianDataSourceConnectFailoverTests.cs index 799fead6..bf712227 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Backend/HistorianDataSourceConnectFailoverTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Backend/HistorianDataSourceConnectFailoverTests.cs @@ -19,6 +19,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests.Backend; public sealed class HistorianDataSourceConnectFailoverTests { /// Verifies that ReadRaw throws when no nodes are healthy. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadRaw_when_no_nodes_are_healthy_throws_so_IPC_surfaces_Success_false() { @@ -45,6 +46,7 @@ public sealed class HistorianDataSourceConnectFailoverTests } /// Verifies that ReadRaw tries each cluster node in order. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadRaw_tries_each_cluster_node_in_order_until_one_succeeds_or_all_fail() { @@ -71,6 +73,7 @@ public sealed class HistorianDataSourceConnectFailoverTests } /// Verifies that failed nodes are marked in cooldown and not retried immediately. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadRaw_marks_failed_nodes_in_cooldown_so_a_subsequent_call_sees_no_healthy_nodes() { @@ -96,6 +99,7 @@ public sealed class HistorianDataSourceConnectFailoverTests } /// Verifies that ReadEvents uses a separate event connection path. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadEvents_uses_a_separate_event_connection_path() { @@ -131,6 +135,7 @@ public sealed class HistorianDataSourceConnectFailoverTests /// The historian configuration. /// The connection type. /// Whether to open a read-only connection. + /// Never returns; always throws . public HistorianAccess CreateAndConnect( HistorianConfiguration config, HistorianConnectionType type, bool readOnly = true) => throw new InvalidOperationException($"simulated connect failure to {config.ServerName}"); @@ -149,6 +154,7 @@ public sealed class HistorianDataSourceConnectFailoverTests /// The historian configuration. /// The connection type. /// Whether to open a read-only connection. + /// Never returns; always throws . public HistorianAccess CreateAndConnect( HistorianConfiguration config, HistorianConnectionType type, bool readOnly = true) { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Backend/HistorianDataSourceRequestTimeoutTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Backend/HistorianDataSourceRequestTimeoutTests.cs index 66003e5d..6c30a2b6 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Backend/HistorianDataSourceRequestTimeoutTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Backend/HistorianDataSourceRequestTimeoutTests.cs @@ -65,6 +65,7 @@ public sealed class HistorianDataSourceRequestTimeoutTests } /// Verifies short timeout values correctly fire cancellation on the linked token. + /// A task that represents the asynchronous test operation. [Fact] public async Task Small_timeout_cancels_the_linked_token() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Backend/SdkAlarmHistorianWriteBackendTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Backend/SdkAlarmHistorianWriteBackendTests.cs index 59a9d80a..5396a353 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Backend/SdkAlarmHistorianWriteBackendTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Backend/SdkAlarmHistorianWriteBackendTests.cs @@ -27,6 +27,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests // ── Connection-unavailable path (deterministic, no SDK load) ────────── /// Verifies that an empty batch returns an empty outcome array. + /// A task that represents the asynchronous operation. [Fact] public async Task Empty_batch_returns_empty_array() { @@ -40,6 +41,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests } /// Verifies that when all nodes are unreachable, the entire batch is deferred as RetryPlease. + /// A task that represents the asynchronous operation. [Fact] public async Task Unreachable_node_defers_whole_batch_as_RetryPlease() { @@ -57,6 +59,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests } /// Verifies that a large batch with unreachable nodes returns one outcome per event. + /// A task that represents the asynchronous operation. [Fact] public async Task Unreachable_node_large_batch_returns_one_outcome_per_event() { @@ -73,6 +76,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests } /// Verifies that a connection failure marks the node as failed in the endpoint picker. + /// A task that represents the asynchronous operation. [Fact] public async Task Connect_failure_marks_node_failed_in_picker() { @@ -229,6 +233,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests // these need a live AVEVA Historian and are un-skipped during the PR D.1 smoke. /// Verifies that a single alarm event roundtrip returns an Ack outcome. + /// A task that represents the asynchronous operation. [Fact(Skip = "rig-required: needs a live AVEVA Historian — un-skip during the PR D.1 rollout smoke")] public async Task Live_single_event_roundtrip_returns_Ack() { @@ -241,6 +246,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests } /// Verifies that cluster failover rotates from a bad primary node to a secondary node. + /// A task that represents the asynchronous operation. [Fact(Skip = "rig-required: needs a live AVEVA Historian cluster (two nodes) — un-skip during the PR D.1 rollout smoke")] public async Task Live_cluster_failover_primary_bad_rotates_to_secondary() { @@ -311,10 +317,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests /// private sealed class ThrowingConnectionFactory : IHistorianConnectionFactory { - /// Creates and attempts to connect, always throwing a simulated connect failure. - /// The historian configuration specifying the target server. - /// The connection type (Process or Event). - /// Whether to open a read-only connection. + /// public HistorianAccess CreateAndConnect( HistorianConfiguration config, HistorianConnectionType type, bool readOnly = true) => throw new InvalidOperationException($"simulated connect failure to {config.ServerName}"); diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Ipc/PipeRoundTripTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Ipc/PipeRoundTripTests.cs index c62a5c07..c59ac59e 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Ipc/PipeRoundTripTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Ipc/PipeRoundTripTests.cs @@ -93,11 +93,10 @@ public sealed class PipeRoundTripTests public Task> ReadEventsAsync(string? sourceName, DateTime startTime, DateTime endTime, int maxEvents, CancellationToken ct = default) => Task.FromResult(Events); - /// Gets a health snapshot of the fake historian. - /// A health snapshot. + /// public HistorianHealthSnapshot GetHealthSnapshot() => new(); - /// Disposes the fake historian. + /// public void Dispose() { } } @@ -153,6 +152,7 @@ public sealed class PipeRoundTripTests } /// Verifies that raw historian samples round-trip correctly through the frame handler. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReadRaw_RoundTripsSamples() { @@ -183,6 +183,7 @@ public sealed class PipeRoundTripTests } /// Verifies that read failures are properly surfaced as error replies. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReadRaw_FailureSurfacesAsErrorReply() { @@ -199,6 +200,7 @@ public sealed class PipeRoundTripTests } /// Verifies that processed (aggregate) historian samples round-trip correctly through the frame handler. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReadProcessed_RoundTripsBuckets() { @@ -219,6 +221,7 @@ public sealed class PipeRoundTripTests } /// Verifies that at-time historian samples round-trip correctly through the frame handler. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReadAtTime_RoundTripsSamples() { @@ -240,6 +243,7 @@ public sealed class PipeRoundTripTests } /// Verifies that historian events round-trip correctly through the frame handler. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReadEvents_RoundTripsEvents() { @@ -270,6 +274,7 @@ public sealed class PipeRoundTripTests } /// Verifies that alarm events are routed to the writer and per-event status is returned. + /// A task that represents the asynchronous test operation. [Fact] public async Task WriteAlarmEvents_RoutesToWriter_AndReturnsPerEventStatus() { @@ -303,6 +308,7 @@ public sealed class PipeRoundTripTests } /// Verifies that writing alarm events fails cleanly when no writer is configured. + /// A task that represents the asynchronous test operation. [Fact] public async Task WriteAlarmEvents_FailsCleanly_WhenNoWriterConfigured() { @@ -324,6 +330,7 @@ public sealed class PipeRoundTripTests } /// Verifies that frame reader and writer preserve message kind and body through a round trip. + /// A task that represents the asynchronous test operation. [Fact] public async Task FrameReader_FrameWriter_RoundTripPreservesKindAndBody() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Ipc/PipeServerSidRejectTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Ipc/PipeServerSidRejectTests.cs index 5381db78..baa9cebf 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Ipc/PipeServerSidRejectTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests/Ipc/PipeServerSidRejectTests.cs @@ -27,6 +27,7 @@ public sealed class PipeServerSidRejectTests private static readonly ILogger Quiet = Logger.None; /// Verifies that a caller SID mismatch sends HelloAck with reject reason before disconnect. + /// A task that represents the asynchronous operation. [Fact] public async Task Caller_SID_mismatch_sends_HelloAck_with_reject_reason_before_disconnect() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/AddressingGrammarTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/AddressingGrammarTests.cs index a53ccfb2..60acad99 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/AddressingGrammarTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/AddressingGrammarTests.cs @@ -36,6 +36,7 @@ public sealed class AddressingGrammarTests } /// Verifies that 5-digit and 6-digit Modicon formats map to the same wire offset. + /// A task that represents the asynchronous test operation. [Fact] public async Task Modicon_5_And_6_Digit_Both_Map_To_Same_Wire_Offset() { @@ -54,6 +55,7 @@ public sealed class AddressingGrammarTests } /// Verifies that Float32 values roundtrip correctly with CDAB byte order. + /// A task that represents the asynchronous test operation. [Fact] public async Task Float32_With_CDAB_Roundtrips_Through_Wire() { @@ -76,6 +78,7 @@ public sealed class AddressingGrammarTests } /// Verifies that Int16 array reads surface as typed arrays. + /// A task that represents the asynchronous test operation. [Fact] public async Task Int16_Array_Reads_Surface_As_Typed_Array() { @@ -93,6 +96,7 @@ public sealed class AddressingGrammarTests } /// Verifies that block read coalescing reduces PDU count end-to-end. + /// A task that represents the asynchronous test operation. [Fact] public async Task Block_Read_Coalescing_Reduces_PDU_Count_End_To_End() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205BcdQuirkTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205BcdQuirkTests.cs index 8fbf8e40..6e372a6b 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205BcdQuirkTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205BcdQuirkTests.cs @@ -15,6 +15,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests.DL205; public sealed class DL205BcdQuirkTests(ModbusSimulatorFixture sim) { /// Verifies DL205 binary-coded-decimal register decodes as decimal value 1234. + /// A task that represents the asynchronous test. [Fact] public async Task DL205_BCD16_decodes_HR1072_as_decimal_1234() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205CoilMappingTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205CoilMappingTests.cs index b77b635f..25f18e6b 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205CoilMappingTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205CoilMappingTests.cs @@ -15,6 +15,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests.DL205; public sealed class DL205CoilMappingTests(ModbusSimulatorFixture sim) { /// Verifies that DirectLOGIC Y0 output maps to Modbus coil 2048. + /// A task that represents the asynchronous test. [Fact] public async Task DL260_Y0_maps_to_coil_2048() { @@ -42,6 +43,7 @@ public sealed class DL205CoilMappingTests(ModbusSimulatorFixture sim) } /// Verifies that DirectLOGIC C0 relay maps to Modbus coil 3072. + /// A task that represents the asynchronous test. [Fact] public async Task DL260_C0_maps_to_coil_3072() { @@ -69,6 +71,7 @@ public sealed class DL205CoilMappingTests(ModbusSimulatorFixture sim) } /// Verifies that a scratch DirectLOGIC C relay supports write and read operations. + /// A task that represents the asynchronous test. [Fact] public async Task DL260_scratch_Crelay_supports_write_then_read() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205ExceptionCodeTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205ExceptionCodeTests.cs index 4a7e166f..62b7ad32 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205ExceptionCodeTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205ExceptionCodeTests.cs @@ -17,6 +17,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests.DL205; public sealed class DL205ExceptionCodeTests(ModbusSimulatorFixture sim) { /// Verifies that DL205 FC03 reads at unmapped registers return BadOutOfRange status. + /// A task that represents the asynchronous test operation. [Fact] public async Task DL205_FC03_at_unmapped_register_returns_BadOutOfRange() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205FloatCdabQuirkTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205FloatCdabQuirkTests.cs index fecfce23..9805461a 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205FloatCdabQuirkTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205FloatCdabQuirkTests.cs @@ -17,6 +17,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests.DL205; public sealed class DL205FloatCdabQuirkTests(ModbusSimulatorFixture sim) { /// Verifies that DL205 float32 CDAB word order correctly decodes 1.5f from HR1056. + /// A task that represents the asynchronous operation. [Fact] public async Task DL205_Float32_CDAB_decodes_1_5f_from_HR1056() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205SmokeTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205SmokeTests.cs index cbbc910f..fce3ad52 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205SmokeTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205SmokeTests.cs @@ -26,6 +26,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests.DL205; public sealed class DL205SmokeTests(ModbusSimulatorFixture sim) { /// Verifies that DL205 roundtrip write then read of holding register succeeds. + /// A task that represents the asynchronous operation. [Fact] public async Task DL205_roundtrip_write_then_read_of_holding_register() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205StringQuirkTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205StringQuirkTests.cs index 3614de57..daca6489 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205StringQuirkTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205StringQuirkTests.cs @@ -24,6 +24,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests.DL205; public sealed class DL205StringQuirkTests(ModbusSimulatorFixture sim) { /// Verifies that DL205 low-byte-first string packing correctly decodes "Hello" from HR1040. + /// A task that represents the asynchronous operation. [Fact] public async Task DL205_string_low_byte_first_decodes_Hello_from_HR1040() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205VMemoryQuirkTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205VMemoryQuirkTests.cs index 7242d680..5a0a787f 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205VMemoryQuirkTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205VMemoryQuirkTests.cs @@ -17,6 +17,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests.DL205; public sealed class DL205VMemoryQuirkTests(ModbusSimulatorFixture sim) { /// Verifies that DL205 V2000 user memory octal address resolves to PDU address 0x0400. + /// A task that represents the asynchronous test operation. [Fact] public async Task DL205_V2000_user_memory_resolves_to_PDU_0x0400_marker() { @@ -53,6 +54,7 @@ public sealed class DL205VMemoryQuirkTests(ModbusSimulatorFixture sim) } /// Verifies that DL205 V40400 system memory address resolves to PDU address 0x2100. + /// A task that represents the asynchronous test operation. [Fact] public async Task DL205_V40400_system_memory_resolves_to_PDU_0x2100_marker() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205XInputTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205XInputTests.cs index 201a46a8..1d4aa714 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205XInputTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205XInputTests.cs @@ -22,6 +22,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests.DL205; public sealed class DL205XInputTests(ModbusSimulatorFixture sim) { /// Verifies that DL260 X20 octal maps to discrete input 16 and reads the ON state. + /// A task that represents the asynchronous operation. [Fact] public async Task DL260_X20_octal_maps_to_DiscreteInput_16_and_reads_ON() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/ExceptionInjectionTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/ExceptionInjectionTests.cs index 05dee5be..39d7a2a0 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/ExceptionInjectionTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/ExceptionInjectionTests.cs @@ -64,6 +64,7 @@ public sealed class ExceptionInjectionTests(ModbusSimulatorFixture sim) /// The Modbus register address to read. /// The expected OPC UA status code. /// Scenario description for assertion messages. + /// A task that represents the asynchronous test operation. [Theory] [InlineData(1000, StatusBadNotSupported, "exc 0x01 (Illegal Function) -> BadNotSupported")] [InlineData(1001, StatusBadOutOfRange, "exc 0x02 (Illegal Data Address) -> BadOutOfRange")] @@ -82,6 +83,7 @@ public sealed class ExceptionInjectionTests(ModbusSimulatorFixture sim) } /// Verifies that reads at non-injected addresses return Good status. + /// A task that represents the asynchronous test operation. [Fact] public async Task FC03_read_at_non_injected_address_returns_Good() { @@ -98,6 +100,7 @@ public sealed class ExceptionInjectionTests(ModbusSimulatorFixture sim) /// The Modbus register address to write. /// The expected OPC UA status code. /// Scenario description for assertion messages. + /// A task that represents the asynchronous test operation. [Theory] [InlineData(2000, StatusBadDeviceFailure, "exc 0x04 on FC06 -> BadDeviceFailure (CPU in PROGRAM mode)")] [InlineData(2001, StatusBadDeviceFailure, "exc 0x06 on FC06 -> BadDeviceFailure (Server Busy)")] diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Mitsubishi/MitsubishiProfile.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Mitsubishi/MitsubishiProfile.cs index f831e5e0..407feba3 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Mitsubishi/MitsubishiProfile.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Mitsubishi/MitsubishiProfile.cs @@ -26,6 +26,7 @@ public static class MitsubishiProfile /// Builds Modbus driver options configured for the Mitsubishi profile. /// The Modbus server hostname or IP address. /// The Modbus server port number. + /// A instance configured for the Mitsubishi profile. public static ModbusDriverOptions BuildOptions(string host, int port) => new() { Host = host, diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Mitsubishi/MitsubishiQuirkTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Mitsubishi/MitsubishiQuirkTests.cs index 8d28ecbc..0172908e 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Mitsubishi/MitsubishiQuirkTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Mitsubishi/MitsubishiQuirkTests.cs @@ -20,6 +20,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests.Mitsubishi; public sealed class MitsubishiQuirkTests(ModbusSimulatorFixture sim) { /// Verifies that Mitsubishi D0 register fingerprint reads the expected value 0x1234. + /// A task that represents the asynchronous operation. [Fact] public async Task Mitsubishi_D0_fingerprint_reads_0x1234() { @@ -36,6 +37,7 @@ public sealed class MitsubishiQuirkTests(ModbusSimulatorFixture sim) } /// Verifies that Mitsubishi Float32 with CDAB word order decodes correctly to 1.5 from D100. + /// A task that represents the asynchronous operation. [Fact] public async Task Mitsubishi_Float32_CDAB_decodes_1_5f_from_D100() { @@ -62,6 +64,7 @@ public sealed class MitsubishiQuirkTests(ModbusSimulatorFixture sim) } /// Verifies that Mitsubishi D registers store binary values, not BCD. + /// A task that represents the asynchronous operation. [Fact] public async Task Mitsubishi_D10_is_binary_not_BCD() { @@ -81,6 +84,7 @@ public sealed class MitsubishiQuirkTests(ModbusSimulatorFixture sim) } /// Verifies that reading a binary D register as BCD throws when the value contains non-decimal nibbles. + /// A task that represents the asynchronous operation. [Fact] public async Task Mitsubishi_D10_as_BCD_throws_because_nibble_is_non_decimal() { @@ -102,6 +106,7 @@ public sealed class MitsubishiQuirkTests(ModbusSimulatorFixture sim) } /// Verifies that Mitsubishi Q/L/iQ-R X inputs use hex addressing and X210 maps correctly. + /// A task that represents the asynchronous operation. [Fact] public async Task Mitsubishi_QLiQR_X210_hex_maps_to_DI_528_reads_ON() { @@ -134,6 +139,7 @@ public sealed class MitsubishiQuirkTests(ModbusSimulatorFixture sim) } /// Verifies that Mitsubishi M512 relay maps to coil address 512 and reads ON. + /// A task that represents the asynchronous operation. [Fact] public async Task Mitsubishi_M512_maps_to_coil_512_reads_ON() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Mitsubishi/MitsubishiSmokeTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Mitsubishi/MitsubishiSmokeTests.cs index 6237650e..6e0b7df6 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Mitsubishi/MitsubishiSmokeTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Mitsubishi/MitsubishiSmokeTests.cs @@ -16,6 +16,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests.Mitsubishi; public sealed class MitsubishiSmokeTests(ModbusSimulatorFixture sim) { /// Verifies end-to-end write and read of a Mitsubishi holding register via the Modbus driver. + /// A task that represents the asynchronous operation. [Fact] public async Task Mitsubishi_roundtrip_write_then_read_of_holding_register() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/ModbusSimulatorFixture.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/ModbusSimulatorFixture.cs index afa06eef..2f5d5d5f 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/ModbusSimulatorFixture.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/ModbusSimulatorFixture.cs @@ -82,7 +82,8 @@ public sealed class ModbusSimulatorFixture : IAsyncDisposable } } - /// + /// Disposes the fixture asynchronously (no-op for this fixture). + /// A completed value task. public ValueTask DisposeAsync() => ValueTask.CompletedTask; } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/S7/S7_1500SmokeTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/S7/S7_1500SmokeTests.cs index 034557f1..752681c3 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/S7/S7_1500SmokeTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/S7/S7_1500SmokeTests.cs @@ -25,6 +25,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests.S7; public sealed class S7_1500SmokeTests(ModbusSimulatorFixture sim) { /// Verifies that an S7-1500 roundtrip write then read of a holding register succeeds. + /// A task that represents the asynchronous test. [Fact] public async Task S7_1500_roundtrip_write_then_read_of_holding_register() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/S7/S7_ByteOrderTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/S7/S7_ByteOrderTests.cs index f604dbf3..a93a9d4d 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/S7/S7_ByteOrderTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/S7/S7_ByteOrderTests.cs @@ -17,6 +17,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests.S7; public sealed class S7_ByteOrderTests(ModbusSimulatorFixture sim) { /// Verifies that S7 Float32 with ABCD byte order correctly decodes 1.5f from HR100. + /// A task that represents the asynchronous operation. [Fact] public async Task S7_Float32_ABCD_decodes_1_5f_from_HR100() { @@ -63,6 +64,7 @@ public sealed class S7_ByteOrderTests(ModbusSimulatorFixture sim) } /// Verifies that S7 Int32 with ABCD byte order correctly decodes 0x12345678 from HR300. + /// A task that represents the asynchronous operation. [Fact] public async Task S7_Int32_ABCD_decodes_0x12345678_from_HR300() { @@ -98,6 +100,7 @@ public sealed class S7_ByteOrderTests(ModbusSimulatorFixture sim) } /// Verifies that S7 DB1 fingerprint marker reads 0xABCD from HR0. + /// A task that represents the asynchronous operation. [Fact] public async Task S7_DB1_fingerprint_marker_at_HR0_reads_0xABCD() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/MelsecAddressTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/MelsecAddressTests.cs index b16b6322..1ea5add1 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/MelsecAddressTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/MelsecAddressTests.cs @@ -9,6 +9,8 @@ public sealed class MelsecAddressTests // --- X / Y hex vs octal family trap --- /// Verifies that Q-series and iQR family X inputs parse as hexadecimal. + /// The X input address string to parse. + /// The expected discrete coil address after parsing. [Theory] [InlineData("X0", (ushort)0)] [InlineData("X9", (ushort)9)] @@ -22,6 +24,8 @@ public sealed class MelsecAddressTests => MelsecAddress.XInputToDiscrete(x, MelsecFamily.Q_L_iQR).ShouldBe(expected); /// Verifies that F-series and iQF family X inputs parse as octal. + /// The X input address string to parse. + /// The expected discrete coil address after parsing. [Theory] [InlineData("X0", (ushort)0)] [InlineData("X7", (ushort)7)] @@ -32,6 +36,8 @@ public sealed class MelsecAddressTests => MelsecAddress.XInputToDiscrete(x, MelsecFamily.F_iQF).ShouldBe(expected); /// Verifies that Q-series and iQR family Y outputs parse as hexadecimal. + /// The Y output address string to parse. + /// The expected coil address after parsing. [Theory] [InlineData("Y0", (ushort)0)] [InlineData("Y1F", (ushort)31)] @@ -39,6 +45,8 @@ public sealed class MelsecAddressTests => MelsecAddress.YOutputToCoil(y, MelsecFamily.Q_L_iQR).ShouldBe(expected); /// Verifies that F-series and iQF family Y outputs parse as octal. + /// The Y output address string to parse. + /// The expected coil address after parsing. [Theory] [InlineData("Y0", (ushort)0)] [InlineData("Y17", (ushort)15)] @@ -58,6 +66,7 @@ public sealed class MelsecAddressTests } /// Verifies that non-octal X input addresses are rejected for F-series and iQF families. + /// An invalid X input address string that should be rejected. [Theory] [InlineData("X8")] // 8 is non-octal [InlineData("X12G")] // G is non-hex @@ -65,6 +74,7 @@ public sealed class MelsecAddressTests => Should.Throw(() => MelsecAddress.XInputToDiscrete(bad, MelsecFamily.F_iQF)); /// Verifies that non-hexadecimal X input addresses are rejected for Q-series and iQR families. + /// An invalid X input address string that should be rejected. [Theory] [InlineData("X12G")] public void XInputToDiscrete_QLiQR_rejects_non_hex(string bad) @@ -84,6 +94,8 @@ public sealed class MelsecAddressTests // --- M-relay (decimal, both families) --- /// Verifies that M relay addresses parse as decimal. + /// The M relay address string to parse. + /// The expected coil address after parsing. [Theory] [InlineData("M0", (ushort)0)] [InlineData("M10", (ushort)10)] // M addresses are DECIMAL, not hex or octal @@ -109,6 +121,8 @@ public sealed class MelsecAddressTests // --- D-register (decimal, both families) --- /// Verifies that D register addresses parse as decimal. + /// The D register address string to parse. + /// The expected holding register address after parsing. [Theory] [InlineData("D0", (ushort)0)] [InlineData("D100", (ushort)100)] diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusArrayTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusArrayTests.cs index 25e00a65..b332d012 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusArrayTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusArrayTests.cs @@ -22,6 +22,7 @@ public sealed class ModbusArrayTests } /// Verifies reading an Int16 array returns a typed array. + /// A task that represents the asynchronous test operation. [Fact] public async Task Read_Int16_Array_Returns_Typed_Array() { @@ -36,6 +37,7 @@ public sealed class ModbusArrayTests } /// Verifies reading a Float32 array with word swap returns a typed array. + /// A task that represents the asynchronous test operation. [Fact] public async Task Read_Float32_Array_Returns_Typed_Array_With_WordSwap() { @@ -63,6 +65,7 @@ public sealed class ModbusArrayTests } /// Verifies reading a coil array returns a bool array. + /// A task that represents the asynchronous test operation. [Fact] public async Task Read_Coil_Array_Returns_Bool_Array() { @@ -78,6 +81,7 @@ public sealed class ModbusArrayTests } /// Verifies writing an Int16 array lands contiguously in the register bank. + /// A task that represents the asynchronous test operation. [Fact] public async Task Write_Int16_Array_Lands_Contiguous_In_Bank() { @@ -96,6 +100,7 @@ public sealed class ModbusArrayTests } /// Verifies writing a coil array packs bits in LSB-first order. + /// A task that represents the asynchronous test operation. [Fact] public async Task Write_Coil_Array_Packs_LSB_First() { @@ -114,6 +119,7 @@ public sealed class ModbusArrayTests } /// Verifies writing an array with mismatched length surfaces an error. + /// A task that represents the asynchronous test operation. [Fact] public async Task Write_Array_Mismatch_Length_Surfaces_Error() { @@ -129,6 +135,7 @@ public sealed class ModbusArrayTests } /// Verifies discovery surfaces IsArray and ArrayDim correctly. + /// A task that represents the asynchronous test operation. [Fact] public async Task Discovery_Surfaces_IsArray_And_ArrayDim() { @@ -145,6 +152,7 @@ public sealed class ModbusArrayTests } /// Verifies scalar tag discovery keeps IsArray false. + /// A task that represents the asynchronous test operation. [Fact] public async Task Scalar_Tag_Discovery_Stays_NonArray() { @@ -164,39 +172,27 @@ public sealed class ModbusArrayTests /// List to capture discovered attributes into. private sealed class RecordingBuilder(List captured) : IAddressSpaceBuilder { - /// Creates a folder in the address space. - /// The browse name of the folder. - /// The display name of the folder. - /// This builder instance. + /// public IAddressSpaceBuilder Folder(string browseName, string displayName) => this; - /// Creates a variable in the address space. - /// The browse name of the variable. - /// The display name of the variable. - /// The attribute information. - /// A variable handle. + /// public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo attributeInfo) { captured.Add(attributeInfo); return new StubHandle(browseName); } - /// Adds a property to the current node. - /// The browse name of the property. - /// The data type of the property. - /// The property value. + /// public void AddProperty(string browseName, DriverDataType dataType, object? value) { } /// Stub variable handle for testing. /// The full reference of the handle. private sealed class StubHandle(string fullRef) : IVariableHandle { - /// Gets the full reference. + /// public string FullReference => fullRef; - /// Marks this variable as an alarm condition. - /// The alarm condition information. - /// An alarm condition sink. + /// public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) => throw new NotSupportedException("RecordingBuilder doesn't model alarms"); } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusBitRmwTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusBitRmwTests.cs index 29581da1..4746590d 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusBitRmwTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusBitRmwTests.cs @@ -14,16 +14,10 @@ public sealed class ModbusBitRmwTests public readonly ushort[] HoldingRegisters = new ushort[256]; public readonly List Pdus = new(); - /// Connects asynchronously (no-op for fake). - /// Cancellation token (unused). - /// A completed task. + /// public Task ConnectAsync(CancellationToken ct) => Task.CompletedTask; - /// Sends a Modbus PDU and returns a response. - /// The Modbus unit ID (unused). - /// The protocol data unit to send. - /// Cancellation token (unused). - /// A task containing the response PDU. + /// public Task SendAsync(byte unitId, byte[] pdu, CancellationToken ct) { Pdus.Add(pdu); @@ -71,6 +65,7 @@ public sealed class ModbusBitRmwTests } /// Verifies that setting a bit reads the current register, ORs the bit, and writes back. + /// A task that represents the asynchronous operation. [Fact] public async Task Bit_set_reads_current_register_ORs_bit_writes_back() { @@ -90,6 +85,7 @@ public sealed class ModbusBitRmwTests } /// Verifies that clearing a bit reads the current register, ANDs the bit off, and writes back. + /// A task that represents the asynchronous operation. [Fact] public async Task Bit_clear_reads_current_register_ANDs_bit_off_writes_back() { @@ -104,6 +100,7 @@ public sealed class ModbusBitRmwTests } /// Verifies that concurrent bit writes to the same register preserve all updates via serialization. + /// A task that represents the asynchronous operation. [Fact] public async Task Concurrent_bit_writes_to_same_register_preserve_all_updates() { @@ -123,6 +120,7 @@ public sealed class ModbusBitRmwTests } /// Verifies that bit writes to different registers proceed in parallel without contention. + /// A task that represents the asynchronous operation. [Fact] public async Task Bit_write_on_different_registers_proceeds_in_parallel_without_contention() { @@ -140,6 +138,7 @@ public sealed class ModbusBitRmwTests } /// Verifies that bit writes preserve other bits in the same register. + /// A task that represents the asynchronous operation. [Fact] public async Task Bit_write_preserves_other_bits_in_the_same_register() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusCapTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusCapTests.cs index ddb768bf..3a970c42 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusCapTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusCapTests.cs @@ -55,11 +55,13 @@ public sealed class ModbusCapTests return Task.FromException(new ModbusException(fc, 0x01, $"fc={fc} unsupported")); } - /// + /// Releases resources used by this transport instance. + /// A completed value task. public ValueTask DisposeAsync() => ValueTask.CompletedTask; } /// Verifies that a read within the cap issues a single FC03 request. + /// A task that represents the asynchronous operation. [Fact] public async Task Read_within_cap_issues_single_FC03_request() { @@ -77,6 +79,7 @@ public sealed class ModbusCapTests } /// Verifies that a read above cap splits into two FC03 requests. + /// A task that represents the asynchronous operation. [Fact] public async Task Read_above_cap_splits_into_two_FC03_requests() { @@ -112,6 +115,7 @@ public sealed class ModbusCapTests } /// Verifies that read cap honors Mitsubishi lower cap of 64 registers. + /// A task that represents the asynchronous operation. [Fact] public async Task Read_cap_honors_Mitsubishi_lower_cap_of_64() { @@ -131,6 +135,7 @@ public sealed class ModbusCapTests } /// Verifies that write exceeding cap throws instead of splitting. + /// A task that represents the asynchronous operation. [Fact] public async Task Write_exceeding_cap_throws_instead_of_splitting() { @@ -155,6 +160,7 @@ public sealed class ModbusCapTests } /// Verifies that write within cap proceeds normally. + /// A task that represents the asynchronous operation. [Fact] public async Task Write_within_cap_proceeds_normally() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusCoalescingAutoRecoveryTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusCoalescingAutoRecoveryTests.cs index e821208a..ec3a3dc4 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusCoalescingAutoRecoveryTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusCoalescingAutoRecoveryTests.cs @@ -49,11 +49,13 @@ public sealed class ModbusCoalescingAutoRecoveryTests default: return Task.FromResult(new byte[] { pdu[0], 0, 0 }); } } - /// + /// Releases resources used by this transport instance. + /// A completed value task. public ValueTask DisposeAsync() => ValueTask.CompletedTask; } /// Verifies that the first failure falls back to per-tag reads in the same scan. + /// A task that represents the asynchronous operation. [Fact] public async Task First_Failure_Falls_Back_To_PerTag_Same_Scan() { @@ -82,6 +84,7 @@ public sealed class ModbusCoalescingAutoRecoveryTests } /// Verifies that the second scan skips coalesced reads of prohibited ranges. + /// A task that represents the asynchronous operation. [Fact] public async Task Second_Scan_Skips_Coalesced_Read_Of_Prohibited_Range() { @@ -112,6 +115,7 @@ public sealed class ModbusCoalescingAutoRecoveryTests } /// Verifies that reprobe clears prohibition when the range becomes healthy. + /// A task that represents the asynchronous operation. [Fact] public async Task Reprobe_Clears_Prohibition_When_Range_Becomes_Healthy() { @@ -142,6 +146,7 @@ public sealed class ModbusCoalescingAutoRecoveryTests } /// Verifies that reprobe leaves prohibition in place when the range is still bad. + /// A task that represents the asynchronous operation. [Fact] public async Task Reprobe_Leaves_Prohibition_When_Range_Is_Still_Bad() { @@ -166,6 +171,7 @@ public sealed class ModbusCoalescingAutoRecoveryTests } /// Verifies that GetAutoProhibitedRanges surfaces an operator-visible snapshot. + /// A task that represents the asynchronous operation. [Fact] public async Task GetAutoProhibitedRanges_Surfaces_Operator_Visible_Snapshot() { @@ -199,6 +205,7 @@ public sealed class ModbusCoalescingAutoRecoveryTests } /// Verifies that tags outside prohibited ranges still coalesce. + /// A task that represents the asynchronous operation. [Fact] public async Task Tags_Outside_Prohibited_Range_Still_Coalesce() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusCoalescingBisectionTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusCoalescingBisectionTests.cs index 5351e005..20d98d62 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusCoalescingBisectionTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusCoalescingBisectionTests.cs @@ -22,15 +22,9 @@ public sealed class ModbusCoalescingBisectionTests { /// Gets or sets the protected address that will cause read failures. public ushort ProtectedAddress { get; set; } = ushort.MaxValue; - /// Simulates connecting to the Modbus device. - /// The cancellation token. - /// A completed task. + /// public Task ConnectAsync(CancellationToken ct) => Task.CompletedTask; - /// Simulates sending a Modbus PDU and failing if the protected address is accessed. - /// The Modbus unit ID. - /// The protocol data unit. - /// The cancellation token. - /// The response PDU or an exception if the protected address is accessed. + /// public Task SendAsync(byte unitId, byte[] pdu, CancellationToken ct) { var addr = (ushort)((pdu[1] << 8) | pdu[2]); @@ -54,6 +48,7 @@ public sealed class ModbusCoalescingBisectionTests } /// Verifies that bisection narrows a multi-register prohibition on each reprobe cycle. + /// A task that represents the asynchronous operation. [Fact] public async Task Bisection_Narrows_Multi_Register_Prohibition_Per_Reprobe() { @@ -98,6 +93,7 @@ public sealed class ModbusCoalescingBisectionTests } /// Verifies that the prohibition is cleared when both bisected halves succeed in recovery. + /// A task that represents the asynchronous operation. [Fact] public async Task Bisection_Clears_When_Both_Halves_Are_Healthy() { @@ -127,6 +123,7 @@ public sealed class ModbusCoalescingBisectionTests } /// Verifies that the prohibition splits into two entries when both bisected halves still fail. + /// A task that represents the asynchronous operation. [Fact] public async Task Bisection_Splits_Into_Two_When_Both_Halves_Still_Fail() { @@ -160,15 +157,9 @@ public sealed class ModbusCoalescingBisectionTests private sealed class TwoHoleTransport : IModbusTransport { public readonly HashSet ProtectedAddresses = new(); - /// Simulates connecting to the Modbus device. - /// The cancellation token. - /// A completed task. + /// public Task ConnectAsync(CancellationToken ct) => Task.CompletedTask; - /// Simulates sending a Modbus PDU and failing if any protected address is accessed. - /// The Modbus unit ID. - /// The protocol data unit. - /// The cancellation token. - /// The response PDU or an exception if a protected address is accessed. + /// public Task SendAsync(byte unitId, byte[] pdu, CancellationToken ct) { var addr = (ushort)((pdu[1] << 8) | pdu[2]); diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusCoalescingTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusCoalescingTests.cs index da79cb6a..ebcee591 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusCoalescingTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusCoalescingTests.cs @@ -15,13 +15,9 @@ public sealed class ModbusCoalescingTests private sealed class CountingTransport : IModbusTransport { public readonly List<(byte Unit, byte Fc, ushort Address, ushort Quantity)> Reads = new(); - /// Establishes a connection asynchronously. - /// Cancellation token for the operation. + /// public Task ConnectAsync(CancellationToken ct) => Task.CompletedTask; - /// Sends a Modbus PDU and receives the response. - /// The Modbus unit identifier. - /// The Protocol Data Unit to send. - /// Cancellation token for the operation. + /// public Task SendAsync(byte unitId, byte[] pdu, CancellationToken ct) { var addr = (ushort)((pdu[1] << 8) | pdu[2]); @@ -38,11 +34,13 @@ public sealed class ModbusCoalescingTests default: return Task.FromResult(new byte[] { pdu[0], 0, 0 }); } } - /// Disposes the transport asynchronously. + /// Disposes the transport; no-op for this in-memory fake. + /// A completed value task. public ValueTask DisposeAsync() => ValueTask.CompletedTask; } /// Verifies that MaxReadGap=0 defaults to per-tag reads without coalescing. + /// A task that represents the asynchronous test operation. [Fact] public async Task MaxReadGap_Zero_Defaults_To_Per_Tag_Reads() { @@ -62,6 +60,7 @@ public sealed class ModbusCoalescingTests } /// Verifies that MaxReadGap bridges adjacent tags into a single read. + /// A task that represents the asynchronous test operation. [Fact] public async Task MaxReadGap_Bridges_Two_Adjacent_Tags_Into_One_Read() { @@ -84,6 +83,7 @@ public sealed class ModbusCoalescingTests } /// Verifies that MaxReadGap splits blocks when gaps exceed threshold. + /// A task that represents the asynchronous test operation. [Fact] public async Task MaxReadGap_Splits_When_Gap_Exceeds_Threshold() { @@ -104,6 +104,7 @@ public sealed class ModbusCoalescingTests } /// Verifies that tags with CoalesceProhibited are read separately. + /// A task that represents the asynchronous test operation. [Fact] public async Task CoalesceProhibited_Tag_Reads_Alone() { @@ -125,6 +126,7 @@ public sealed class ModbusCoalescingTests } /// Verifies that coalescing does not cross unit ID boundaries. + /// A task that represents the asynchronous test operation. [Fact] public async Task Coalescing_Does_Not_Cross_UnitId_Boundaries() { @@ -145,6 +147,7 @@ public sealed class ModbusCoalescingTests } /// Verifies that coalescing splits blocks exceeding MaxRegistersPerRead. + /// A task that represents the asynchronous test operation. [Fact] public async Task Coalescing_Splits_Block_That_Exceeds_MaxRegistersPerRead() { @@ -166,6 +169,7 @@ public sealed class ModbusCoalescingTests } /// Verifies that coalesced reads surface each tag value independently. + /// A task that represents the asynchronous test operation. [Fact] public async Task Coalesced_Read_Surfaces_Each_Tag_Value_Independently() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusDriverTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusDriverTests.cs index 8b445a81..bf294456 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusDriverTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusDriverTests.cs @@ -24,15 +24,11 @@ public sealed class ModbusDriverTests /// Gets or sets a value indicating whether connect operations should fail. public bool ForceConnectFail { get; set; } - /// Initiates a connection to the Modbus server. - /// Cancellation token. + /// public Task ConnectAsync(CancellationToken ct) => ForceConnectFail ? Task.FromException(new InvalidOperationException("connect refused")) : Task.CompletedTask; - /// Sends a Modbus PDU and receives the response. - /// Modbus unit ID. - /// Protocol data unit bytes to send. - /// Cancellation token. + /// public Task SendAsync(byte unitId, byte[] pdu, CancellationToken ct) { var fc = pdu[0]; @@ -112,6 +108,7 @@ public sealed class ModbusDriverTests } /// Disposes the transport asynchronously. + /// A task that represents the asynchronous operation. public ValueTask DisposeAsync() => ValueTask.CompletedTask; } @@ -124,6 +121,7 @@ public sealed class ModbusDriverTests } /// Verifies that Initialize connects and populates the tag map. + /// A task that represents the asynchronous operation. [Fact] public async Task Initialize_connects_and_populates_tag_map() { @@ -135,6 +133,7 @@ public sealed class ModbusDriverTests } /// Verifies that reading Int16 holding registers returns big-endian values correctly. + /// A task that represents the asynchronous operation. [Fact] public async Task Read_Int16_holding_register_returns_BigEndian_value() { @@ -148,6 +147,7 @@ public sealed class ModbusDriverTests } /// Verifies that reading Float32 values spans two registers in big-endian format. + /// A task that represents the asynchronous operation. [Fact] public async Task Read_Float32_spans_two_registers_BigEndian() { @@ -165,6 +165,7 @@ public sealed class ModbusDriverTests } /// Verifies that reading coils returns boolean values. + /// A task that represents the asynchronous operation. [Fact] public async Task Read_Coil_returns_boolean() { @@ -177,6 +178,7 @@ public sealed class ModbusDriverTests } /// Verifies that reading unknown tags returns BadNodeIdUnknown status instead of throwing. + /// A task that represents the asynchronous operation. [Fact] public async Task Unknown_tag_returns_BadNodeIdUnknown_not_an_exception() { @@ -188,6 +190,7 @@ public sealed class ModbusDriverTests } /// Verifies that writing UInt16 holding registers round-trips correctly. + /// A task that represents the asynchronous operation. [Fact] public async Task Write_UInt16_holding_register_roundtrips() { @@ -200,6 +203,7 @@ public sealed class ModbusDriverTests } /// Verifies that writing Float32 values uses function code 16 (WriteMultipleRegisters). + /// A task that represents the asynchronous operation. [Fact] public async Task Write_Float32_uses_FC16_WriteMultipleRegisters() { @@ -218,6 +222,7 @@ public sealed class ModbusDriverTests } /// Verifies that writing to input registers returns BadNotWritable status. + /// A task that represents the asynchronous operation. [Fact] public async Task Write_to_InputRegister_returns_BadNotWritable() { @@ -229,6 +234,7 @@ public sealed class ModbusDriverTests } /// Verifies that Discover streams one folder per driver with a variable per tag. + /// A task that represents the asynchronous operation. [Fact] public async Task Discover_streams_one_folder_per_driver_with_a_variable_per_tag() { @@ -250,6 +256,7 @@ public sealed class ModbusDriverTests } /// Verifies that Discover propagates WriteIdempotent from tag to attribute info. + /// A task that represents the asynchronous operation. [Fact] public async Task Discover_propagates_WriteIdempotent_from_tag_to_attribute_info() { @@ -278,41 +285,31 @@ public sealed class ModbusDriverTests /// Gets the list of discovered variables. public List<(string BrowseName, DriverAttributeInfo Info)> Variables { get; } = new(); - /// Records a folder in the address space. - /// Folder browse name. - /// Folder display name. + /// public IAddressSpaceBuilder Folder(string browseName, string displayName) { Folders.Add((browseName, displayName)); return this; } - /// Records a variable in the address space. - /// Variable browse name. - /// Variable display name. - /// Driver attribute information. + /// public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo info) { Variables.Add((browseName, info)); return new Handle(info.FullName); } - /// Adds a property (no-op for recording). - /// Property name (unused). - /// Property data type (unused). - /// Property value (unused). + /// public void AddProperty(string _, DriverDataType __, object? ___) { } /// Handle to a discovered variable. private sealed class Handle(string fullRef) : IVariableHandle { - /// Gets the full reference name. + /// public string FullReference => fullRef; - /// Marks this variable as an alarm condition. - /// Alarm condition information. + /// public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) => new NullSink(); } /// No-op alarm condition sink for testing. private sealed class NullSink : IAlarmConditionSink { - /// Handles alarm transitions (no-op). - /// Alarm event arguments. + /// public void OnTransition(AlarmEventArgs args) { } } } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusExceptionMapperTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusExceptionMapperTests.cs index 7a13f5b0..a45a1ca8 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusExceptionMapperTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusExceptionMapperTests.cs @@ -33,22 +33,20 @@ public sealed class ModbusExceptionMapperTests /// Test transport that raises Modbus exceptions on send. private sealed class ExceptionRaisingTransport(byte exceptionCode) : IModbusTransport { - /// Completes immediately. - /// Cancellation token. + /// public Task ConnectAsync(CancellationToken ct) => Task.CompletedTask; - /// Returns a failed task with a Modbus exception. - /// Modbus unit identifier. - /// Protocol data unit to send. - /// Cancellation token. + /// public Task SendAsync(byte unitId, byte[] pdu, CancellationToken ct) => Task.FromException(new ModbusException(pdu[0], exceptionCode, $"fc={pdu[0]} code={exceptionCode}")); /// Completes immediately. + /// A completed value task. public ValueTask DisposeAsync() => ValueTask.CompletedTask; } /// Verifies that exception 0x02 surfaces as BadOutOfRange, not BadInternalError. + /// A task that represents the asynchronous operation. [Fact] public async Task Read_surface_exception_02_as_BadOutOfRange_not_BadInternalError() { @@ -63,6 +61,7 @@ public sealed class ModbusExceptionMapperTests } /// Verifies that exception 0x04 surfaces as BadDeviceFailure. + /// A task that represents the asynchronous operation. [Fact] public async Task Write_surface_exception_04_as_BadDeviceFailure() { @@ -82,22 +81,20 @@ public sealed class ModbusExceptionMapperTests /// Test transport that raises non-Modbus exceptions on send. private sealed class NonModbusFailureTransport : IModbusTransport { - /// Completes immediately. - /// Cancellation token. + /// public Task ConnectAsync(CancellationToken ct) => Task.CompletedTask; - /// Returns a failed task with a non-Modbus exception. - /// Modbus unit identifier. - /// Protocol data unit to send. - /// Cancellation token. + /// public Task SendAsync(byte unitId, byte[] pdu, CancellationToken ct) => Task.FromException(new EndOfStreamException("socket closed mid-response")); /// Completes immediately. + /// A completed value task. public ValueTask DisposeAsync() => ValueTask.CompletedTask; } /// Verifies that non-Modbus transport failures surface as BadCommunicationError. + /// A task that represents the asynchronous operation. [Fact] public async Task Read_non_modbus_failure_maps_to_BadCommunicationError_not_BadInternalError() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusLifecycleHygieneTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusLifecycleHygieneTests.cs index 007ad260..628d6c2a 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusLifecycleHygieneTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusLifecycleHygieneTests.cs @@ -23,14 +23,10 @@ public sealed class ModbusLifecycleHygieneTests public int DisposeCount; public int SendCount; - /// Establishes a connection asynchronously. - /// Cancellation token for the operation. + /// public Task ConnectAsync(CancellationToken ct) { Interlocked.Increment(ref ConnectCount); return Task.CompletedTask; } - /// Sends a Modbus PDU and receives the response. - /// The Modbus unit identifier. - /// The Protocol Data Unit to send. - /// Cancellation token for the operation. + /// public Task SendAsync(byte unitId, byte[] pdu, CancellationToken ct) { Interlocked.Increment(ref SendCount); @@ -71,7 +67,8 @@ public sealed class ModbusLifecycleHygieneTests } } - /// Disposes the transport asynchronously. + /// Releases the transport and increments the dispose counter. + /// A completed value task. public ValueTask DisposeAsync() { Interlocked.Increment(ref DisposeCount); return ValueTask.CompletedTask; } } @@ -87,6 +84,7 @@ public sealed class ModbusLifecycleHygieneTests // -------------------- Finding -002 / -012 (2) -------------------- /// Verifies that reinitialization clears stale tag cache entries. + /// A task that represents the asynchronous operation. [Fact] public async Task Reinitialize_clears_stale_tagsByName_entries() { @@ -108,6 +106,7 @@ public sealed class ModbusLifecycleHygieneTests } /// Verifies that reinitialization clears the deadband and write-suppression caches. + /// A task that represents the asynchronous operation. [Fact] public async Task Reinitialize_clears_lastPublished_and_lastWritten_caches() { @@ -149,6 +148,7 @@ public sealed class ModbusLifecycleHygieneTests // -------------------- Finding -004 / -012 (4) -------------------- /// Verifies that DisposeAsync without Shutdown stops probe loops and tears down the transport. + /// A task that represents the asynchronous operation. [Fact] public async Task DisposeAsync_without_explicit_Shutdown_tears_down_probe_loop_and_transport() { @@ -187,6 +187,7 @@ public sealed class ModbusLifecycleHygieneTests } /// Verifies that DisposeAsync disposes the poll engine so subscriptions stop. + /// A task that represents the asynchronous operation. [Fact] public async Task DisposeAsync_disposes_the_pollEngine_so_subscriptions_stop() { @@ -232,13 +233,9 @@ public sealed class ModbusLifecycleHygieneTests /// How many bytes to return — anything < 2 + bytecount is malformed. public int ResponseBytes { get; set; } = 1; // just the fc byte, no bytecount - /// Establishes a connection asynchronously. - /// Cancellation token for the operation. + /// public Task ConnectAsync(CancellationToken ct) => Task.CompletedTask; - /// Sends a Modbus PDU and receives the response. - /// The Modbus unit identifier. - /// The Protocol Data Unit to send. - /// Cancellation token for the operation. + /// public Task SendAsync(byte unitId, byte[] pdu, CancellationToken ct) { var resp = new byte[ResponseBytes]; @@ -246,11 +243,13 @@ public sealed class ModbusLifecycleHygieneTests if (ResponseBytes >= 2) resp[1] = 4; // claim 4 bytes of payload but provide none return Task.FromResult(resp); } - /// Disposes the transport asynchronously. + /// Releases the transport; this implementation is a no-op. + /// A completed value task. public ValueTask DisposeAsync() => ValueTask.CompletedTask; } /// Verifies that short response PDUs surface as BadCommunicationError, not IndexOutOfRangeException. + /// A task that represents the asynchronous operation. [Fact] public async Task Short_response_PDU_surfaces_as_BadCommunicationError_not_an_IndexOutOfRangeException() { @@ -269,6 +268,7 @@ public sealed class ModbusLifecycleHygieneTests } /// Verifies that response payloads truncated below declared byte count surface as BadCommunicationError. + /// A task that represents the asynchronous operation. [Fact] public async Task Response_payload_truncated_below_declared_byteCount_surfaces_as_BadCommunicationError() { @@ -317,16 +317,13 @@ public sealed class ModbusLifecycleHygieneTests /// private sealed class EmptyBitTransport : IModbusTransport { - /// Returns a completed task without performing any connection. - /// The cancellation token for the operation. + /// public Task ConnectAsync(CancellationToken ct) => Task.CompletedTask; - /// Returns a response with zero-byte payload to simulate empty bitmap. - /// The Modbus unit ID. - /// The protocol data unit being sent. - /// The cancellation token for the operation. + /// public Task SendAsync(byte unitId, byte[] pdu, CancellationToken ct) => Task.FromResult(new byte[] { pdu[0], 0 }); - /// Completes the disposal without doing any work. + /// Releases the transport; this implementation is a no-op. + /// A completed value task. public ValueTask DisposeAsync() => ValueTask.CompletedTask; } @@ -340,6 +337,7 @@ public sealed class ModbusLifecycleHygieneTests /// record so reference-assignment atomicity already prevents tearing; the test guards /// against future regressions to a struct-typed health surface). /// + /// A task that represents the asynchronous operation. [Fact] public async Task GetHealth_under_concurrent_pressure_always_returns_a_complete_snapshot() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusLoggerInjectionTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusLoggerInjectionTests.cs index 0ec88d81..d815435f 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusLoggerInjectionTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusLoggerInjectionTests.cs @@ -42,7 +42,7 @@ public sealed class ModbusLoggerInjectionTests private sealed class NullScope : IDisposable { public static readonly NullScope Instance = new(); - /// + /// Disposes the scope (no-op). public void Dispose() { } } } @@ -52,15 +52,9 @@ public sealed class ModbusLoggerInjectionTests { /// Gets or sets the protected address. public ushort ProtectedAddress { get; set; } = 102; - /// Simulates connecting to the Modbus device. - /// The cancellation token. - /// A completed task. + /// public Task ConnectAsync(CancellationToken ct) => Task.CompletedTask; - /// Simulates sending a Modbus PDU. - /// The Modbus unit ID. - /// The protocol data unit. - /// The cancellation token. - /// The response PDU. + /// public Task SendAsync(byte unitId, byte[] pdu, CancellationToken ct) { var addr = (ushort)((pdu[1] << 8) | pdu[2]); @@ -71,12 +65,13 @@ public sealed class ModbusLoggerInjectionTests resp[0] = pdu[0]; resp[1] = (byte)(qty * 2); return Task.FromResult(resp); } - /// Disposes the transport asynchronously. + /// Disposes the test transport stub. /// A completed value task. public ValueTask DisposeAsync() => ValueTask.CompletedTask; } /// Verifies first failure emits single warning and subsequent refires stay quiet. + /// A task that represents the asynchronous operation. [Fact] public async Task First_Failure_Emits_Single_Warning_Subsequent_Refire_Stays_Quiet() { @@ -108,6 +103,7 @@ public sealed class ModbusLoggerInjectionTests } /// Verifies reprobe clearing prohibition emits information log. + /// A task that represents the asynchronous operation. [Fact] public async Task Reprobe_Clearing_Prohibition_Emits_Information_Log() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusMultiUnitTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusMultiUnitTests.cs index ac01324f..bcfbf9f5 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusMultiUnitTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusMultiUnitTests.cs @@ -14,13 +14,9 @@ public sealed class ModbusMultiUnitTests private sealed class UnitCapturingTransport : IModbusTransport { public readonly List SeenUnitIds = new(); - /// Connects to the transport. - /// Token to cancel the connection. + /// public Task ConnectAsync(CancellationToken ct) => Task.CompletedTask; - /// Sends a Modbus PDU and returns a response. - /// The Modbus unit ID for the request. - /// The protocol data unit to send. - /// Token to cancel the operation. + /// public Task SendAsync(byte unitId, byte[] pdu, CancellationToken ct) { SeenUnitIds.Add(unitId); @@ -36,11 +32,13 @@ public sealed class ModbusMultiUnitTests default: return Task.FromResult(new byte[] { pdu[0], 0, 0 }); } } - /// Disposes the transport resources. + /// Releases the transport; this implementation is a no-op. + /// A completed value task. public ValueTask DisposeAsync() => ValueTask.CompletedTask; } /// Verifies that per-tag UnitId routes reads to the correct slave. + /// A task that represents the asynchronous test operation. [Fact] public async Task PerTag_UnitId_Routes_To_Correct_Slave_In_MBAP() { @@ -61,6 +59,7 @@ public sealed class ModbusMultiUnitTests } /// Verifies that tags without UnitId override use the driver-level UnitId. + /// A task that represents the asynchronous test operation. [Fact] public async Task Tag_Without_UnitId_Falls_Back_To_DriverLevel() { @@ -77,6 +76,7 @@ public sealed class ModbusMultiUnitTests } /// Verifies that IPerCallHostResolver returns per-slave host strings. + /// A task that represents the asynchronous test operation. [Fact] public async Task IPerCallHostResolver_Returns_Per_Slave_Host_String() { @@ -97,6 +97,7 @@ public sealed class ModbusMultiUnitTests } /// Verifies that IPerCallHostResolver falls back to hostname for unknown tags. + /// A task that represents the asynchronous test operation. [Fact] public async Task IPerCallHostResolver_Unknown_Tag_Falls_Back_To_HostName() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusProbeTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusProbeTests.cs index f2359f1a..0b4599d7 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusProbeTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusProbeTests.cs @@ -19,16 +19,10 @@ public sealed class ModbusProbeTests public volatile bool Reachable = true; public int ProbeCount; - /// Asynchronously connects the transport. - /// The cancellation token. - /// A completed task. + /// public Task ConnectAsync(CancellationToken ct) => Task.CompletedTask; - /// Asynchronously sends a Modbus PDU and returns a response. - /// The Modbus unit ID. - /// The protocol data unit to send. - /// The cancellation token. - /// A task that returns the response bytes. + /// public Task SendAsync(byte unitId, byte[] pdu, CancellationToken ct) { if (pdu[0] == 0x03) Interlocked.Increment(ref ProbeCount); @@ -60,6 +54,7 @@ public sealed class ModbusProbeTests } /// Verifies that the initial state is Unknown before the first probe tick. + /// A task that represents the asynchronous operation. [Fact] public async Task Initial_state_is_Unknown_before_first_probe_tick() { @@ -73,6 +68,7 @@ public sealed class ModbusProbeTests } /// Verifies that the first successful probe transitions the state to Running. + /// A task that represents the asynchronous operation. [Fact] public async Task First_successful_probe_transitions_to_Running() { @@ -105,6 +101,7 @@ public sealed class ModbusProbeTests } /// Verifies that a transport failure transitions the state to Stopped. + /// A task that represents the asynchronous operation. [Fact] public async Task Transport_failure_transitions_to_Stopped() { @@ -128,6 +125,7 @@ public sealed class ModbusProbeTests } /// Verifies that recovery transitions the state from Stopped back to Running. + /// A task that represents the asynchronous operation. [Fact] public async Task Recovery_transitions_Stopped_back_to_Running() { @@ -155,6 +153,7 @@ public sealed class ModbusProbeTests } /// Verifies that repeated successful probes do not generate duplicate Running events. + /// A task that represents the asynchronous operation. [Fact] public async Task Repeated_successful_probes_do_not_generate_duplicate_Running_events() { @@ -176,6 +175,7 @@ public sealed class ModbusProbeTests } /// Verifies that a disabled probe stays Unknown and fires no events. + /// A task that represents the asynchronous operation. [Fact] public async Task Disabled_probe_stays_Unknown_and_fires_no_events() { @@ -192,6 +192,7 @@ public sealed class ModbusProbeTests } /// Verifies that shutdown stops the probe loop. + /// A task that represents the asynchronous operation. [Fact] public async Task Shutdown_stops_the_probe_loop() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusProtocolOptionsTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusProtocolOptionsTests.cs index 5bdf2cc9..f7360933 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusProtocolOptionsTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusProtocolOptionsTests.cs @@ -15,15 +15,9 @@ public sealed class ModbusProtocolOptionsTests private sealed class CapturingTransport : IModbusTransport { public readonly List Sent = new(); - /// Asynchronously connects the transport. - /// The cancellation token. - /// A completed task. + /// public Task ConnectAsync(CancellationToken ct) => Task.CompletedTask; - /// Asynchronously sends a Modbus PDU and returns a response. - /// The Modbus unit ID. - /// The protocol data unit to send. - /// The cancellation token. - /// A task that returns the response bytes. + /// public Task SendAsync(byte unitId, byte[] pdu, CancellationToken ct) { Sent.Add(pdu); @@ -70,6 +64,7 @@ public sealed class ModbusProtocolOptionsTests } /// Verifies that single coil write uses FC05 by default. + /// A task that represents the asynchronous test operation. [Fact] public async Task Single_Coil_Write_Uses_FC05_By_Default() { @@ -84,6 +79,7 @@ public sealed class ModbusProtocolOptionsTests } /// Verifies that single coil write uses FC15 when forced. + /// A task that represents the asynchronous test operation. [Fact] public async Task Single_Coil_Write_Uses_FC15_When_Forced() { @@ -99,6 +95,7 @@ public sealed class ModbusProtocolOptionsTests } /// Verifies that single register write uses FC06 by default. + /// A task that represents the asynchronous test operation. [Fact] public async Task Single_Register_Write_Uses_FC06_By_Default() { @@ -113,6 +110,7 @@ public sealed class ModbusProtocolOptionsTests } /// Verifies that single register write uses FC16 when forced. + /// A task that represents the asynchronous test operation. [Fact] public async Task Single_Register_Write_Uses_FC16_When_Forced() { @@ -128,6 +126,7 @@ public sealed class ModbusProtocolOptionsTests } /// Verifies that coil array read automatically chunks at MaxCoilsPerRead. + /// A task that represents the asynchronous test operation. [Fact] public async Task Coil_Array_Read_Auto_Chunks_At_MaxCoilsPerRead() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusSubscribeOptionsTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusSubscribeOptionsTests.cs index ff26087c..fc930bac 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusSubscribeOptionsTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusSubscribeOptionsTests.cs @@ -20,13 +20,9 @@ public sealed class ModbusSubscribeOptionsTests public ushort CurrentValue; public int WritesSent; public int FC06Count; - /// Establishes a connection (no-op for testing). - /// The cancellation token. + /// public Task ConnectAsync(CancellationToken ct) => Task.CompletedTask; - /// Sends a Modbus PDU and returns a response. - /// The Modbus unit ID. - /// The protocol data unit to send. - /// The cancellation token. + /// public Task SendAsync(byte unitId, byte[] pdu, CancellationToken ct) { switch (pdu[0]) @@ -52,10 +48,12 @@ public sealed class ModbusSubscribeOptionsTests } } /// Disposes the transport (no-op for testing). + /// A completed value task. public ValueTask DisposeAsync() => ValueTask.CompletedTask; } /// Verifies that deadband filter suppresses changes below the threshold. + /// A task that represents the asynchronous test operation. [Fact] public async Task Deadband_Suppresses_SubThreshold_Changes() { @@ -97,6 +95,7 @@ public sealed class ModbusSubscribeOptionsTests } /// Verifies that null deadband publishes every value change. + /// A task that represents the asynchronous test operation. [Fact] public async Task Deadband_Null_Publishes_Every_Change() { @@ -122,6 +121,7 @@ public sealed class ModbusSubscribeOptionsTests } /// Verifies that WriteOnChangeOnly suppresses writes with identical repeated values. + /// A task that represents the asynchronous test operation. [Fact] public async Task WriteOnChangeOnly_Suppresses_Identical_Repeated_Writes() { @@ -141,6 +141,7 @@ public sealed class ModbusSubscribeOptionsTests } /// Verifies that WriteOnChangeOnly defaults to false and writes all values. + /// A task that represents the asynchronous test operation. [Fact] public async Task WriteOnChangeOnly_Default_False_Always_Writes() { @@ -159,6 +160,7 @@ public sealed class ModbusSubscribeOptionsTests } /// Verifies that external reads invalidate the WriteOnChangeOnly cache. + /// A task that represents the asynchronous test operation. [Fact] public async Task WriteOnChangeOnly_Cache_Invalidated_By_Read_Divergence() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusSubscriptionTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusSubscriptionTests.cs index f44688a0..3ef6fecd 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusSubscriptionTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusSubscriptionTests.cs @@ -18,14 +18,10 @@ public sealed class ModbusSubscriptionTests private sealed class FakeTransport : IModbusTransport { public readonly ushort[] HoldingRegisters = new ushort[256]; - /// Simulates connecting to the Modbus device. - /// The cancellation token. + /// public Task ConnectAsync(CancellationToken ct) => Task.CompletedTask; - /// Simulates sending a Modbus PDU. - /// The Modbus unit ID. - /// The protocol data unit. - /// The cancellation token. + /// public Task SendAsync(byte unitId, byte[] pdu, CancellationToken ct) { if (pdu[0] != 0x03) return Task.FromException(new NotSupportedException("FC not supported")); @@ -41,7 +37,8 @@ public sealed class ModbusSubscriptionTests } return Task.FromResult(resp); } - /// Disposes the transport asynchronously. + /// Disposes the fake transport. No-op in this test double. + /// A completed value task. public ValueTask DisposeAsync() => ValueTask.CompletedTask; } @@ -53,6 +50,7 @@ public sealed class ModbusSubscriptionTests } /// Verifies initial poll raises OnDataChange for every subscribed tag. + /// A task that represents the asynchronous operation. [Fact] public async Task Initial_poll_raises_OnDataChange_for_every_subscribed_tag() { @@ -75,6 +73,7 @@ public sealed class ModbusSubscriptionTests } /// Verifies unchanged values do not raise after initial poll. + /// A task that represents the asynchronous operation. [Fact] public async Task Unchanged_values_do_not_raise_after_initial_poll() { @@ -93,6 +92,7 @@ public sealed class ModbusSubscriptionTests } /// Verifies value change between polls raises OnDataChange. + /// A task that represents the asynchronous operation. [Fact] public async Task Value_change_between_polls_raises_OnDataChange() { @@ -114,6 +114,7 @@ public sealed class ModbusSubscriptionTests } /// Verifies unsubscribe stops the polling loop. + /// A task that represents the asynchronous operation. [Fact] public async Task Unsubscribe_stops_the_polling_loop() { @@ -134,6 +135,7 @@ public sealed class ModbusSubscriptionTests } /// Verifies SubscribeAsync floors intervals below 100ms. + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeAsync_floors_intervals_below_100ms() { @@ -154,6 +156,7 @@ public sealed class ModbusSubscriptionTests } /// Verifies multiple subscriptions fire independently. + /// A task that represents the asynchronous operation. [Fact] public async Task Multiple_subscriptions_fire_independently() { @@ -196,6 +199,7 @@ public sealed class ModbusSubscriptionTests /// value steps up by 5 every poll (well over the deadband of 2) so every poll publishes, /// maximising contention on the cache. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Concurrent_deadband_subscriptions_do_not_corrupt_the_publish_cache() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusTcpReconnectTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusTcpReconnectTests.cs index 47641eb4..d9b2f779 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusTcpReconnectTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests/ModbusTcpReconnectTests.cs @@ -107,6 +107,7 @@ public sealed class ModbusTcpReconnectTests } /// Stops the server and releases resources. + /// A task that represents the asynchronous dispose operation. public async ValueTask DisposeAsync() { _stop.Cancel(); @@ -116,6 +117,7 @@ public sealed class ModbusTcpReconnectTests } /// Verifies that the transport recovers from a mid-session socket drop with auto-reconnect enabled. + /// A task that represents the asynchronous operation. [Fact] public async Task Transport_recovers_from_mid_session_drop_and_retries_successfully() { @@ -136,6 +138,7 @@ public sealed class ModbusTcpReconnectTests } /// Verifies that socket drops propagate to the caller when auto-reconnect is disabled. + /// A task that represents the asynchronous operation. [Fact] public async Task Transport_without_AutoReconnect_propagates_drop_to_caller() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.IntegrationTests/BrowseRoundTripTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.IntegrationTests/BrowseRoundTripTests.cs index f1e1b8ab..016da9d2 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.IntegrationTests/BrowseRoundTripTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.IntegrationTests/BrowseRoundTripTests.cs @@ -13,6 +13,8 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.IntegrationTests; [Trait("Category", "Integration"), Trait("Fixture", "opc-plc")] public class BrowseRoundTripTests { + /// Verifies that a three-level expand round-trips resolve back to valid node IDs. + /// A task that represents the asynchronous operation. [Fact] public async Task Three_level_expand_round_trips_resolve_back() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.Tests/OpcUaClientBrowseSessionTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.Tests/OpcUaClientBrowseSessionTests.cs index adadfb1e..e51f3576 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.Tests/OpcUaClientBrowseSessionTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.Tests/OpcUaClientBrowseSessionTests.cs @@ -37,6 +37,7 @@ public sealed class OpcUaClientBrowseSessionTests /// RootAsync should surface at least one node under ObjectsFolder /// (opc-plc exposes a non-empty top level). + /// A task that represents the asynchronous operation. [Fact] public async Task RootAsync_returns_at_least_one_node() { @@ -49,6 +50,7 @@ public sealed class OpcUaClientBrowseSessionTests /// ExpandAsync should accept a stable NodeId returned by RootAsync /// and round-trip through the live namespace table. + /// A task that represents the asynchronous operation. [Fact] public async Task ExpandAsync_round_trips_stable_NodeId() { @@ -64,6 +66,7 @@ public sealed class OpcUaClientBrowseSessionTests /// The OPC UA picker treats variables as terminal leaves, so /// AttributesAsync must always be empty for this driver. + /// A task that represents the asynchronous operation. [Fact] public async Task AttributesAsync_is_empty_for_opcuaclient() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.Tests/OpcUaClientDriverBrowserTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.Tests/OpcUaClientDriverBrowserTests.cs index c61cbd47..78a4b790 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.Tests/OpcUaClientDriverBrowserTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.Tests/OpcUaClientDriverBrowserTests.cs @@ -19,6 +19,7 @@ public sealed class OpcUaClientDriverBrowserTests public void DriverType_is_OpcUaClient() => _sut.DriverType.ShouldBe("OpcUaClient"); /// An empty endpoint must fail fast with a clear EndpointUrl-mentioning message. + /// A task that represents the asynchronous test operation. [Fact] public async Task OpenAsync_with_empty_endpoint_throws() { @@ -29,6 +30,7 @@ public sealed class OpcUaClientDriverBrowserTests } /// A JSON literal that deserializes to null must fail fast. + /// A task that represents the asynchronous test operation. [Fact] public async Task OpenAsync_with_null_json_throws() { @@ -41,6 +43,7 @@ public sealed class OpcUaClientDriverBrowserTests /// must say so explicitly rather than surfacing a downstream COM/SDK error. /// OpcUaAuthType.Certificate serializes as the numeric value 2 under the /// browser's default System.Text.Json options (no string-enum converter). + /// A task that represents the asynchronous test operation. [Fact] public async Task OpenAsync_with_certificate_auth_throws_clear_message() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.IntegrationTests/OpcPlcFixture.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.IntegrationTests/OpcPlcFixture.cs index 037343c1..c4cae526 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.IntegrationTests/OpcPlcFixture.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.IntegrationTests/OpcPlcFixture.cs @@ -100,6 +100,7 @@ public sealed class OpcPlcFixture : IAsyncDisposable /// /// Disposes the fixture resources (currently a no-op). /// + /// A completed value task. public ValueTask DisposeAsync() => ValueTask.CompletedTask; } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.IntegrationTests/OpcUaClientSmokeTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.IntegrationTests/OpcUaClientSmokeTests.cs index 6635564d..9c419fe0 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.IntegrationTests/OpcUaClientSmokeTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.IntegrationTests/OpcUaClientSmokeTests.cs @@ -17,6 +17,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.IntegrationTests; public sealed class OpcUaClientSmokeTests(OpcPlcFixture sim) { /// Verifies that the client can connect and read a node through the real OPC UA stack. + /// A task that represents the asynchronous operation. [Fact] public async Task Client_connects_and_reads_StepUp_node_through_real_OPC_UA_stack() { @@ -35,6 +36,7 @@ public sealed class OpcUaClientSmokeTests(OpcPlcFixture sim) } /// Verifies that the client can read a batch of varied types from the simulator. + /// A task that represents the asynchronous operation. [Fact] public async Task Client_reads_batch_of_varied_types_from_live_simulator() { @@ -60,6 +62,7 @@ public sealed class OpcUaClientSmokeTests(OpcPlcFixture sim) } /// Verifies that the client can subscribe to data changes from the live server. + /// A task that represents the asynchronous operation. [Fact] public async Task Client_subscribe_receives_StepUp_data_changes_from_live_server() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientAlarmTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientAlarmTests.cs index 08008733..a1896949 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientAlarmTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientAlarmTests.cs @@ -33,6 +33,7 @@ public sealed class OpcUaClientAlarmTests } /// Verifies that SubscribeAlarmsAsync without initialize throws InvalidOperationException. + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeAlarmsAsync_without_initialize_throws_InvalidOperationException() { @@ -42,6 +43,7 @@ public sealed class OpcUaClientAlarmTests } /// Verifies that UnsubscribeAlarmsAsync with unknown handle is noop. + /// A task that represents the asynchronous operation. [Fact] public async Task UnsubscribeAlarmsAsync_with_unknown_handle_is_noop() { @@ -51,6 +53,7 @@ public sealed class OpcUaClientAlarmTests } /// Verifies that AcknowledgeAsync without initialize throws InvalidOperationException. + /// A task that represents the asynchronous operation. [Fact] public async Task AcknowledgeAsync_without_initialize_throws_InvalidOperationException() { @@ -62,6 +65,7 @@ public sealed class OpcUaClientAlarmTests } /// Verifies that AcknowledgeAsync with empty batch is noop even without init. + /// A task that represents the asynchronous operation. [Fact] public async Task AcknowledgeAsync_with_empty_batch_is_noop_even_without_init() { @@ -73,7 +77,7 @@ public sealed class OpcUaClientAlarmTests private sealed class FakeAlarmHandle : IAlarmSubscriptionHandle { - /// Gets the diagnostic identifier for this alarm handle. + /// public string DiagnosticId => "fake-alarm"; } } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientDiscoveryTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientDiscoveryTests.cs index 23db27b2..aee56910 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientDiscoveryTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientDiscoveryTests.cs @@ -13,6 +13,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests; public sealed class OpcUaClientDiscoveryTests { /// Verifies that DiscoverAsync throws InvalidOperationException when not initialized. + /// A task that represents the asynchronous operation. [Fact] public async Task DiscoverAsync_without_initialize_throws_InvalidOperationException() { @@ -44,22 +45,14 @@ public sealed class OpcUaClientDiscoveryTests /// Test builder that provides no-op implementations for discovery tests. private sealed class NullAddressSpaceBuilder : IAddressSpaceBuilder { - /// Returns this builder (no-op). - /// The browse name of the folder. - /// The display name of the folder. + /// public IAddressSpaceBuilder Folder(string browseName, string displayName) => this; - /// Returns a stub handle. - /// The browse name of the variable. - /// The display name of the variable. - /// The attribute information for the variable. + /// public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo attributeInfo) => new StubHandle(); - /// No-op property addition. - /// The browse name of the property. - /// The data type of the property. - /// The property value. + /// public void AddProperty(string browseName, DriverDataType dataType, object? value) { } /// No-op alarm condition attachment. @@ -71,11 +64,10 @@ public sealed class OpcUaClientDiscoveryTests /// Stub variable handle for testing. private sealed class StubHandle : IVariableHandle { - /// Gets the full reference as "stub". + /// public string FullReference => "stub"; - /// Throws NotSupportedException. - /// The alarm condition information (unused). + /// public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) => throw new NotSupportedException(); } } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientDriverScaffoldTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientDriverScaffoldTests.cs index 6307a393..3d6070b3 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientDriverScaffoldTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientDriverScaffoldTests.cs @@ -45,6 +45,7 @@ public sealed class OpcUaClientDriverScaffoldTests } /// Verifies that Initialize against unreachable endpoint transitions to Faulted and throws. + /// A task that represents the asynchronous operation. [Fact] public async Task Initialize_against_unreachable_endpoint_transitions_to_Faulted_and_throws() { @@ -72,6 +73,7 @@ public sealed class OpcUaClientDriverScaffoldTests } /// Verifies that Reinitialize against unreachable endpoint re-throws the error. + /// A task that represents the asynchronous operation. [Fact] public async Task Reinitialize_against_unreachable_endpoint_re_throws() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientFailoverTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientFailoverTests.cs index 4d68f7bf..d15b59c5 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientFailoverTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientFailoverTests.cs @@ -60,6 +60,7 @@ public sealed class OpcUaClientFailoverTests } /// Verifies that initializing against all unreachable endpoints throws AggregateException listing each. + /// A task that represents the asynchronous operation. [Fact] public async Task Initialize_against_all_unreachable_endpoints_throws_AggregateException_listing_each() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientHistoryTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientHistoryTests.cs index 381e99ce..8ead7940 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientHistoryTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientHistoryTests.cs @@ -34,6 +34,7 @@ public sealed class OpcUaClientHistoryTests } /// Verifies ReadRawAsync throws without initialization. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReadRawAsync_without_initialize_throws_InvalidOperationException() { @@ -45,6 +46,7 @@ public sealed class OpcUaClientHistoryTests } /// Verifies ReadRawAsync with malformed NodeId returns empty result. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReadRawAsync_with_malformed_NodeId_returns_empty_result_not_throw() { @@ -58,6 +60,7 @@ public sealed class OpcUaClientHistoryTests } /// Verifies ReadProcessedAsync throws without initialization. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReadProcessedAsync_without_initialize_throws_InvalidOperationException() { @@ -70,6 +73,7 @@ public sealed class OpcUaClientHistoryTests } /// Verifies ReadAtTimeAsync throws without initialization. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReadAtTimeAsync_without_initialize_throws_InvalidOperationException() { @@ -81,6 +85,7 @@ public sealed class OpcUaClientHistoryTests } /// Verifies ReadEventsAsync throws NotSupportedException as documented. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReadEventsAsync_throws_NotSupportedException_as_documented() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientLowFindingsRegressionTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientLowFindingsRegressionTests.cs index 24532d9d..a60d6fa7 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientLowFindingsRegressionTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientLowFindingsRegressionTests.cs @@ -121,6 +121,7 @@ public sealed class OpcUaClientLowFindingsRegressionTests } /// Verifies that UnsubscribeAsync with unknown handle does not throw after the fix. + /// A task that represents the asynchronous test operation. [Fact] public async Task UnsubscribeAsync_unknown_handle_does_not_throw_after_fix() { @@ -132,6 +133,7 @@ public sealed class OpcUaClientLowFindingsRegressionTests } /// Verifies that UnsubscribeAlarmsAsync with unknown handle does not throw after the fix. + /// A task that represents the asynchronous test operation. [Fact] public async Task UnsubscribeAlarmsAsync_unknown_handle_does_not_throw_after_fix() { @@ -143,14 +145,14 @@ public sealed class OpcUaClientLowFindingsRegressionTests /// Fake subscription handle for testing. private sealed class FakeHandle : Core.Abstractions.ISubscriptionHandle { - /// Gets the diagnostic identifier for this handle. + /// public string DiagnosticId => "fake-sub"; } /// Fake alarm subscription handle for testing. private sealed class FakeAlarmHandle : Core.Abstractions.IAlarmSubscriptionHandle { - /// Gets the diagnostic identifier for this handle. + /// public string DiagnosticId => "fake-alarm-sub"; } } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientMediumFindingsRegressionTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientMediumFindingsRegressionTests.cs index ea8f9221..c71c56f0 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientMediumFindingsRegressionTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientMediumFindingsRegressionTests.cs @@ -23,6 +23,7 @@ public sealed class OpcUaClientMediumFindingsRegressionTests // ---- Driver.OpcUaClient-009 ---- /// Verifies that WriteAsync without session returns BadCommunicationError, not BadTimeout. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteAsync_without_session_returns_BadCommunicationError_not_BadTimeout() { @@ -69,6 +70,7 @@ public sealed class OpcUaClientMediumFindingsRegressionTests } /// Verifies that InitializeAsync with AutoAccept emits a warning log. + /// A task that represents the asynchronous operation. [Fact] public async Task InitializeAsync_AutoAccept_emits_warning_log() { @@ -108,6 +110,7 @@ public sealed class OpcUaClientMediumFindingsRegressionTests } /// Verifies that FlushOptionalCachesAsync completes without throwing. + /// A task that represents the asynchronous operation. [Fact] public async Task FlushOptionalCachesAsync_completes_without_throwing() { @@ -118,6 +121,7 @@ public sealed class OpcUaClientMediumFindingsRegressionTests } /// Verifies that FlushOptionalCachesAsync resets the footprint counter. + /// A task that represents the asynchronous operation. [Fact] public async Task FlushOptionalCachesAsync_resets_footprint_counter() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientReadWriteTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientReadWriteTests.cs index 01b132a4..a3373241 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientReadWriteTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientReadWriteTests.cs @@ -13,6 +13,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests; public sealed class OpcUaClientReadWriteTests { /// Verifies that ReadAsync throws InvalidOperationException when not initialized. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadAsync_without_initialize_throws_InvalidOperationException() { @@ -22,6 +23,7 @@ public sealed class OpcUaClientReadWriteTests } /// Verifies that WriteAsync throws InvalidOperationException when not initialized. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteAsync_without_initialize_throws_InvalidOperationException() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientSubscribeAndProbeTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientSubscribeAndProbeTests.cs index bfde6c6a..dbc45afc 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientSubscribeAndProbeTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientSubscribeAndProbeTests.cs @@ -14,6 +14,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests; public sealed class OpcUaClientSubscribeAndProbeTests { /// Verifies that subscribe without initialization throws InvalidOperationException. + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeAsync_without_initialize_throws_InvalidOperationException() { @@ -23,6 +24,7 @@ public sealed class OpcUaClientSubscribeAndProbeTests } /// Verifies that unsubscribe with unknown handle is a no-op. + /// A task that represents the asynchronous operation. [Fact] public async Task UnsubscribeAsync_with_unknown_handle_is_noop() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.IntegrationTests/S7_1500/S7_1500Profile.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.IntegrationTests/S7_1500/S7_1500Profile.cs index c329e472..6c229e94 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.IntegrationTests/S7_1500/S7_1500Profile.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.IntegrationTests/S7_1500/S7_1500Profile.cs @@ -28,6 +28,7 @@ public static class S7_1500Profile /// Builds the S7 driver options for the S7-1500 integration tests. /// The hostname or IP address of the S7 PLC. /// The port number for the S7 PLC connection. + /// A instance pre-configured with the standard S7-1500 test tags. public static S7DriverOptions BuildOptions(string host, int port) => new() { Host = host, diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.IntegrationTests/S7_1500/S7_1500SmokeTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.IntegrationTests/S7_1500/S7_1500SmokeTests.cs index 6371e0bc..132ba672 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.IntegrationTests/S7_1500/S7_1500SmokeTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.IntegrationTests/S7_1500/S7_1500SmokeTests.cs @@ -18,6 +18,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.S7.IntegrationTests.S7_1500; public sealed class S7_1500SmokeTests(Snap7ServerFixture sim) { /// Verifies that the driver reads a seeded u16 value through real S7comm. + /// A task that represents the asynchronous test. [Fact] public async Task Driver_reads_seeded_u16_through_real_S7comm() { @@ -36,6 +37,7 @@ public sealed class S7_1500SmokeTests(Snap7ServerFixture sim) } /// Verifies that the driver reads a typed batch of seeded values. + /// A task that represents the asynchronous test. [Fact] public async Task Driver_reads_seeded_typed_batch() { @@ -60,6 +62,7 @@ public sealed class S7_1500SmokeTests(Snap7ServerFixture sim) } /// Verifies that the driver can write and then read back a value on a scratch register. + /// A task that represents the asynchronous test. [Fact] public async Task Driver_write_then_read_round_trip_on_scratch_word() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.IntegrationTests/Snap7ServerFixture.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.IntegrationTests/Snap7ServerFixture.cs index 142fb3c7..58a4148b 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.IntegrationTests/Snap7ServerFixture.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.IntegrationTests/Snap7ServerFixture.cs @@ -82,6 +82,7 @@ public sealed class Snap7ServerFixture : IAsyncDisposable } /// Disposes the fixture asynchronously. + /// A completed value task. public ValueTask DisposeAsync() => ValueTask.CompletedTask; } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/S7DiscoveryAndSubscribeTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/S7DiscoveryAndSubscribeTests.cs index a437e46f..f1914823 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/S7DiscoveryAndSubscribeTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/S7DiscoveryAndSubscribeTests.cs @@ -18,31 +18,21 @@ public sealed class S7DiscoveryAndSubscribeTests public readonly List Folders = new(); public readonly List<(string Name, DriverAttributeInfo Attr)> Variables = new(); - /// Adds a folder to the address space. - /// The browse name of the folder. - /// The display name of the folder. - /// This builder instance for method chaining. + /// public IAddressSpaceBuilder Folder(string browseName, string displayName) { Folders.Add(browseName); return this; } - /// Adds a variable to the address space. - /// The browse name of the variable. - /// The display name of the variable. - /// The attribute information for the variable. - /// A handle to the created variable. + /// public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo attributeInfo) { Variables.Add((browseName, attributeInfo)); return new StubHandle(); } - /// Adds a property to a variable. - /// The browse name of the property. - /// The data type of the property. - /// The initial value of the property. + /// public void AddProperty(string browseName, DriverDataType dataType, object? value) { } /// Attaches an alarm condition to a variable. @@ -53,18 +43,17 @@ public sealed class S7DiscoveryAndSubscribeTests private sealed class StubHandle : IVariableHandle { - /// Gets the full reference of the variable. + /// public string FullReference => "stub"; - /// Marks this variable as an alarm condition. - /// The alarm condition information. - /// An alarm condition sink. + /// public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) => throw new NotImplementedException("S7 driver never calls this — no alarm surfacing"); } } /// Verifies that DiscoverAsync projects every configured tag into the address space. + /// A task that represents the asynchronous operation. [Fact] public async Task DiscoverAsync_projects_every_tag_into_the_address_space() { @@ -92,6 +81,7 @@ public sealed class S7DiscoveryAndSubscribeTests } /// Verifies that DiscoverAsync propagates the WriteIdempotent flag from tag configuration to attribute info. + /// A task that represents the asynchronous operation. [Fact] public async Task DiscoverAsync_propagates_WriteIdempotent_from_tag_to_attribute_info() { @@ -127,6 +117,7 @@ public sealed class S7DiscoveryAndSubscribeTests } /// Verifies that SubscribeAsync returns unique handles and UnsubscribeAsync correctly accepts them. + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeAsync_returns_unique_handles_and_UnsubscribeAsync_accepts_them() { @@ -151,6 +142,7 @@ public sealed class S7DiscoveryAndSubscribeTests } /// Verifies that Subscribe floors the publishing interval at 100ms. + /// A task that represents the asynchronous operation. [Fact] public async Task Subscribe_publishing_interval_is_floored_at_100ms() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/S7DriverCodeReviewFixTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/S7DriverCodeReviewFixTests.cs index 085a1281..30bc166c 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/S7DriverCodeReviewFixTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/S7DriverCodeReviewFixTests.cs @@ -18,6 +18,7 @@ public sealed class S7DriverCodeReviewFixTests /// Verifies that initialize rejects timer or counter tags with NotSupportedException. /// The S7 Timer or Counter address to test. + /// A task that represents the asynchronous operation. [Theory] [InlineData("T0")] [InlineData("T15")] @@ -48,6 +49,7 @@ public sealed class S7DriverCodeReviewFixTests } /// Verifies that initialize accepts DB and MIQ addresses without triggering the unsupported guard. + /// A task that represents the asynchronous operation. [Fact] public async Task Initialize_accepts_DB_and_MIQ_addresses_without_the_unsupported_guard_tripping() { @@ -75,6 +77,7 @@ public sealed class S7DriverCodeReviewFixTests // ---- Driver.S7-011 — driverConfigJson must be applied on Initialize ---- /// Verifies that initialize applies the supplied driverConfigJson over constructor options. + /// A task that represents the asynchronous operation. [Fact] public async Task Initialize_applies_the_supplied_driverConfigJson_over_the_constructor_options() { @@ -101,6 +104,7 @@ public sealed class S7DriverCodeReviewFixTests } /// Verifies that initialize rejects a timer tag supplied only through driverConfigJson. + /// A task that represents the asynchronous operation. [Fact] public async Task Initialize_rejects_a_timer_tag_supplied_only_through_driverConfigJson() { @@ -120,6 +124,7 @@ public sealed class S7DriverCodeReviewFixTests } /// Verifies that reinitialize applies a changed driverConfigJson. + /// A task that represents the asynchronous operation. [Fact] public async Task Reinitialize_applies_a_changed_driverConfigJson() { @@ -142,6 +147,7 @@ public sealed class S7DriverCodeReviewFixTests // ---- Driver.S7-006 — Shutdown drains probe/poll loops before disposing the gate ---- /// Verifies that shutdown completes cleanly with active subscriptions and no disposal race. + /// A task that represents the asynchronous operation. [Fact] public async Task Shutdown_completes_cleanly_with_active_subscriptions_and_no_disposal_race() { @@ -167,6 +173,7 @@ public sealed class S7DriverCodeReviewFixTests } /// Verifies that dispose after subscribe does not throw ObjectDisposedException. + /// A task that represents the asynchronous operation. [Fact] public async Task Dispose_after_subscribe_does_not_throw_ObjectDisposedException() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/S7DriverCodeReviewFixTests2.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/S7DriverCodeReviewFixTests2.cs index fe6e8c74..33632e20 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/S7DriverCodeReviewFixTests2.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/S7DriverCodeReviewFixTests2.cs @@ -16,6 +16,7 @@ public sealed class S7DriverCodeReviewFixTests2 // ── Driver.S7-003 — Read/WriteAsync must throw ArgumentNullException, not NRE ───────── /// Verifies that ReadAsync throws ArgumentNullException for null references. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadAsync_with_null_fullReferences_throws_ArgumentNullException() { @@ -28,6 +29,7 @@ public sealed class S7DriverCodeReviewFixTests2 } /// Verifies that WriteAsync throws ArgumentNullException for null writes. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteAsync_with_null_writes_throws_ArgumentNullException() { @@ -39,6 +41,7 @@ public sealed class S7DriverCodeReviewFixTests2 // ── Driver.S7-009 — Poll loop must update health on sustained failure ──────────────── /// Verifies that the poll loop degrades health when the driver is uninitialized. + /// A task that represents the asynchronous operation. [Fact] public async Task PollLoop_against_uninitialized_driver_degrades_health() { @@ -68,6 +71,7 @@ public sealed class S7DriverCodeReviewFixTests2 } /// Verifies that the poll loop applies capped backoff after consecutive failures. + /// A task that represents the asynchronous operation. [Fact] public async Task PollLoop_applies_capped_backoff_after_consecutive_failures() { @@ -140,6 +144,7 @@ public sealed class S7DriverCodeReviewFixTests2 /// Verifies that Initialize rejects not-yet-implemented data types with NotSupportedException. /// The S7 data type that is not yet implemented. + /// A task that represents the asynchronous operation. [Theory] [InlineData(S7DataType.Int64)] [InlineData(S7DataType.UInt64)] @@ -173,6 +178,7 @@ public sealed class S7DriverCodeReviewFixTests2 /// Verifies that Initialize accepts implemented data types. /// The S7 data type to test. /// The S7 address string corresponding to the data type. + /// A task that represents the asynchronous operation. [Theory] [InlineData(S7DataType.Bool, "DB1.DBX0.0")] [InlineData(S7DataType.Byte, "DB1.DBB0")] diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/S7DriverReadWriteTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/S7DriverReadWriteTests.cs index 8a22b779..1d119b70 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/S7DriverReadWriteTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/S7DriverReadWriteTests.cs @@ -14,6 +14,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.S7.Tests; public sealed class S7DriverReadWriteTests { /// Verifies that initialization rejects invalid tag addresses and fails fast. + /// A task that represents the asynchronous test operation. [Fact] public async Task Initialize_rejects_invalid_tag_address_and_fails_fast() { @@ -36,6 +37,7 @@ public sealed class S7DriverReadWriteTests } /// Verifies that ReadAsync without initialize throws InvalidOperationException. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReadAsync_without_initialize_throws_InvalidOperationException() { @@ -45,6 +47,7 @@ public sealed class S7DriverReadWriteTests } /// Verifies that WriteAsync without initialize throws InvalidOperationException. + /// A task that represents the asynchronous test operation. [Fact] public async Task WriteAsync_without_initialize_throws_InvalidOperationException() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/S7DriverScaffoldTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/S7DriverScaffoldTests.cs index b0e4b431..ca42ee2e 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/S7DriverScaffoldTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/S7DriverScaffoldTests.cs @@ -53,6 +53,7 @@ public sealed class S7DriverScaffoldTests } /// Verifies that Initialize against unreachable host transitions to Faulted and throws. + /// A task that represents the asynchronous operation. [Fact] public async Task Initialize_against_unreachable_host_transitions_to_Faulted_and_throws() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCAT3SmokeTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCAT3SmokeTests.cs index a5f46f30..cc86bc82 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCAT3SmokeTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCAT3SmokeTests.cs @@ -34,6 +34,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests; public sealed class TwinCAT3SmokeTests(TwinCATXarFixture sim) { /// Verifies that the driver reads a seeded DINT value through real ADS. + /// A task that represents the asynchronous operation. [TwinCATFact] public async Task Driver_reads_seeded_DINT_through_real_ADS() { @@ -56,6 +57,7 @@ public sealed class TwinCAT3SmokeTests(TwinCATXarFixture sim) } /// Verifies a write-then-read round trip on a scratch REAL variable. + /// A task that represents the asynchronous operation. [TwinCATFact] public async Task Driver_write_then_read_round_trip_on_scratch_REAL() { @@ -80,6 +82,7 @@ public sealed class TwinCAT3SmokeTests(TwinCATXarFixture sim) } /// Verifies that the driver receives native ADS notifications on counter value changes. + /// A task that represents the asynchronous operation. [TwinCATFact] public async Task Driver_subscribe_receives_native_ADS_notifications_on_counter_changes() { @@ -117,6 +120,7 @@ public sealed class TwinCAT3SmokeTests(TwinCATXarFixture sim) } /// Verifies that the driver browses the committed symbol hierarchy via real ADS. + /// A task that represents the asynchronous operation. [TwinCATFact] public async Task Driver_browses_committed_symbol_hierarchy_via_real_ADS() { @@ -158,6 +162,7 @@ public sealed class TwinCAT3SmokeTests(TwinCATXarFixture sim) } /// Verifies that the driver round-trips array element write and read operations. + /// A task that represents the asynchronous operation. [TwinCATFact] public async Task Driver_round_trips_array_element_write_and_read() { @@ -185,6 +190,7 @@ public sealed class TwinCAT3SmokeTests(TwinCATXarFixture sim) } /// Verifies that the driver auto-reconnects after the underlying ADS client is disposed. + /// A task that represents the asynchronous operation. [TwinCATFact] public async Task Driver_auto_reconnects_after_underlying_client_is_disposed() { @@ -215,6 +221,7 @@ public sealed class TwinCAT3SmokeTests(TwinCATXarFixture sim) /// The TwinCAT symbol path to read. /// The TwinCAT data type of the symbol. /// The expected value in culture-invariant format, or null to skip value assertion. + /// A task that represents the asynchronous operation. [TwinCATTheory] // vBool's expected value is null — the initial TRUE seed doesn't reliably survive cold // restarts on this deployment, and the point of this theory is round-tripping the type @@ -296,6 +303,7 @@ public sealed class TwinCAT3SmokeTests(TwinCATXarFixture sim) } /// Verifies that the driver reads bit-indexed BOOL values from word symbols. + /// A task that represents the asynchronous operation. [TwinCATFact] public async Task Driver_reads_bit_indexed_BOOL_from_word() { @@ -335,6 +343,7 @@ public sealed class TwinCAT3SmokeTests(TwinCATXarFixture sim) } /// Verifies that the driver reads deeply nested UDT member paths. + /// A task that represents the asynchronous operation. [TwinCATFact] public async Task Driver_reads_deeply_nested_UDT_path() { @@ -373,6 +382,7 @@ public sealed class TwinCAT3SmokeTests(TwinCATXarFixture sim) } /// Verifies that the driver reports errors for unknown tags, nonexistent symbols, and read-only writes. + /// A task that represents the asynchronous operation. [TwinCATFact] public async Task Driver_reports_errors_for_unknown_tag_and_nonexistent_symbol_and_readonly_write() { @@ -424,6 +434,7 @@ public sealed class TwinCAT3SmokeTests(TwinCATXarFixture sim) } /// Verifies that the driver routes reads per device and isolates unreachable peers. + /// A task that represents the asynchronous operation. [TwinCATFact] public async Task Driver_routes_reads_per_device_and_isolates_unreachable_peers() { @@ -476,6 +487,7 @@ public sealed class TwinCAT3SmokeTests(TwinCATXarFixture sim) } /// Verifies that the probe loop raises a host status transition to Running when the target is reachable. + /// A task that represents the asynchronous operation. [TwinCATFact] public async Task Probe_loop_raises_host_status_transition_to_Running_on_reachable_target() { @@ -520,6 +532,7 @@ public sealed class TwinCAT3SmokeTests(TwinCATXarFixture sim) } /// Verifies that DiscoverAsync renders declared tags and controller browse symbols to the address space builder. + /// A task that represents the asynchronous operation. [TwinCATFact] public async Task DiscoverAsync_renders_declared_tags_and_controller_browse_hits_address_space_builder() { @@ -609,47 +622,34 @@ internal sealed class RecordingAddressSpaceBuilder : IAddressSpaceBuilder /// Gets the browse names of all recorded folders. public IEnumerable FolderBrowseNames => Folders.Select(f => f.BrowseName); - /// Records a folder call and returns this builder for chaining. - /// The browse name of the folder. - /// The display name of the folder. - /// This builder instance for method chaining. + /// public IAddressSpaceBuilder Folder(string browseName, string displayName) { Folders.Add((browseName, displayName)); return this; } - /// Records a variable call and returns a handle for it. - /// The browse name of the variable. - /// The display name of the variable. - /// The driver attribute information. - /// A variable handle for the recorded variable. + /// public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo info) { Variables.Add((browseName, info)); return new Handle(info.FullName); } - /// Records a property call (no-op in this test double). - /// The property name. - /// The property data type. - /// The property value. + /// public void AddProperty(string name, DriverDataType type, object? value) { } private sealed class Handle(string fullRef) : IVariableHandle { - /// Gets the full reference of the variable. + /// public string FullReference => fullRef; - /// Marks the variable as an alarm condition with the specified information. - /// Alarm condition information. - /// An alarm condition sink for handling transitions. + /// public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) => new NullSink(); } private sealed class NullSink : IAlarmConditionSink { - /// Handles an alarm condition transition (no-op in this test double). - /// The alarm event arguments. + /// public void OnTransition(AlarmEventArgs args) { } } } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCATXarFixture.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCATXarFixture.cs index 3bcf82f1..1ea6571e 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCATXarFixture.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.IntegrationTests/TwinCATXarFixture.cs @@ -87,14 +87,17 @@ public sealed class TwinCATXarFixture : IAsyncLifetime } } - /// + /// Initializes the fixture asynchronously (no-op for this fixture). + /// A completed value task. public ValueTask InitializeAsync() => ValueTask.CompletedTask; - /// + /// Disposes the fixture asynchronously (no-op for this fixture). + /// A completed value task. public ValueTask DisposeAsync() => ValueTask.CompletedTask; /// true when the XAR runtime is reachable + the AmsNetId is set. /// Used by the skip attributes to avoid spinning up the fixture for every test /// class. + /// true if the runtime is available; otherwise false. public static bool IsRuntimeAvailable() { if (string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(NetIdEnvVar))) return false; diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/FakeTwinCATClient.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/FakeTwinCATClient.cs index 41495e23..7280ad01 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/FakeTwinCATClient.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/FakeTwinCATClient.cs @@ -5,7 +5,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests; internal class FakeTwinCATClient : ITwinCATClient { - /// Gets a value indicating whether the client is connected. + /// public bool IsConnected { get; private set; } /// Gets the number of times Connect has been called. public int ConnectCount { get; private set; } @@ -38,11 +38,7 @@ internal class FakeTwinCATClient : ITwinCATClient /// Test hook — fire the symbol-version-changed signal as the real client would. public void FireSymbolVersionChanged() => OnSymbolVersionChanged?.Invoke(this, EventArgs.Empty); - /// Simulates connecting to the TwinCAT system. - /// The AMS address to connect to. - /// The connection timeout. - /// The cancellation token. - /// A task that completes when the connection succeeds or fails. + /// public virtual Task ConnectAsync(TwinCATAmsAddress address, TimeSpan timeout, CancellationToken ct) { ConnectCount++; @@ -51,12 +47,7 @@ internal class FakeTwinCATClient : ITwinCATClient return Task.CompletedTask; } - /// Simulates reading a value from a symbol. - /// The path to the symbol to read. - /// The data type of the symbol. - /// The optional bit index for bit-level reads. - /// The cancellation token. - /// A task that returns the simulated value and status. + /// public virtual Task<(object? value, uint status)> ReadValueAsync( string symbolPath, TwinCATDataType type, int? bitIndex, CancellationToken ct) { @@ -66,13 +57,7 @@ internal class FakeTwinCATClient : ITwinCATClient return Task.FromResult((value, status)); } - /// Simulates writing a value to a symbol. - /// The path to the symbol to write. - /// The data type of the symbol. - /// The optional bit index for bit-level writes. - /// The value to write. - /// The cancellation token. - /// A task that returns the write status. + /// public virtual Task WriteValueAsync( string symbolPath, TwinCATDataType type, int? bitIndex, object? value, CancellationToken ct) { @@ -83,9 +68,7 @@ internal class FakeTwinCATClient : ITwinCATClient return Task.FromResult(status); } - /// Simulates probing the connection status. - /// The cancellation token. - /// A task that returns the probe result. + /// public virtual Task ProbeAsync(CancellationToken ct) { if (ThrowOnProbe) return Task.FromResult(false); @@ -108,15 +91,7 @@ internal class FakeTwinCATClient : ITwinCATClient /// Records the most recently-supplied maxDelayMs for Driver.TwinCAT-014 tests. public int LastMaxDelayMs { get; private set; } - /// Simulates adding a notification for value changes. - /// The path to the symbol to watch. - /// The data type of the symbol. - /// The optional bit index for bit-level notifications. - /// The sampling cycle time. - /// The maximum delay in milliseconds. - /// The callback to invoke on value change. - /// The cancellation token. - /// A task that returns a notification handle. + /// public virtual Task AddNotificationAsync( string symbolPath, TwinCATDataType type, int? bitIndex, TimeSpan cycleTime, int maxDelayMs, Action onChange, CancellationToken cancellationToken) @@ -147,9 +122,7 @@ internal class FakeTwinCATClient : ITwinCATClient /// Gets or sets a value indicating whether BrowseSymbolsAsync should throw. public bool ThrowOnBrowse { get; set; } - /// Simulates browsing the symbol tree. - /// The cancellation token. - /// An async enumerable of discovered symbols. + /// public virtual async IAsyncEnumerable BrowseSymbolsAsync( [EnumeratorCancellation] CancellationToken cancellationToken) { @@ -195,8 +168,7 @@ internal sealed class FakeTwinCATClientFactory : ITwinCATClientFactory /// Gets or sets an optional customization function for creating clients. public Func? Customise { get; set; } - /// Creates a new fake TwinCAT client. - /// A newly created client instance. + /// public ITwinCATClient Create() { var client = Customise?.Invoke() ?? new FakeTwinCATClient(); diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATCapabilityTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATCapabilityTests.cs index e6238956..0d703232 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATCapabilityTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATCapabilityTests.cs @@ -12,6 +12,7 @@ public sealed class TwinCATCapabilityTests // ---- ITagDiscovery ---- /// Verifies that DiscoverAsync emits pre-declared tags. + /// A task that represents the asynchronous operation. [Fact] public async Task DiscoverAsync_emits_pre_declared_tags() { @@ -39,6 +40,7 @@ public sealed class TwinCATCapabilityTests // ---- ISubscribable ---- /// Verifies that Subscribe initial poll raises OnDataChange. + /// A task that represents the asynchronous operation. [Fact] public async Task Subscribe_initial_poll_raises_OnDataChange() { @@ -66,6 +68,7 @@ public sealed class TwinCATCapabilityTests } /// Verifies that ShutdownAsync cancels active subscriptions. + /// A task that represents the asynchronous operation. [Fact] public async Task ShutdownAsync_cancels_active_subscriptions() { @@ -97,6 +100,7 @@ public sealed class TwinCATCapabilityTests // ---- IHostConnectivityProbe ---- /// Verifies that GetHostStatuses returns entry per device. + /// A task that represents the asynchronous operation. [Fact] public async Task GetHostStatuses_returns_entry_per_device() { @@ -115,6 +119,7 @@ public sealed class TwinCATCapabilityTests } /// Verifies that Probe transitions to Running on successful probe. + /// A task that represents the asynchronous operation. [Fact] public async Task Probe_transitions_to_Running_on_successful_probe() { @@ -142,6 +147,7 @@ public sealed class TwinCATCapabilityTests } /// Verifies that Probe transitions to Stopped on probe failure. + /// A task that represents the asynchronous operation. [Fact] public async Task Probe_transitions_to_Stopped_on_probe_failure() { @@ -169,6 +175,7 @@ public sealed class TwinCATCapabilityTests } /// Verifies that Probe is disabled when Enabled is false. + /// A task that represents the asynchronous operation. [Fact] public async Task Probe_disabled_when_Enabled_is_false() { @@ -188,6 +195,7 @@ public sealed class TwinCATCapabilityTests // ---- IPerCallHostResolver ---- /// Verifies that ResolveHost returns declared device for known tag. + /// A task that represents the asynchronous operation. [Fact] public async Task ResolveHost_returns_declared_device_for_known_tag() { @@ -212,6 +220,7 @@ public sealed class TwinCATCapabilityTests } /// Verifies that ResolveHost falls back to first device for unknown reference. + /// A task that represents the asynchronous operation. [Fact] public async Task ResolveHost_falls_back_to_first_device_for_unknown_ref() { @@ -226,6 +235,7 @@ public sealed class TwinCATCapabilityTests } /// Verifies that ResolveHost falls back to unresolved sentinel when no devices. + /// A task that represents the asynchronous operation. [Fact] public async Task ResolveHost_falls_back_to_unresolved_sentinel_when_no_devices() { @@ -253,39 +263,26 @@ public sealed class TwinCATCapabilityTests /// Gets the list of variables added to the address space. public List<(string BrowseName, DriverAttributeInfo Info)> Variables { get; } = new(); - /// Adds a folder with the specified browse name and display name. - /// The browse name of the folder. - /// The display name of the folder. - /// This builder instance. + /// public IAddressSpaceBuilder Folder(string browseName, string displayName) { Folders.Add((browseName, displayName)); return this; } - /// Adds a variable with the specified browse name, display name, and attribute info. - /// The browse name of the variable. - /// The display name of the variable. - /// The driver attribute information for the variable. - /// A variable handle for the added variable. + /// public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo info) { Variables.Add((browseName, info)); return new Handle(info.FullName); } - /// Adds a property to a variable. - /// The property name. - /// The property data type. - /// The property value. + /// public void AddProperty(string _, DriverDataType __, object? ___) { } private sealed class Handle(string fullRef) : IVariableHandle { - /// Gets the full reference name of the variable. + /// public string FullReference => fullRef; - /// Marks the variable as an alarm condition. - /// The alarm condition information. - /// An alarm condition sink. + /// public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) => new NullSink(); } private sealed class NullSink : IAlarmConditionSink { - /// Called when an alarm transitions. - /// The alarm event arguments. + /// public void OnTransition(AlarmEventArgs args) { } } } } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATDriverTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATDriverTests.cs index 7d6c5658..8914de5c 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATDriverTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATDriverTests.cs @@ -18,6 +18,7 @@ public sealed class TwinCATDriverTests } /// Verifies that device addresses are parsed during initialization. + /// A task that represents the asynchronous test. [Fact] public async Task InitializeAsync_parses_device_addresses() { @@ -38,6 +39,7 @@ public sealed class TwinCATDriverTests } /// Verifies that malformed device addresses cause initialization to fault. + /// A task that represents the asynchronous test. [Fact] public async Task InitializeAsync_malformed_address_faults() { @@ -52,6 +54,7 @@ public sealed class TwinCATDriverTests } /// Verifies that shutdown clears all devices. + /// A task that represents the asynchronous test. [Fact] public async Task ShutdownAsync_clears_devices() { @@ -68,6 +71,7 @@ public sealed class TwinCATDriverTests } /// Verifies that reinitialization cycles devices. + /// A task that represents the asynchronous test. [Fact] public async Task ReinitializeAsync_cycles_devices() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATHighFindingsRegressionTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATHighFindingsRegressionTests.cs index b20b013f..5680a4f5 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATHighFindingsRegressionTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATHighFindingsRegressionTests.cs @@ -20,6 +20,7 @@ public sealed class TwinCATHighFindingsRegressionTests // ---- Driver.TwinCAT-001 — Reinitialize applies the new config generation ---- /// Verifies ReinitializeAsync applies new device configuration at runtime. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReinitializeAsync_applies_changed_device_config() { @@ -49,6 +50,7 @@ public sealed class TwinCATHighFindingsRegressionTests } /// Verifies InitializeAsync applies supplied JSON config over constructor options. + /// A task that represents the asynchronous test operation. [Fact] public async Task InitializeAsync_applies_supplied_config_over_constructor_options() { @@ -83,6 +85,7 @@ public sealed class TwinCATHighFindingsRegressionTests } /// Verifies 64-bit LInt reads preserve values larger than int.MaxValue. + /// A task that represents the asynchronous test operation. [Fact] public async Task LInt_read_round_trips_value_above_int_MaxValue() { @@ -107,6 +110,7 @@ public sealed class TwinCATHighFindingsRegressionTests // ---- Driver.TwinCAT-007 — concurrent EnsureConnectedAsync creates exactly one client ---- /// Verifies concurrent reads on one device create and share exactly one client. + /// A task that represents the asynchronous test operation. [Fact] public async Task Concurrent_reads_on_one_device_create_a_single_client() { @@ -130,6 +134,7 @@ public sealed class TwinCATHighFindingsRegressionTests } /// Verifies concurrent reads and writes on one device share exactly one client. + /// A task that represents the asynchronous test operation. [Fact] public async Task Concurrent_reads_and_writes_share_one_client() { @@ -166,6 +171,7 @@ public sealed class TwinCATHighFindingsRegressionTests } /// Verifies symbol version change events raise the OnRediscoveryNeeded event. + /// A task that represents the asynchronous test operation. [Fact] public async Task Symbol_version_changed_raises_OnRediscoveryNeeded() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATLowFindingsRegressionTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATLowFindingsRegressionTests.cs index 019794b3..8faf14ac 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATLowFindingsRegressionTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATLowFindingsRegressionTests.cs @@ -37,6 +37,7 @@ public sealed class TwinCATLowFindingsRegressionTests // ---- Driver.TwinCAT-006 — ResolveHost sentinel when no devices are configured ---- /// Verifies that ResolveHost returns an unresolved sentinel when no devices are configured. + /// A task that represents the asynchronous test operation. [Fact] public async Task ResolveHost_returns_unresolved_sentinel_when_no_devices() { @@ -51,6 +52,7 @@ public sealed class TwinCATLowFindingsRegressionTests } /// Verifies that the unresolved sentinel does not match any GetHostStatuses entry. + /// A task that represents the asynchronous test operation. [Fact] public async Task ResolveHost_unresolved_sentinel_matches_no_GetHostStatuses_entry() { @@ -65,6 +67,7 @@ public sealed class TwinCATLowFindingsRegressionTests // ---- Driver.TwinCAT-014 — config surface knobs are honoured ---- /// Verifies that ProbeOptions.Timeout is applied to probe calls. + /// A task that represents the asynchronous test operation. [Fact] public async Task ProbeOptions_Timeout_is_applied_to_probe_calls() { @@ -209,6 +212,7 @@ public sealed class TwinCATLowFindingsRegressionTests } /// Verifies that the probe loop and read operations share one client per device. + /// A task that represents the asynchronous test operation. [Fact] public async Task Probe_loop_and_read_share_one_client_per_device() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATNativeNotificationTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATNativeNotificationTests.cs index 47df6aa9..24e599bc 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATNativeNotificationTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATNativeNotificationTests.cs @@ -23,6 +23,7 @@ public sealed class TwinCATNativeNotificationTests } /// Verifies that native subscribe registers one notification per tag. + /// A task that represents the asynchronous operation. [Fact] public async Task Native_subscribe_registers_one_notification_per_tag() { @@ -39,6 +40,7 @@ public sealed class TwinCATNativeNotificationTests } /// Verifies that native notification fires OnDataChange with pushed value. + /// A task that represents the asynchronous operation. [Fact] public async Task Native_notification_fires_OnDataChange_with_pushed_value() { @@ -60,6 +62,7 @@ public sealed class TwinCATNativeNotificationTests } /// Verifies that native unsubscribe disposes all notifications. + /// A task that represents the asynchronous operation. [Fact] public async Task Native_unsubscribe_disposes_all_notifications() { @@ -76,6 +79,7 @@ public sealed class TwinCATNativeNotificationTests } /// Verifies that native unsubscribe halts future notifications. + /// A task that represents the asynchronous operation. [Fact] public async Task Native_unsubscribe_halts_future_notifications() { @@ -99,6 +103,7 @@ public sealed class TwinCATNativeNotificationTests } /// Verifies that native subscribe failure mid-registration cleans up partial state. + /// A task that represents the asynchronous operation. [Fact] public async Task Native_subscribe_failure_mid_registration_cleans_up_partial_state() { @@ -157,6 +162,7 @@ public sealed class TwinCATNativeNotificationTests } /// Verifies that native shutdown disposes subscriptions. + /// A task that represents the asynchronous operation. [Fact] public async Task Native_shutdown_disposes_subscriptions() { @@ -172,6 +178,7 @@ public sealed class TwinCATNativeNotificationTests } /// Verifies that the poll path still works when UseNativeNotifications is false. + /// A task that represents the asynchronous operation. [Fact] public async Task Poll_path_still_works_when_UseNativeNotifications_false() { @@ -200,6 +207,7 @@ public sealed class TwinCATNativeNotificationTests } /// Verifies that subscribe handle DiagnosticId indicates native vs poll. + /// A task that represents the asynchronous operation. [Fact] public async Task Subscribe_handle_DiagnosticId_indicates_native_vs_poll() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATReadWriteTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATReadWriteTests.cs index e97d9409..295e57a9 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATReadWriteTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATReadWriteTests.cs @@ -23,6 +23,7 @@ public sealed class TwinCATReadWriteTests // ---- Read ---- /// Verifies that an unknown reference maps to BadNodeIdUnknown status. + /// A task that represents the asynchronous test operation. [Fact] public async Task Unknown_reference_maps_to_BadNodeIdUnknown() { @@ -34,6 +35,7 @@ public sealed class TwinCATReadWriteTests } /// Verifies that a successful DInt read returns Good status and the correct value. + /// A task that represents the asynchronous test operation. [Fact] public async Task Successful_DInt_read_returns_Good_value() { @@ -51,6 +53,7 @@ public sealed class TwinCATReadWriteTests } /// Verifies that repeated read operations reuse the same connection. + /// A task that represents the asynchronous test operation. [Fact] public async Task Repeat_read_reuses_connection() { @@ -69,6 +72,7 @@ public sealed class TwinCATReadWriteTests } /// Verifies that ADS read errors are mapped via the status mapper. + /// A task that represents the asynchronous test operation. [Fact] public async Task Read_with_ADS_error_maps_via_status_mapper() { @@ -87,6 +91,7 @@ public sealed class TwinCATReadWriteTests } /// Verifies that read exceptions surface as BadCommunicationError status. + /// A task that represents the asynchronous test operation. [Fact] public async Task Read_exception_surfaces_BadCommunicationError() { @@ -101,6 +106,7 @@ public sealed class TwinCATReadWriteTests } /// Verifies that connect failures surface BadCommunicationError and dispose the client. + /// A task that represents the asynchronous test operation. [Fact] public async Task Connect_failure_surfaces_BadCommunicationError_and_disposes_client() { @@ -115,6 +121,7 @@ public sealed class TwinCATReadWriteTests } /// Verifies that batched read operations preserve the order of results. + /// A task that represents the asynchronous test operation. [Fact] public async Task Batched_reads_preserve_order() { @@ -142,6 +149,7 @@ public sealed class TwinCATReadWriteTests // ---- Write ---- /// Verifies that non-writable tags are rejected with BadNotWritable status. + /// A task that represents the asynchronous test operation. [Fact] public async Task Non_writable_tag_rejected_with_BadNotWritable() { @@ -155,6 +163,7 @@ public sealed class TwinCATReadWriteTests } /// Verifies that successful writes log the symbol, type, and value correctly. + /// A task that represents the asynchronous test operation. [Fact] public async Task Successful_write_logs_symbol_type_value() { @@ -173,6 +182,7 @@ public sealed class TwinCATReadWriteTests } /// Verifies that ADS write errors are mapped and surfaced correctly. + /// A task that represents the asynchronous test operation. [Fact] public async Task Write_with_ADS_error_surfaces_mapped_status() { @@ -192,6 +202,7 @@ public sealed class TwinCATReadWriteTests } /// Verifies that write exceptions surface as BadCommunicationError status. + /// A task that represents the asynchronous test operation. [Fact] public async Task Write_exception_surfaces_BadCommunicationError() { @@ -206,6 +217,7 @@ public sealed class TwinCATReadWriteTests } /// Verifies that batched write operations preserve order across mixed outcomes. + /// A task that represents the asynchronous test operation. [Fact] public async Task Batch_write_preserves_order_across_outcomes() { @@ -236,6 +248,7 @@ public sealed class TwinCATReadWriteTests } /// Verifies that cancellation tokens propagate correctly during read operations. + /// A task that represents the asynchronous test operation. [Fact] public async Task Cancellation_propagates() { @@ -253,6 +266,7 @@ public sealed class TwinCATReadWriteTests } /// Verifies that shutdown disposes the client correctly. + /// A task that represents the asynchronous test operation. [Fact] public async Task ShutdownAsync_disposes_client() { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATSymbolBrowserTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATSymbolBrowserTests.cs index 73c04696..ccce5f9e 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATSymbolBrowserTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests/TwinCATSymbolBrowserTests.cs @@ -9,6 +9,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Tests; public sealed class TwinCATSymbolBrowserTests { /// Verifies that discovery without EnableControllerBrowse only emits predeclared tags. + /// A task that represents the asynchronous test operation. [Fact] public async Task Discovery_without_EnableControllerBrowse_emits_only_predeclared() { @@ -38,6 +39,7 @@ public sealed class TwinCATSymbolBrowserTests } /// Verifies that discovery with browse enabled adds controller symbols under Discovered folder. + /// A task that represents the asynchronous test operation. [Fact] public async Task Discovery_with_browse_enabled_adds_controller_symbols_under_Discovered_folder() { @@ -68,6 +70,7 @@ public sealed class TwinCATSymbolBrowserTests } /// Verifies that browse filters out system symbols correctly. + /// A task that represents the asynchronous test operation. [Fact] public async Task Browse_filters_system_symbols() { @@ -99,6 +102,7 @@ public sealed class TwinCATSymbolBrowserTests } /// Verifies that browse skips symbols with null datatype. + /// A task that represents the asynchronous test operation. [Fact] public async Task Browse_skips_symbols_with_null_datatype() { @@ -127,6 +131,7 @@ public sealed class TwinCATSymbolBrowserTests } /// Verifies that read-only symbols surface as ViewOnly. + /// A task that represents the asynchronous test operation. [Fact] public async Task ReadOnly_symbol_surfaces_ViewOnly() { @@ -154,6 +159,7 @@ public sealed class TwinCATSymbolBrowserTests } /// Verifies that browse failure is non-fatal and predeclared tags still emit. + /// A task that represents the asynchronous test operation. [Fact] public async Task Browse_failure_is_non_fatal_predeclared_still_emits() { @@ -252,9 +258,7 @@ public sealed class TwinCATSymbolBrowserTests public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) => new NullSink(); } - /// - /// A null implementation of IAlarmConditionSink for test purposes. - /// + /// A null implementation of IAlarmConditionSink for test purposes. private sealed class NullSink : IAlarmConditionSink { /// diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/AbCipDriverPageFormSerializationTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/AbCipDriverPageFormSerializationTests.cs index 060ca94a..e76a04dd 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/AbCipDriverPageFormSerializationTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/AbCipDriverPageFormSerializationTests.cs @@ -21,6 +21,7 @@ public sealed class AbCipDriverPageFormSerializationTests UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip, }; + /// Verifies that serializing and deserializing an round-trip preserves all known fields. [Fact] public void RoundTrip_PreservesKnownFields() { @@ -71,6 +72,7 @@ public sealed class AbCipDriverPageFormSerializationTests back.Tags[0].DataType.ShouldBe(AbCipDataType.Real); } + /// Verifies that deserializing JSON with unknown fields drops them without error. [Fact] public void Deserialize_DropsUnknownFields() { @@ -86,6 +88,7 @@ public sealed class AbCipDriverPageFormSerializationTests back.ProbeTimeoutSeconds.ShouldBe(10); } + /// Verifies that a device row round-trips correctly through its definition. [Fact] public void DeviceRow_round_trips_through_definition() { @@ -101,6 +104,7 @@ public sealed class AbCipDriverPageFormSerializationTests back.DeviceName.ShouldBe("PLC-A"); } + /// Verifies that editing one field of a device row preserves all other fields. [Fact] public void DeviceRow_preserves_unedited_fields() { @@ -116,6 +120,7 @@ public sealed class AbCipDriverPageFormSerializationTests back.ConnectionSize.ShouldBe(4002); } + /// Verifies that a tag row round-trips correctly through its definition. [Fact] public void TagRow_round_trips_through_definition() { @@ -134,6 +139,7 @@ public sealed class AbCipDriverPageFormSerializationTests back.Writable.ShouldBeTrue(); } + /// Verifies that editing one field of a tag row preserves all other fields. [Fact] public void TagRow_preserves_unedited_fields() { @@ -154,6 +160,7 @@ public sealed class AbCipDriverPageFormSerializationTests back.Members[0].Name.ShouldBe("Sub"); } + /// Verifies that device row validation rejects a duplicate host address. [Fact] public void ValidateDeviceRow_rejects_duplicate_host() { @@ -162,6 +169,7 @@ public sealed class AbCipDriverPageFormSerializationTests .ShouldNotBeNull(); } + /// Verifies that tag row validation rejects a duplicate tag name. [Fact] public void ValidateTagRow_rejects_duplicate_name() { @@ -170,6 +178,7 @@ public sealed class AbCipDriverPageFormSerializationTests .ShouldNotBeNull(); } + /// Verifies that device and tag lists survive a full options serialize/deserialize round-trip. [Fact] public void Device_and_tag_lists_survive_options_serialize_round_trip() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/AbLegacyDriverPageFormSerializationTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/AbLegacyDriverPageFormSerializationTests.cs index 1df73121..d3114817 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/AbLegacyDriverPageFormSerializationTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/AbLegacyDriverPageFormSerializationTests.cs @@ -22,6 +22,7 @@ public sealed class AbLegacyDriverPageFormSerializationTests UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip, }; + /// Verifies that round-trip serialization preserves all known fields. [Fact] public void RoundTrip_PreservesKnownFields() { @@ -71,6 +72,7 @@ public sealed class AbLegacyDriverPageFormSerializationTests back.Tags[1].DataType.ShouldBe(AbLegacyDataType.Bit); } + /// Verifies that deserialization silently drops unknown fields. [Fact] public void Deserialize_DropsUnknownFields() { @@ -86,6 +88,7 @@ public sealed class AbLegacyDriverPageFormSerializationTests back.ProbeTimeoutSeconds.ShouldBe(10); } + /// Verifies that a device row round-trips through its definition. [Fact] public void DeviceRow_round_trips_through_definition() { @@ -101,6 +104,7 @@ public sealed class AbLegacyDriverPageFormSerializationTests back.DeviceName.ShouldBe("PLC-A"); } + /// Verifies that a device row preserves unedited fields when converting back to definition. [Fact] public void DeviceRow_preserves_unedited_fields() { @@ -114,6 +118,7 @@ public sealed class AbLegacyDriverPageFormSerializationTests back.DeviceName.ShouldBe("PLC-A"); } + /// Verifies that a tag row round-trips through its definition. [Fact] public void TagRow_round_trips_through_definition() { @@ -132,6 +137,7 @@ public sealed class AbLegacyDriverPageFormSerializationTests back.Writable.ShouldBeTrue(); } + /// Verifies that a tag row preserves unedited fields when converting back to definition. [Fact] public void TagRow_preserves_unedited_fields() { @@ -146,6 +152,7 @@ public sealed class AbLegacyDriverPageFormSerializationTests back.WriteIdempotent.ShouldBeTrue(); } + /// Verifies that device row validation rejects a duplicate host address. [Fact] public void ValidateDeviceRow_rejects_duplicate_host() { @@ -154,6 +161,7 @@ public sealed class AbLegacyDriverPageFormSerializationTests .ShouldNotBeNull(); } + /// Verifies that tag row validation rejects a duplicate tag name. [Fact] public void ValidateTagRow_rejects_duplicate_name() { @@ -162,6 +170,7 @@ public sealed class AbLegacyDriverPageFormSerializationTests .ShouldNotBeNull(); } + /// Verifies that device and tag lists survive a full options serialization round-trip. [Fact] public void Device_and_tag_lists_survive_options_serialize_round_trip() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Browsing/BrowseSessionReaperTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Browsing/BrowseSessionReaperTests.cs index 076aa933..f6687d30 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Browsing/BrowseSessionReaperTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Browsing/BrowseSessionReaperTests.cs @@ -14,6 +14,8 @@ public sealed class BrowseSessionReaperTests private static BrowseSessionReaper NewReaper(BrowseSessionRegistry registry) => new(registry, NullLogger.Instance); + /// Verifies that ReapOnceAsync evicts a session that has been idle beyond the timeout. + /// A task that represents the asynchronous operation. [Fact] public async Task ReapOnceAsync_evicts_idle_session() { @@ -31,6 +33,8 @@ public sealed class BrowseSessionReaperTests session.Disposed.ShouldBeTrue(); } + /// Verifies that ReapOnceAsync preserves a recently used session. + /// A task that represents the asynchronous operation. [Fact] public async Task ReapOnceAsync_preserves_recent_session() { @@ -45,6 +49,8 @@ public sealed class BrowseSessionReaperTests session.Disposed.ShouldBeFalse(); } + /// Verifies that ReapOnceAsync handles a session already removed from the registry without throwing. + /// A task that represents the asynchronous operation. [Fact] public async Task ReapOnceAsync_handles_already_removed_session() { @@ -65,6 +71,8 @@ public sealed class BrowseSessionReaperTests session.Disposed.ShouldBeFalse(); } + /// Verifies that ReapOnceAsync continues processing remaining sessions when one session's dispose throws. + /// A task that represents the asynchronous operation. [Fact] public async Task ReapOnceAsync_continues_when_one_session_dispose_throws() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Browsing/BrowseSessionRegistryTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Browsing/BrowseSessionRegistryTests.cs index 2e2dca9b..f60ad202 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Browsing/BrowseSessionRegistryTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Browsing/BrowseSessionRegistryTests.cs @@ -9,6 +9,7 @@ namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Browsing; /// concurrent-registration behaviour. public sealed class BrowseSessionRegistryTests { + /// Verifies that a registered session can be retrieved by its token. [Fact] public void Register_then_TryGet_returns_session() { @@ -22,6 +23,7 @@ public sealed class BrowseSessionRegistryTests got.ShouldBeSameAs((IBrowseSession)session); } + /// Verifies that looking up an unknown token returns false. [Fact] public void TryGet_unknown_returns_false() { @@ -31,6 +33,7 @@ public sealed class BrowseSessionRegistryTests got.ShouldBeNull(); } + /// Verifies that a removed session can no longer be retrieved. [Fact] public void TryRemove_then_TryGet_returns_false() { @@ -43,6 +46,8 @@ public sealed class BrowseSessionRegistryTests registry.TryGet(session.Token, out _).ShouldBeFalse(); } + /// Verifies that concurrent registrations from many tasks are all visible in the snapshot. + /// A task that represents the asynchronous operation. [Fact] public async Task Concurrent_Register_from_many_tasks_all_visible_in_Snapshot() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Browsing/BrowserSessionServiceTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Browsing/BrowserSessionServiceTests.cs index d69d8322..3bcbc8cc 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Browsing/BrowserSessionServiceTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Browsing/BrowserSessionServiceTests.cs @@ -15,6 +15,8 @@ public sealed class BrowserSessionServiceTests BrowseSessionRegistry registry, params IDriverBrowser[] browsers) => new(browsers, registry, NullLogger.Instance); + /// Verifies that OpenAsync returns Ok=false with a message when the driver type is unknown. + /// A task that represents the asynchronous test operation. [Fact] public async Task OpenAsync_unknown_driver_type_returns_Ok_false_with_message() { @@ -29,6 +31,8 @@ public sealed class BrowserSessionServiceTests result.Message!.ShouldContain("Unknown"); } + /// Verifies that OpenAsync returns a token and registers the session on the happy path. + /// A task that represents the asynchronous test operation. [Fact] public async Task OpenAsync_happy_path_returns_token_and_registers() { @@ -50,6 +54,8 @@ public sealed class BrowserSessionServiceTests registered.ShouldBeSameAs((IBrowseSession)session); } + /// Verifies that OpenAsync swallows driver exceptions and returns Ok=false. + /// A task that represents the asynchronous test operation. [Fact] public async Task OpenAsync_swallows_driver_throws_returns_Ok_false() { @@ -68,6 +74,8 @@ public sealed class BrowserSessionServiceTests result.Message!.ShouldContain("boom"); } + /// Verifies that RootAsync throws BrowseSessionNotFoundException for an unknown token. + /// A task that represents the asynchronous test operation. [Fact] public async Task RootAsync_unknown_token_throws_BrowseSessionNotFoundException() { @@ -78,6 +86,8 @@ public sealed class BrowserSessionServiceTests () => service.RootAsync(Guid.NewGuid(), CancellationToken.None)); } + /// Verifies that RootAsync invokes the session Root method and returns its result. + /// A task that represents the asynchronous test operation. [Fact] public async Task RootAsync_invokes_session_Root() { @@ -96,6 +106,8 @@ public sealed class BrowserSessionServiceTests actual.ShouldBe(expected); } + /// Verifies that RootAsync cancels the call when the per-call timeout elapses. + /// A task that represents the asynchronous test operation. [Fact] public async Task RootAsync_enforces_PerCallTimeout() { @@ -118,6 +130,8 @@ public sealed class BrowserSessionServiceTests sw.Elapsed.ShouldBeLessThan(TimeSpan.FromSeconds(35)); } + /// Verifies that CloseAsync removes the session from the registry and disposes it. + /// A task that represents the asynchronous test operation. [Fact] public async Task CloseAsync_removes_and_disposes_session() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Browsing/FakeBrowseSession.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Browsing/FakeBrowseSession.cs index 1930dc41..d2042224 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Browsing/FakeBrowseSession.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Browsing/FakeBrowseSession.cs @@ -11,7 +11,7 @@ internal sealed class FakeBrowseSession : IBrowseSession /// public Guid Token { get; } = Guid.NewGuid(); - /// Mutable so tests can rewind the timestamp into the reaper's eviction window. + /// public DateTime LastUsedUtc { get; set; } = DateTime.UtcNow; /// True once has run to completion. @@ -40,7 +40,8 @@ internal sealed class FakeBrowseSession : IBrowseSession public Task> AttributesAsync(string nodeId, CancellationToken ct) => AttributesHandler?.Invoke(nodeId, ct) ?? Task.FromResult>(Array.Empty()); - /// + /// Disposes the fake browse session asynchronously, recording completion or throwing if configured. + /// A completed value task. public ValueTask DisposeAsync() { if (ThrowOnDispose) throw new InvalidOperationException("dispose-failed"); diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/DriverStatusSnapshotStoreTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/DriverStatusSnapshotStoreTests.cs index 410c5e53..c2e882b0 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/DriverStatusSnapshotStoreTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/DriverStatusSnapshotStoreTests.cs @@ -17,6 +17,7 @@ public sealed class DriverStatusSnapshotStoreTests private static DriverHealthChanged Snap(string instance, string state = "Healthy") => new("MAIN", instance, state, null, null, 0, new DateTime(2026, 5, 29, 0, 0, 0, DateTimeKind.Utc)); + /// Verifies that Upsert raises SnapshotChanged with the stored snapshot. [Fact] public void Upsert_raises_SnapshotChanged_with_the_stored_snapshot() { @@ -31,6 +32,7 @@ public sealed class DriverStatusSnapshotStoreTests received[0].ShouldBeSameAs(snap); } + /// Verifies that Upsert then TryGet returns the latest snapshot. [Fact] public void Upsert_then_TryGet_returns_the_latest_snapshot() { @@ -42,6 +44,7 @@ public sealed class DriverStatusSnapshotStoreTests latest.State.ShouldBe("Degraded"); } + /// Verifies that an unsubscribed handler stops receiving events after removal. [Fact] public void Unsubscribed_handler_stops_receiving_after_removal() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/FocasDriverPageFormSerializationTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/FocasDriverPageFormSerializationTests.cs index 2f057cc6..33bb528c 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/FocasDriverPageFormSerializationTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/FocasDriverPageFormSerializationTests.cs @@ -21,6 +21,7 @@ public sealed class FocasDriverPageFormSerializationTests UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip, }; + /// Verifies that a round-trip serialization preserves all known FOCAS driver option fields. [Fact] public void RoundTrip_PreservesKnownFields() { @@ -76,6 +77,7 @@ public sealed class FocasDriverPageFormSerializationTests back.Tags.ShouldBeEmpty(); } + /// Verifies that deserialization silently drops unknown fields without throwing. [Fact] public void Deserialize_DropsUnknownFields() { @@ -89,6 +91,7 @@ public sealed class FocasDriverPageFormSerializationTests back.ProbeTimeoutSeconds.ShouldBe(18); } + /// Verifies that the form model round-trip preserves all editable FOCAS driver option fields. [Fact] public void FormModel_RoundTrip_PreservesEditableFields() { @@ -140,6 +143,7 @@ public sealed class FocasDriverPageFormSerializationTests roundTripped.FixedTree.TimerPollInterval.ShouldBe(TimeSpan.FromSeconds(45)); } + /// Verifies that a FocasDeviceRow round-trips correctly through its definition type. [Fact] public void DeviceRow_round_trips_through_definition() { @@ -155,6 +159,7 @@ public sealed class FocasDriverPageFormSerializationTests back.DeviceName.ShouldBe("CNC1"); } + /// Verifies that FocasDeviceRow preserves unedited fields when converting to and from a definition. [Fact] public void DeviceRow_preserves_unedited_fields() { @@ -168,6 +173,7 @@ public sealed class FocasDriverPageFormSerializationTests back.Series.ShouldBe(FocasCncSeries.Thirty_i); } + /// Verifies that a FocasTagRow round-trips correctly through its definition type. [Fact] public void TagRow_round_trips_through_definition() { @@ -186,6 +192,7 @@ public sealed class FocasDriverPageFormSerializationTests back.Writable.ShouldBeTrue(); } + /// Verifies that FocasTagRow preserves unedited fields when converting to a definition. [Fact] public void TagRow_preserves_unedited_fields() { @@ -200,6 +207,7 @@ public sealed class FocasDriverPageFormSerializationTests back.WriteIdempotent.ShouldBeTrue(); } + /// Verifies that device row validation rejects a duplicate host address. [Fact] public void ValidateDeviceRow_rejects_duplicate_host() { @@ -208,6 +216,7 @@ public sealed class FocasDriverPageFormSerializationTests .ShouldNotBeNull(); } + /// Verifies that tag row validation rejects a duplicate tag name. [Fact] public void ValidateTagRow_rejects_duplicate_name() { @@ -216,6 +225,7 @@ public sealed class FocasDriverPageFormSerializationTests .ShouldNotBeNull(); } + /// Verifies that device and tag lists survive a full options serialization round-trip. [Fact] public void Device_and_tag_lists_survive_options_serialize_round_trip() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/GalaxyDriverPageFormSerializationTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/GalaxyDriverPageFormSerializationTests.cs index 001fdcc8..d0410505 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/GalaxyDriverPageFormSerializationTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/GalaxyDriverPageFormSerializationTests.cs @@ -25,6 +25,7 @@ public sealed class GalaxyDriverPageFormSerializationTests WriteIndented = false, }; + /// Verifies that a round-trip serialization preserves all known fields of GalaxyDriverOptions. [Fact] public void RoundTrip_PreservesKnownFields() { @@ -76,6 +77,7 @@ public sealed class GalaxyDriverPageFormSerializationTests back.ProbeTimeoutSeconds.ShouldBe(45); } + /// Verifies that deserialization silently drops unknown fields from the JSON input. [Fact] public void Deserialize_DropsUnknownFields() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/HistorianWonderwareDriverPageFormSerializationTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/HistorianWonderwareDriverPageFormSerializationTests.cs index 365a3e6f..6e963244 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/HistorianWonderwareDriverPageFormSerializationTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/HistorianWonderwareDriverPageFormSerializationTests.cs @@ -15,6 +15,7 @@ public sealed class HistorianWonderwareDriverPageFormSerializationTests WriteIndented = false, }; + /// Verifies that a round-trip serialization/deserialization preserves all known fields. [Fact] public void RoundTrip_PreservesKnownFields() { @@ -42,6 +43,7 @@ public sealed class HistorianWonderwareDriverPageFormSerializationTests back.ProbeTimeoutSeconds.ShouldBe(25); } + /// Verifies that null timeout values fall back to the expected defaults after deserialization. [Fact] public void RoundTrip_NullTimeouts_UsesDefaults() { @@ -59,6 +61,7 @@ public sealed class HistorianWonderwareDriverPageFormSerializationTests back.EffectiveCallTimeout.ShouldBe(TimeSpan.FromSeconds(30)); } + /// Verifies that unknown JSON properties are silently ignored during deserialization. [Fact] public void Deserialize_DropsUnknownFields() { @@ -82,6 +85,7 @@ public sealed class HistorianWonderwareDriverPageFormSerializationTests back.PipeName.ShouldBe("otopcua-historian"); } + /// Verifies that the form model FromRecord/ToRecord round-trip preserves all fields losslessly. [Fact] public void FormModel_RoundTrip_PreservesAllFields() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/InProcessBroadcasterTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/InProcessBroadcasterTests.cs index 263a31da..7bd15e38 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/InProcessBroadcasterTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/InProcessBroadcasterTests.cs @@ -12,6 +12,7 @@ namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests; /// public sealed class InProcessBroadcasterTests { + /// Verifies that Publish raises the Received event for all current subscribers. [Fact] public void Publish_raises_Received_for_all_current_subscribers() { @@ -27,6 +28,7 @@ public sealed class InProcessBroadcasterTests b.ShouldBe(["evt-1"]); } + /// Verifies that an unsubscribed handler stops receiving events after removal. [Fact] public void Unsubscribed_handler_stops_receiving() { @@ -42,6 +44,7 @@ public sealed class InProcessBroadcasterTests received.ShouldBe(["first"]); } + /// Verifies that Publish with no subscribers does not throw an exception. [Fact] public void Publish_with_no_subscribers_does_not_throw() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ModbusDriverPageFormSerializationTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ModbusDriverPageFormSerializationTests.cs index b0777da0..b2276861 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ModbusDriverPageFormSerializationTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ModbusDriverPageFormSerializationTests.cs @@ -21,6 +21,7 @@ public sealed class ModbusDriverPageFormSerializationTests UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip, }; + /// Verifies that round-trip serialization preserves all known Modbus driver fields. [Fact] public void RoundTrip_PreservesKnownFields() { @@ -97,6 +98,7 @@ public sealed class ModbusDriverPageFormSerializationTests back.ProbeTimeoutSeconds.ShouldBe(10); } + /// Verifies that deserialization silently drops unknown Modbus driver fields. [Fact] public void Deserialize_DropsUnknownFields() { @@ -112,6 +114,7 @@ public sealed class ModbusDriverPageFormSerializationTests back.ProbeTimeoutSeconds.ShouldBe(10); } + /// Verifies that a tag row round-trips through its definition. [Fact] public void TagRow_round_trips_through_definition() { @@ -129,6 +132,7 @@ public sealed class ModbusDriverPageFormSerializationTests back.Writable.ShouldBeTrue(); } + /// Verifies that the tag list survives a full options serialization round-trip. [Fact] public void Tag_list_survives_options_serialize_round_trip() { @@ -144,6 +148,7 @@ public sealed class ModbusDriverPageFormSerializationTests back.Tags[0].Name.ShouldBe("A"); } + /// Verifies that tag row validation rejects a duplicate tag name. [Fact] public void ValidateRow_rejects_duplicate_name() { @@ -152,6 +157,7 @@ public sealed class ModbusDriverPageFormSerializationTests .ShouldNotBeNull(); } + /// Verifies that converting a tag row to a definition preserves unedited fields. [Fact] public void ToDefinition_preserves_unedited_fields() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/OpcUaClientDriverPageFormSerializationTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/OpcUaClientDriverPageFormSerializationTests.cs index 60920f4d..bd6be2f5 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/OpcUaClientDriverPageFormSerializationTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/OpcUaClientDriverPageFormSerializationTests.cs @@ -17,6 +17,7 @@ public sealed class OpcUaClientDriverPageFormSerializationTests WriteIndented = false, }; + /// Verifies that a round-trip serialization preserves all known driver option fields. [Fact] public void RoundTrip_PreservesKnownFields() { @@ -68,6 +69,7 @@ public sealed class OpcUaClientDriverPageFormSerializationTests back.ProbeTimeoutSeconds.ShouldBe(20); } + /// Verifies that deserialization silently drops unknown fields. [Fact] public void Deserialize_DropsUnknownFields() { @@ -83,6 +85,7 @@ public sealed class OpcUaClientDriverPageFormSerializationTests back.ProbeTimeoutSeconds.ShouldBe(20); } + /// Verifies that the form model round-trip preserves all driver option fields. [Fact] public void FormModel_RoundTrip_PreservesAllFields() { @@ -156,6 +159,7 @@ public sealed class OpcUaClientDriverPageFormSerializationTests result.ProbeTimeoutSeconds.ShouldBe(25); } + /// Verifies that EndpointUrlRow.ToUrl trims leading and trailing whitespace. [Fact] public void EndpointUrlRow_FromUrl_ToUrl_Trims() { @@ -165,6 +169,7 @@ public sealed class OpcUaClientDriverPageFormSerializationTests row.ToUrl().ShouldBe("opc.tcp://plc:4840"); } + /// Verifies that ValidateRow rejects a blank URL. [Fact] public void EndpointUrlRow_ValidateRow_RejectsBlank() { @@ -176,6 +181,7 @@ public sealed class OpcUaClientDriverPageFormSerializationTests error.ShouldBe("URL is required."); } + /// Verifies that ValidateRow rejects a URL with a non-opc.tcp scheme. [Fact] public void EndpointUrlRow_ValidateRow_RejectsNonOpcTcpScheme() { @@ -187,6 +193,7 @@ public sealed class OpcUaClientDriverPageFormSerializationTests error.ShouldBe("Endpoint URL must start with opc.tcp://"); } + /// Verifies that ValidateRow rejects a duplicate URL. [Fact] public void EndpointUrlRow_ValidateRow_RejectsDuplicate() { @@ -204,6 +211,7 @@ public sealed class OpcUaClientDriverPageFormSerializationTests error.ShouldContain("Duplicate endpoint"); } + /// Verifies that editing a row in-place does not flag it as a duplicate of itself. [Fact] public void EndpointUrlRow_ValidateRow_AllowsEditingRowInPlace() { @@ -220,6 +228,7 @@ public sealed class OpcUaClientDriverPageFormSerializationTests error.ShouldBeNull(); } + /// Verifies that an endpoint URL list round-trips and preserves the original order. [Fact] public void EndpointUrls_ListRoundTrip_PreservesOrder() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Pickers/AbLegacyAddressBuilderTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Pickers/AbLegacyAddressBuilderTests.cs index b4452b18..4acafae2 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Pickers/AbLegacyAddressBuilderTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Pickers/AbLegacyAddressBuilderTests.cs @@ -6,6 +6,11 @@ namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Pickers; public sealed class AbLegacyAddressBuilderTests { + /// Verifies that Build_Canonical returns the expected canonical address string for each file type. + /// The file type letter (e.g., N, B, F). + /// The file number. + /// The element index within the file. + /// The expected canonical address string. [Theory] [InlineData("N", 7, 0, "N7:0")] [InlineData("B", 3, 1, "B3:1")] diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Pickers/FocasAddressBuilderTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Pickers/FocasAddressBuilderTests.cs index d95d057a..13637c6b 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Pickers/FocasAddressBuilderTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Pickers/FocasAddressBuilderTests.cs @@ -6,6 +6,10 @@ namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Pickers; public sealed class FocasAddressBuilderTests { + /// Verifies that Build produces the canonical group:parameterId address string. + /// The FOCAS parameter group name. + /// The FOCAS parameter ID. + /// The expected canonical address string. [Theory] [InlineData("axis", 5, "axis:5")] [InlineData("spindle", 0, "spindle:0")] diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Pickers/HistorianWonderwareAddressBuilderTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Pickers/HistorianWonderwareAddressBuilderTests.cs index 816e550f..4af8b96a 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Pickers/HistorianWonderwareAddressBuilderTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Pickers/HistorianWonderwareAddressBuilderTests.cs @@ -6,6 +6,11 @@ namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Pickers; public sealed class HistorianWonderwareAddressBuilderTests { + /// Verifies that Build produces the canonical address query string for various tag/mode/interval combinations. + /// The tag name input. + /// The retrieval mode input. + /// The interval input. + /// The expected canonical address string. [Theory] [InlineData("SysTimeHour", "Cyclic", 60, "SysTimeHour?mode=Cyclic&interval=60")] [InlineData("ReactorTemp", "Last", 1, "ReactorTemp?mode=Last&interval=1")] diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Pickers/ModbusAddressBuilderTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Pickers/ModbusAddressBuilderTests.cs index 1a25886a..2dbae2db 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Pickers/ModbusAddressBuilderTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Pickers/ModbusAddressBuilderTests.cs @@ -6,6 +6,11 @@ namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Pickers; public sealed class ModbusAddressBuilderTests { + /// Verifies that Build produces canonical Modbus address strings for all supported register types. + /// The Modbus register type. + /// The register offset. + /// The data length. + /// The expected canonical address string. [Theory] [InlineData("Holding", 1, 1, "4x00001-1")] [InlineData("Coil", 0, 1, "0x00000-1")] @@ -15,6 +20,7 @@ public sealed class ModbusAddressBuilderTests public void Build_Canonical(string type, int offset, int length, string expected) => ModbusAddressBuilder.Build(type, offset, length).ShouldBe(expected); + /// Verifies that Build falls back to the Holding register type for unknown type strings. [Fact] public void Build_UnknownType_FallsBackToHolding() => ModbusAddressBuilder.Build("Unknown", 1, 1).ShouldBe("4x00001-1"); diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Pickers/S7AddressBuilderTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Pickers/S7AddressBuilderTests.cs index 38f102df..f59cb1a7 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Pickers/S7AddressBuilderTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Pickers/S7AddressBuilderTests.cs @@ -6,6 +6,12 @@ namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Pickers; public sealed class S7AddressBuilderTests { + /// Verifies that Build produces the canonical S7 address string for all supported areas and types. + /// The S7 memory area (DB / M / I / Q). + /// The DB number (relevant only for the DB area). + /// The byte offset within the area. + /// The S7 data type qualifier (X / B / W / D / REAL). + /// The expected canonical address string. [Theory] [InlineData("DB", 10, 20, "REAL", "DB10.DBD20:REAL")] [InlineData("DB", 1, 0, "X", "DB1.DBX0.0:X")] diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ResilienceFormModelTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ResilienceFormModelTests.cs index cb95310a..ca640854 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ResilienceFormModelTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ResilienceFormModelTests.cs @@ -6,10 +6,12 @@ using ZB.MOM.WW.OtOpcUa.Core.Resilience; public class ResilienceFormModelTests { + /// Verifies that a blank form model serializes to null JSON. [Fact] public void Blank_form_serializes_to_null() => new ResilienceFormModel().ToJson().ShouldBeNull(); + /// Verifies that a partial policy override round-trips correctly through JSON. [Fact] public void Partial_override_round_trips() { @@ -26,6 +28,7 @@ public class ResilienceFormModelTests back.Policies["Write"].IsEmpty.ShouldBeTrue(); } + /// Verifies that malformed JSON input yields an empty model with no-error handling. [Fact] public void Malformed_json_yields_empty_model() { @@ -34,6 +37,7 @@ public class ResilienceFormModelTests m.Policies["Read"].IsEmpty.ShouldBeTrue(); } + /// Verifies that JSON emitted by the form model can be parsed by the runtime resilience options parser. [Fact] public void Emitted_json_is_consumable_by_the_runtime_parser() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/S7DriverPageFormSerializationTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/S7DriverPageFormSerializationTests.cs index 2e50a9d9..02010f2c 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/S7DriverPageFormSerializationTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/S7DriverPageFormSerializationTests.cs @@ -15,6 +15,7 @@ public sealed class S7DriverPageFormSerializationTests WriteIndented = false, }; + /// Verifies that serializing and deserializing S7 driver options preserves all known fields. [Fact] public void RoundTrip_PreservesKnownFields() { @@ -54,6 +55,7 @@ public sealed class S7DriverPageFormSerializationTests back.Tags.ShouldBeEmpty(); } + /// Verifies that deserializing JSON with unknown fields silently drops the unrecognized members. [Fact] public void Deserialize_DropsUnknownFields() { @@ -67,6 +69,7 @@ public sealed class S7DriverPageFormSerializationTests back.ProbeTimeoutSeconds.ShouldBe(12); } + /// Verifies that the S7 form model round-trip preserves all editable fields including tags. [Fact] public void FormModel_RoundTrip_PreservesEditableFields() { @@ -122,6 +125,7 @@ public sealed class S7DriverPageFormSerializationTests roundTripped.Tags[1].Writable.ShouldBeFalse(); } + /// Verifies that an S7 tag row round-trip preserves all editable fields. [Fact] public void S7TagRow_RoundTrip_PreservesEditableFields() { @@ -137,6 +141,7 @@ public sealed class S7DriverPageFormSerializationTests back.StringLength.ShouldBe(80); } + /// Verifies that unedited fields are carried through after an S7 tag row edit. [Fact] public void S7TagRow_CarriesThroughUneditedFields() { @@ -154,6 +159,7 @@ public sealed class S7DriverPageFormSerializationTests back.WriteIdempotent.ShouldBeTrue(); } + /// Verifies that S7 tag row validation rejects duplicate tag names. [Fact] public void S7TagRow_ValidateRow_RejectsDuplicateNames() { @@ -180,6 +186,7 @@ public sealed class S7DriverPageFormSerializationTests S7DriverPage.S7TagRow.ValidateRow(ok, all, editIndex: 1).ShouldBeNull(); } + /// Verifies that the tag list serialize round-trip preserves all tag definitions. [Fact] public void TagList_SerializeRoundTrip_PreservesTags() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/TwinCATDriverPageFormSerializationTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/TwinCATDriverPageFormSerializationTests.cs index 67f41ca3..d9083112 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/TwinCATDriverPageFormSerializationTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/TwinCATDriverPageFormSerializationTests.cs @@ -14,6 +14,7 @@ public sealed class TwinCATDriverPageFormSerializationTests WriteIndented = false, }; + /// Verifies that serializing and deserializing TwinCAT driver options preserves all known fields. [Fact] public void RoundTrip_PreservesKnownFields() { @@ -51,6 +52,7 @@ public sealed class TwinCATDriverPageFormSerializationTests back.Tags.ShouldBeEmpty(); } + /// Verifies that deserializing JSON with unknown fields silently drops the unrecognized members. [Fact] public void Deserialize_DropsUnknownFields() { @@ -64,6 +66,7 @@ public sealed class TwinCATDriverPageFormSerializationTests back.ProbeTimeoutSeconds.ShouldBe(25); } + /// Verifies that the form model round-trip preserves all editable fields. [Fact] public void FormModel_RoundTrip_PreservesEditableFields() { @@ -96,6 +99,7 @@ public sealed class TwinCATDriverPageFormSerializationTests roundTripped.ProbeTimeoutSeconds.ShouldBe(15); } + /// Verifies that a device row round-trip preserves all editable fields. [Fact] public void DeviceRow_RoundTrip_PreservesEditableFields() { @@ -109,6 +113,7 @@ public sealed class TwinCATDriverPageFormSerializationTests back.DeviceName.ShouldBe("PLC1"); } + /// Verifies that unedited source fields are carried through after a device row edit. [Fact] public void DeviceRow_CarriesThroughUneditedSourceFields() { @@ -124,6 +129,7 @@ public sealed class TwinCATDriverPageFormSerializationTests back.DeviceName.ShouldBe("Renamed"); } + /// Verifies that device row validation rejects duplicate host addresses. [Fact] public void DeviceRow_ValidateRow_RejectsDuplicateHostAddress() { @@ -140,6 +146,7 @@ public sealed class TwinCATDriverPageFormSerializationTests error.ShouldContain("Duplicate"); } + /// Verifies that a tag row round-trip preserves all editable fields. [Fact] public void TagRow_RoundTrip_PreservesEditableFields() { @@ -156,6 +163,7 @@ public sealed class TwinCATDriverPageFormSerializationTests back.Writable.ShouldBeFalse(); } + /// Verifies that the unedited WriteIdempotent field is carried through after a tag row edit. [Fact] public void TagRow_CarriesThroughUneditedWriteIdempotent() { @@ -172,6 +180,7 @@ public sealed class TwinCATDriverPageFormSerializationTests back.WriteIdempotent.ShouldBeTrue(); } + /// Verifies that tag row validation rejects duplicate tag names (case-insensitive). [Fact] public void TagRow_ValidateRow_RejectsDuplicateName() { @@ -189,6 +198,7 @@ public sealed class TwinCATDriverPageFormSerializationTests error.ShouldContain("Duplicate"); } + /// Verifies that ToOptions serializes device and tag lists correctly. [Fact] public void FormModel_ToOptions_SerializesDeviceAndTagLists() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/_PlaceholderTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/_PlaceholderTests.cs index 18e4a342..7734ff84 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/_PlaceholderTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/_PlaceholderTests.cs @@ -5,6 +5,7 @@ namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests; public sealed class _PlaceholderTests { + /// Verifies the project compiles and the test runner can discover test methods. [Fact] public void ProjectCompilesAndTestRunnerDiscoversIt() => 1.ShouldBe(1); } diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests/AuditWriterActorTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests/AuditWriterActorTests.cs index d7e3400c..ba7d4e6c 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests/AuditWriterActorTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests/AuditWriterActorTests.cs @@ -139,6 +139,7 @@ public sealed class AuditWriterActorTests : ControlPlaneActorTestBase /// synchronously, never throws, and routes the event onto the actor's own mailbox /// (Self.Tell) — i.e. the same buffer + dedup + flush pipeline asserted by the Tell /// tests above. Reaches the concrete instance via a TestActorRef. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteAsync_is_best_effort_and_routes_onto_the_actor_mailbox() { @@ -197,6 +198,8 @@ public sealed class AuditWriterActorTests : ControlPlaneActorTestBase /// Verifies the Outcome derivation table: config verbs → Success, the two /// authorization-rejection events → Denied. + /// The audit action string to derive the outcome from. + /// The expected audit outcome for the given action. [Theory] [InlineData("DraftCreated", AuditOutcome.Success)] [InlineData("DraftEdited", AuditOutcome.Success)] diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests/ConfigComposerTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests/ConfigComposerTests.cs index fcd73702..d61b5b40 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests/ConfigComposerTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests/ConfigComposerTests.cs @@ -10,6 +10,7 @@ namespace ZB.MOM.WW.OtOpcUa.ControlPlane.Tests; public sealed class ConfigComposerTests : ControlPlaneActorTestBase { /// Verifies that an empty database produces a stable, reproducible hash. + /// A task that represents the asynchronous operation. [Fact] public async Task Empty_database_produces_stable_hash() { @@ -26,6 +27,7 @@ public sealed class ConfigComposerTests : ControlPlaneActorTestBase } /// Verifies that insertion order does not affect the configuration hash. + /// A task that represents the asynchronous operation. [Fact] public async Task Same_rows_in_different_insert_orders_produce_same_hash() { @@ -54,6 +56,7 @@ public sealed class ConfigComposerTests : ControlPlaneActorTestBase } /// Verifies that different database configurations produce different hashes. + /// A task that represents the asynchronous operation. [Fact] public async Task Different_data_produces_different_hash() { @@ -76,6 +79,7 @@ public sealed class ConfigComposerTests : ControlPlaneActorTestBase } /// Verifies that the revision hash is a 64-character lowercase hexadecimal string. + /// A task that represents the asynchronous operation. [Fact] public async Task Hash_is_64_lowercase_hex_chars() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/ClusterFormationTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/ClusterFormationTests.cs index 7e94a0c6..a77488c2 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/ClusterFormationTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/ClusterFormationTests.cs @@ -12,6 +12,7 @@ namespace ZB.MOM.WW.OtOpcUa.Host.IntegrationTests; public sealed class ClusterFormationTests { /// Verifies that two nodes form a 2-member cluster. + /// A task that represents the asynchronous test operation. [Fact] public async Task Two_nodes_form_a_2_member_cluster() { @@ -29,6 +30,7 @@ public sealed class ClusterFormationTests } /// Verifies that both nodes see each other as role members. + /// A task that represents the asynchronous test operation. [Fact] public async Task Both_nodes_see_each_other_as_role_members() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DeployHappyPathTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DeployHappyPathTests.cs index b2381397..769e9777 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DeployHappyPathTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DeployHappyPathTests.cs @@ -19,6 +19,7 @@ public sealed class DeployHappyPathTests private static CancellationToken Ct => TestContext.Current.CancellationToken; /// Verifies that StartDeployment seals after both nodes apply. + /// A task that represents the asynchronous operation. [Fact] public async Task StartDeployment_seals_after_both_nodes_apply() { @@ -55,6 +56,7 @@ public sealed class DeployHappyPathTests } /// Verifies that replaying dispatch to same revision is idempotent and a no-op. + /// A task that represents the asynchronous operation. [Fact] public async Task Replaying_dispatch_to_same_revision_is_idempotent_no_op() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DockerFixtureAvailability.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DockerFixtureAvailability.cs index 5188ee25..ee932a3e 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DockerFixtureAvailability.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DockerFixtureAvailability.cs @@ -17,6 +17,7 @@ public static class DockerFixtureAvailability /// The host to probe. /// The TCP port to connect to. /// Maximum time to wait in milliseconds; defaults to 500. + /// if the TCP connection succeeded within the timeout; otherwise . public static bool IsReachable(string host, int port, int timeoutMs = 500) { try @@ -43,6 +44,7 @@ public static class DockerFixtureAvailability /// /// Endpoint in host:port format. /// Maximum time to wait in milliseconds; defaults to 500. + /// if the TCP connection succeeded within the timeout; otherwise . public static bool IsReachable(string endpoint, int timeoutMs = 500) { try diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DriverProbeRegistrationTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DriverProbeRegistrationTests.cs index 67286577..1edb2eda 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DriverProbeRegistrationTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DriverProbeRegistrationTests.cs @@ -32,6 +32,7 @@ public sealed class DriverProbeRegistrationTests "Historian.Wonderware", ]; + /// Verifies that AddOtOpcUaDriverProbes registers a probe for every AdminUI driver type. [Fact] public void AddOtOpcUaDriverProbes_registers_a_probe_for_every_AdminUI_driver_type() { @@ -49,6 +50,7 @@ public sealed class DriverProbeRegistrationTests byType.ContainsKey(key).ShouldBeTrue($"No IDriverProbe registered for AdminUI driver type '{key}'."); } + /// Verifies that AddOtOpcUaDriverProbes is idempotent when called multiple times. [Fact] public void AddOtOpcUaDriverProbes_is_idempotent() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DriverReconnectE2eTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DriverReconnectE2eTests.cs index 002bd4c2..acb44e00 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DriverReconnectE2eTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DriverReconnectE2eTests.cs @@ -37,6 +37,7 @@ public sealed class DriverReconnectE2eTests /// to a deployed driver, so no DriverInstanceActor will act on the DPS /// broadcast — the test is validating the command ingestion and reply path only. /// + /// A task that represents the asynchronous test operation. [Fact] public async Task Reconnect_RoundTrip_ReturnsOk() { @@ -63,6 +64,7 @@ public sealed class DriverReconnectE2eTests /// is also accepted (idempotent at the actor layer — the actor simply re-broadcasts /// to DPS and writes another ConfigEdit row). /// + /// A task that represents the asynchronous test operation. [Fact] public async Task Reconnect_IsIdempotent_SecondCallAlsoReturnsOk() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DriverStatusHubE2eTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DriverStatusHubE2eTests.cs index 6d70c31b..d3576ec2 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DriverStatusHubE2eTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DriverStatusHubE2eTests.cs @@ -39,6 +39,7 @@ public sealed class DriverStatusHubE2eTests /// to both the (via Upsert) and the /// mock (via SendAsync). /// + /// A task that represents the asynchronous operation. [Fact] public async Task StatusHub_BridgeActor_ForwardsHealthChanged_ToStoreAndHub() { @@ -106,6 +107,7 @@ public sealed class DriverStatusHubE2eTests /// for the same instance ID results in the store holding only the most recent state /// (last-write-wins) and both hub push calls being made. /// + /// A task that represents the asynchronous operation. [Fact] public async Task StatusHub_BridgeActor_LastSnapshotWins_InStore() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DriverTestConnectE2eTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DriverTestConnectE2eTests.cs index 528a5980..b14188cb 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DriverTestConnectE2eTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/DriverTestConnectE2eTests.cs @@ -45,6 +45,7 @@ public sealed class DriverTestConnectE2eTests /// the reports Ok = true with a /// sub-5 s latency. Skipped when the Docker fixture host is unreachable. /// + /// A task that represents the asynchronous operation. [Fact] public async Task TestConnect_Modbus_AgainstFixture_ReportsOk() { @@ -74,6 +75,7 @@ public sealed class DriverTestConnectE2eTests /// containing a connection-refused indicator. Skipped when the host is unreachable /// (even a refused connection requires the IP to be routable). /// + /// A task that represents the asynchronous operation. [Fact] public async Task TestConnect_Modbus_AgainstWrongPort_ReportsFailure() { @@ -118,6 +120,7 @@ public sealed class DriverTestConnectE2eTests /// (which skips in dev). /// This test does NOT require any Docker fixture and always runs. /// + /// A task that represents the asynchronous operation. [Fact] public async Task TestConnect_Modbus_AgainstBlackHole_ReportsTimeout() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/FailoverDuringDeployTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/FailoverDuringDeployTests.cs index 29d43db7..7e65029d 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/FailoverDuringDeployTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/FailoverDuringDeployTests.cs @@ -19,6 +19,7 @@ public sealed class FailoverDuringDeployTests private static CancellationToken Ct => TestContext.Current.CancellationToken; /// Verifies that stopping node B shrinks the cluster to one up member. + /// A task that represents the asynchronous operation. [Fact] public async Task Stopping_node_b_shrinks_cluster_to_one_up_member() { @@ -34,6 +35,7 @@ public sealed class FailoverDuringDeployTests } /// Verifies that a restarted node B rejoins the cluster on the same port. + /// A task that represents the asynchronous operation. [Fact] public async Task Restarted_node_b_rejoins_cluster_on_same_port() { @@ -51,6 +53,7 @@ public sealed class FailoverDuringDeployTests } /// Verifies that a deployment started with node B down seals with one-node state. + /// A task that represents the asynchronous operation. [Fact] public async Task Deployment_started_with_node_b_down_seals_with_one_node_state() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/FleetDiagnosticsRoundTripTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/FleetDiagnosticsRoundTripTests.cs index d0ec16f9..0fbedf49 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/FleetDiagnosticsRoundTripTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/FleetDiagnosticsRoundTripTests.cs @@ -17,6 +17,7 @@ public sealed class FleetDiagnosticsRoundTripTests private static CancellationToken Ct => TestContext.Current.CancellationToken; /// Verifies that get diagnostics returns a snapshot with the target node ID. + /// A task that represents the asynchronous operation. [Fact] public async Task GetDiagnostics_returns_snapshot_with_target_NodeId() { @@ -41,6 +42,7 @@ public sealed class FleetDiagnosticsRoundTripTests } /// Verifies that get diagnostics after deploy reports the current revision. + /// A task that represents the asynchronous operation. [Fact] public async Task GetDiagnostics_after_deploy_reports_current_revision() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/LdapOpcUaUserAuthenticatorTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/LdapOpcUaUserAuthenticatorTests.cs index 8f859751..34b75a62 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/LdapOpcUaUserAuthenticatorTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/LdapOpcUaUserAuthenticatorTests.cs @@ -19,6 +19,7 @@ public sealed class LdapOpcUaUserAuthenticatorTests { /// On success the data-plane authenticator resolves roles via the mapper from the /// returned Groups — not from the auth result's Roles field — and grants identity. + /// A task that represents the asynchronous test operation. [Fact] public async Task Authenticate_LDAP_success_resolves_roles_via_mapper_from_groups() { @@ -37,6 +38,7 @@ public sealed class LdapOpcUaUserAuthenticatorTests /// The DevStub pre-resolved roles (Administrator) survive the move to the mapper: they are /// unioned with the mapper output so the dev grant still reaches the OPC UA session. + /// A task that represents the asynchronous test operation. [Fact] public async Task Authenticate_devstub_preresolved_roles_are_unioned_with_mapper() { @@ -54,6 +56,7 @@ public sealed class LdapOpcUaUserAuthenticatorTests /// A mapper fault (e.g. DB outage) must not deny an authenticated session — it falls /// back to the pre-resolved roles, matching the login endpoint's behaviour. + /// A task that represents the asynchronous test operation. [Fact] public async Task Authenticate_mapper_fault_falls_back_to_preresolved_roles() { @@ -68,6 +71,7 @@ public sealed class LdapOpcUaUserAuthenticatorTests } /// Verifies that LDAP authentication failure returns Deny result with error text. + /// A task that represents the asynchronous test operation. [Fact] public async Task Authenticate_LDAP_failure_returns_Deny_with_error_text() { @@ -82,6 +86,7 @@ public sealed class LdapOpcUaUserAuthenticatorTests } /// Verifies that LDAP exceptions are converted to backend error denial results. + /// A task that represents the asynchronous test operation. [Fact] public async Task Authenticate_LDAP_exception_returns_backend_error_denial() { @@ -97,6 +102,7 @@ public sealed class LdapOpcUaUserAuthenticatorTests } /// Verifies that authentication falls back to username when LDAP omits display name. + /// A task that represents the asynchronous test operation. [Fact] public async Task Authenticate_falls_back_to_username_when_LDAP_omits_display_name() { @@ -131,10 +137,7 @@ public sealed class LdapOpcUaUserAuthenticatorTests /// The handler to invoke with the username to produce a result. public FakeLdap(Func handler) => _handler = handler; - /// Authenticates a user asynchronously via the handler function. - /// The username to authenticate. - /// The password (ignored by the fake). - /// Cancellation token for the operation. + /// public Task AuthenticateAsync(string username, string password, CancellationToken ct = default) => Task.FromResult(_handler(username)); } @@ -145,6 +148,7 @@ public sealed class LdapOpcUaUserAuthenticatorTests /// Maps groups to roles via the configured delegate; Scope is always null. /// The LDAP groups to map. /// The cancellation token. + /// A task that resolves to the group role mapping result. public Task> MapAsync(IReadOnlyList groups, CancellationToken ct) => Task.FromResult(new GroupRoleMapping(map(groups), Scope: null)); } diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/LdapOptionsBindingTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/LdapOptionsBindingTests.cs index c18b8d1e..ff91e82f 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/LdapOptionsBindingTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/LdapOptionsBindingTests.cs @@ -88,6 +88,8 @@ public sealed class ProdOverlayValidationTests return configuration.GetSection(LdapOptions.SectionName).Get() ?? new LdapOptions(); } + /// Verifies that each production overlay declares the ldaps transport. + /// The overlay file name to validate. [Theory] [InlineData("appsettings.admin.json")] [InlineData("appsettings.driver.json")] @@ -100,6 +102,8 @@ public sealed class ProdOverlayValidationTests options.Transport.ShouldBe(LdapTransport.Ldaps); } + /// Verifies that each production overlay passes startup validation. + /// The overlay file name to validate. [Theory] [InlineData("appsettings.admin.json")] [InlineData("appsettings.driver.json")] @@ -114,6 +118,7 @@ public sealed class ProdOverlayValidationTests Sut.Validate(null, options).Succeeded.ShouldBeTrue(); } + /// Verifies that the Development overlay passes startup validation via the DevStub exemption. [Fact] public void Development_overlay_passes_startup_validation_via_devstub_exemption() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/TwoNodeClusterHarness.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/TwoNodeClusterHarness.cs index cd057db9..f3a533f8 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/TwoNodeClusterHarness.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/TwoNodeClusterHarness.cs @@ -77,6 +77,7 @@ public sealed class TwoNodeClusterHarness : IAsyncDisposable /// Boots both nodes and waits up to for cluster convergence. /// Maximum time to wait for cluster formation; defaults to 20 seconds if not provided. + /// A task that resolves to the started two-node cluster harness. public static async Task StartAsync(TimeSpan? formationTimeout = null) { var harness = new TwoNodeClusterHarness(); @@ -108,6 +109,7 @@ public sealed class TwoNodeClusterHarness : IAsyncDisposable /// a couple of seconds. Use this for failover scenarios; call /// to bring it back on the same Akka port. /// + /// A task that represents the asynchronous stop operation. public async Task StopNodeBAsync() { if (NodeB is null) return; @@ -120,6 +122,7 @@ public sealed class TwoNodeClusterHarness : IAsyncDisposable /// to re-converge to 2 Up members. Use after to test rejoin. /// /// The maximum time to wait for cluster formation; defaults to 20 seconds. + /// A task that represents the asynchronous restart operation. public async Task RestartNodeBAsync(TimeSpan? formationTimeout = null) { NodeB = await BuildNodeAsync(this, NodeRole.Joiner); @@ -136,6 +139,7 @@ public sealed class TwoNodeClusterHarness : IAsyncDisposable /// /// The expected number of Up members in the cluster. /// The maximum time to wait for the expected cluster size. + /// A task that represents the asynchronous wait operation. public async Task WaitForClusterSizeAsync(int expectedUpMembers, TimeSpan timeout) { var deadline = DateTime.UtcNow + timeout; @@ -280,6 +284,7 @@ public sealed class TwoNodeClusterHarness : IAsyncDisposable } /// Asynchronously disposes both nodes and cleans up the SQL database if used. + /// A value task that represents the asynchronous dispose operation. public async ValueTask DisposeAsync() { if (NodeB is not null) await NodeB.DisposeAsync(); @@ -303,11 +308,7 @@ public sealed class TwoNodeClusterHarness : IAsyncDisposable private sealed class StubLdapAuthService : ILdapAuthService { - /// Asynchronously authenticates a user with the stub LDAP service. - /// The username to authenticate. - /// The password to authenticate against. - /// The cancellation token. - /// A task that returns the LDAP authentication result. + /// public Task AuthenticateAsync(string username, string password, CancellationToken ct = default) => Task.FromResult(new LdapAuthResult( Success: password == "valid-password", diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/DualEndpointTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/DualEndpointTests.cs index 5b529eb0..745d29dd 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/DualEndpointTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests/DualEndpointTests.cs @@ -24,6 +24,7 @@ public sealed class DualEndpointTests private const string NodeBUri = "urn:OtOpcUa.DualEndpoint.NodeB"; /// Verifies that a client can read the ServerArray containing both redundant node URIs. + /// A task that represents the asynchronous test operation. [Fact] public async Task Client_reads_both_ApplicationUris_from_NodeA_ServerArray() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/OpcUaApplicationHostImpersonationTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/OpcUaApplicationHostImpersonationTests.cs index f44bf71d..ef07f637 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/OpcUaApplicationHostImpersonationTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/OpcUaApplicationHostImpersonationTests.cs @@ -92,6 +92,7 @@ public sealed class OpcUaApplicationHostImpersonationTests } /// Verifies NullOpcUaUserAuthenticator always returns denial result. + /// A task that represents the asynchronous operation. [Fact] public async Task NullOpcUaUserAuthenticator_always_denies() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/OpcUaApplicationHostSecurityTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/OpcUaApplicationHostSecurityTests.cs index 8a8bc293..34ce8025 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/OpcUaApplicationHostSecurityTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/OpcUaApplicationHostSecurityTests.cs @@ -91,6 +91,7 @@ public sealed class OpcUaApplicationHostSecurityTests : IDisposable /// /// Verifies that StartAsync populates ServerConfiguration with all enabled security profiles. /// + /// A task that represents the asynchronous operation. [Fact] public async Task StartAsync_populates_ServerConfiguration_with_all_enabled_profiles() { @@ -129,6 +130,7 @@ public sealed class OpcUaApplicationHostSecurityTests : IDisposable /// /// Verifies that StartAsync with only SignAndEncrypt omits the None endpoint. /// + /// A task that represents the asynchronous operation. [Fact] public async Task StartAsync_with_only_signandencrypt_omits_None_endpoint() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/OpcUaApplicationHostServerArrayTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/OpcUaApplicationHostServerArrayTests.cs index 6ca2b7e3..fcc0b1b5 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/OpcUaApplicationHostServerArrayTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/OpcUaApplicationHostServerArrayTests.cs @@ -20,6 +20,7 @@ public sealed class OpcUaApplicationHostServerArrayTests /// /// Verifies that ServerArray contains local URI and configured peer URIs after start. /// + /// A task that represents the asynchronous test. [Fact] public async Task ServerArray_contains_local_uri_and_configured_peers_after_start() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/OpcUaApplicationHostTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/OpcUaApplicationHostTests.cs index ec7beceb..160fc3b7 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/OpcUaApplicationHostTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/OpcUaApplicationHostTests.cs @@ -19,6 +19,7 @@ public sealed class OpcUaApplicationHostTests : IDisposable $"otopcua-pki-{Guid.NewGuid():N}"); /// Verifies StartAsync creates a self-signed certificate in the PKI own store. + /// A task that represents the asynchronous operation. [Fact] public async Task StartAsync_creates_application_certificate_in_pki_own() { @@ -41,6 +42,7 @@ public sealed class OpcUaApplicationHostTests : IDisposable } /// Verifies StartAsync reuses an existing certificate on the second boot. + /// A task that represents the asynchronous operation. [Fact] public async Task StartAsync_reuses_existing_certificate_on_second_boot() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/Phase7ApplierHierarchyTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/Phase7ApplierHierarchyTests.cs index befdcdd7..dd244122 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/Phase7ApplierHierarchyTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/Phase7ApplierHierarchyTests.cs @@ -67,6 +67,7 @@ public sealed class Phase7ApplierHierarchyTests : IDisposable } /// Verifies that MaterialiseHierarchy creates folder nodes in a real SDK node manager. + /// A task that represents the asynchronous test operation. [Fact] public async Task MaterialiseHierarchy_against_real_SDK_node_manager_creates_folder_nodes() { @@ -135,31 +136,16 @@ public sealed class Phase7ApplierHierarchyTests : IDisposable /// Gets the list of EnsureFolder calls recorded by this sink. public List<(string NodeId, string? Parent, string DisplayName)> Calls => _calls.ToList(); - /// Records a value write (stub implementation for testing). - /// The node ID of the variable. - /// The value to write. - /// The OPC UA quality value. - /// The source timestamp in UTC. + /// public void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc) { } - /// Records an alarm state write (stub implementation for testing). - /// The node ID of the alarm condition. - /// Whether the alarm is active. - /// Whether the alarm has been acknowledged. - /// The source timestamp in UTC. + /// public void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime sourceTimestampUtc) { } - /// Records a folder creation request. - /// The node ID of the folder. - /// The node ID of the parent folder, or null for root. - /// The display name of the folder. + /// public void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName) => _calls.Enqueue((folderNodeId, parentNodeId, displayName)); - /// Ensures a variable exists (stub implementation for testing). - /// The node ID of the variable. - /// The node ID of the parent folder, or null for root. - /// The display name of the variable. - /// The OPC UA built-in type name. + /// public void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType) { } - /// Rebuilds the address space (stub implementation for testing). + /// public void RebuildAddressSpace() { } } } diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/Phase7ApplierTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/Phase7ApplierTests.cs index 7cbae453..8da59849 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/Phase7ApplierTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/Phase7ApplierTests.cs @@ -246,33 +246,18 @@ public sealed class Phase7ApplierTests /// Gets the list of recorded variable creation calls. public List<(string NodeId, string? Parent, string DisplayName, string DataType)> VariableCalls => VariableQueue.ToList(); - /// Records a value write (no-op in this recording sink). - /// The node ID. - /// The value to write. - /// The OPC UA quality. - /// The source timestamp in UTC. + /// public void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc) { } - /// Records an alarm state write call. - /// The alarm node ID. - /// Whether the alarm is active. - /// Whether the alarm is acknowledged. - /// The source timestamp in UTC. + /// public void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime sourceTimestampUtc) => AlarmQueue.Enqueue((alarmNodeId, active, acknowledged)); - /// Records a folder creation call. - /// The folder node ID. - /// The parent folder node ID, if any. - /// The display name for the folder. + /// public void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName) => FolderQueue.Enqueue((folderNodeId, parentNodeId, displayName)); - /// Records a variable creation call. - /// The variable node ID. - /// The parent folder node ID, if any. - /// The display name for the variable. - /// The OPC UA built-in type name. + /// public void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType) => VariableQueue.Enqueue((variableNodeId, parentFolderNodeId, displayName, dataType)); - /// Records a rebuild address space call. + /// public void RebuildAddressSpace() => Interlocked.Increment(ref RebuildCalls); } @@ -283,34 +268,18 @@ public sealed class Phase7ApplierTests /// Whether to throw on alarm state writes. public ThrowingSink(bool throwOnAlarmWrite) { _throwOnAlarmWrite = throwOnAlarmWrite; } - /// Records a value write (no-op in this sink). - /// The node ID. - /// The value to write. - /// The OPC UA quality. - /// The source timestamp in UTC. + /// public void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc) { } - /// Throws an exception if configured to do so. - /// The alarm node ID. - /// Whether the alarm is active. - /// Whether the alarm is acknowledged. - /// The source timestamp in UTC. - /// Thrown when configured to throw on alarm write. + /// public void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime sourceTimestampUtc) { if (_throwOnAlarmWrite) throw new InvalidOperationException("simulated sink fault"); } - /// No-op folder creation call. - /// The folder node ID. - /// The parent folder node ID, if any. - /// The display name for the folder. + /// public void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName) { } - /// No-op variable creation call. - /// The variable node ID. - /// The parent folder node ID, if any. - /// The display name for the variable. - /// The OPC UA built-in type name. + /// public void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType) { } - /// No-op rebuild address space call. + /// public void RebuildAddressSpace() { } } } diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/SdkAddressSpaceSinkTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/SdkAddressSpaceSinkTests.cs index 18c37d0b..901340ad 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/SdkAddressSpaceSinkTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/SdkAddressSpaceSinkTests.cs @@ -20,6 +20,7 @@ public sealed class SdkAddressSpaceSinkTests : IDisposable $"otopcua-sink-{Guid.NewGuid():N}"); /// Verifies that WriteValue creates and updates variables in the OPC UA node manager. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteValue_creates_and_updates_variable_in_node_manager() { @@ -36,6 +37,7 @@ public sealed class SdkAddressSpaceSinkTests : IDisposable } /// Verifies that WriteAlarmState creates a dedicated node distinct from value writes. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteAlarmState_creates_dedicated_node_distinct_from_value_writes() { @@ -51,6 +53,7 @@ public sealed class SdkAddressSpaceSinkTests : IDisposable } /// Verifies that RebuildAddressSpace clears all registered variables. + /// A task that represents the asynchronous operation. [Fact] public async Task RebuildAddressSpace_clears_all_registered_variables() { @@ -73,6 +76,7 @@ public sealed class SdkAddressSpaceSinkTests : IDisposable } /// Verifies that NullOpcUaAddressSpaceSink does not crash on any call. + /// A task that represents the asynchronous operation. [Fact] public async Task NullOpcUaAddressSpaceSink_does_not_crash_on_any_call() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/SdkServiceLevelPublisherTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/SdkServiceLevelPublisherTests.cs index e8cae938..d73f8aca 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/SdkServiceLevelPublisherTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/SdkServiceLevelPublisherTests.cs @@ -20,6 +20,7 @@ public sealed class SdkServiceLevelPublisherTests : IDisposable $"otopcua-pki-{Guid.NewGuid():N}"); /// Verifies that the publisher writes values to the standard Server.ServiceLevel variable. + /// A task that represents the asynchronous test operation. [Fact] public async Task Publish_writes_value_to_Server_ServiceLevel_variable() { @@ -49,6 +50,7 @@ public sealed class SdkServiceLevelPublisherTests : IDisposable } /// Verifies that publishing service level values is idempotent when called multiple times. + /// A task that represents the asynchronous test operation. [Fact] public async Task Publish_is_idempotent_when_called_multiple_times() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorReconcileTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorReconcileTests.cs index 0e89a2fb..933c5dba 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorReconcileTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorReconcileTests.cs @@ -175,10 +175,7 @@ public sealed class DriverHostActorReconcileTests : RuntimeActorTestBase /// The driver type this factory supports. public CountingDriverFactory(string supportedType) { _supportedType = supportedType; } - /// Attempts to create a driver if the type is supported. - /// The driver type to create. - /// The unique identifier for the driver instance. - /// The driver configuration in JSON format. + /// public IDriver? TryCreate(string driverType, string driverInstanceId, string driverConfigJson) { if (!string.Equals(driverType, _supportedType, StringComparison.Ordinal)) return null; diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorTests.cs index a62af1ba..5636ce4b 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorTests.cs @@ -12,6 +12,7 @@ namespace ZB.MOM.WW.OtOpcUa.Runtime.Tests.Drivers; public sealed class DriverInstanceActorTests : RuntimeActorTestBase { /// Verifies that ApplyDelta calls ReinitializeAsync when connected and replies success. + /// A task that represents the asynchronous operation. [Fact] public async Task ApplyDelta_when_Connected_calls_ReinitializeAsync_and_replies_success() { @@ -48,6 +49,7 @@ public sealed class DriverInstanceActorTests : RuntimeActorTestBase } /// Verifies that writing to a non-IWritable driver returns failure. + /// A task that represents the asynchronous operation. [Fact] public async Task Write_against_non_IWritable_driver_returns_failure() { @@ -66,6 +68,7 @@ public sealed class DriverInstanceActorTests : RuntimeActorTestBase } /// Verifies that writing to an IWritable driver returns success when status is Good. + /// A task that represents the asynchronous operation. [Fact] public async Task Write_against_IWritable_returns_success_when_status_is_Good() { @@ -85,6 +88,7 @@ public sealed class DriverInstanceActorTests : RuntimeActorTestBase } /// Verifies that write propagates status code on Bad result. + /// A task that represents the asynchronous operation. [Fact] public async Task Write_propagates_status_code_on_Bad_result() { @@ -104,6 +108,7 @@ public sealed class DriverInstanceActorTests : RuntimeActorTestBase } /// Verifies that subscribing to an ISubscribable driver forwards OnDataChange to parent. + /// A task that represents the asynchronous operation. [Fact] public async Task Subscribe_against_ISubscribable_forwards_OnDataChange_to_parent() { @@ -129,6 +134,7 @@ public sealed class DriverInstanceActorTests : RuntimeActorTestBase } /// Verifies that subscribe translates OPC UA status severity bits to OpcUaQuality. + /// A task that represents the asynchronous operation. [Fact] public async Task Subscribe_translates_OPC_UA_status_severity_bits_to_OpcUaQuality() { @@ -153,6 +159,7 @@ public sealed class DriverInstanceActorTests : RuntimeActorTestBase } /// Verifies that subscribing to a non-ISubscribable driver replies with failure. + /// A task that represents the asynchronous operation. [Fact] public async Task Subscribe_against_non_ISubscribable_replies_with_failure() { @@ -170,6 +177,7 @@ public sealed class DriverInstanceActorTests : RuntimeActorTestBase } /// Verifies that DisconnectObserved detaches subscription handler so late events are dropped. + /// A task that represents the asynchronous operation. [Fact] public async Task DisconnectObserved_detaches_subscription_handler_so_late_events_are_dropped() { @@ -201,14 +209,12 @@ public sealed class DriverInstanceActorTests : RuntimeActorTestBase /// Gets the number of times reinitialization was called. public int ReinitializeCount; - /// Gets the driver instance ID. + /// public string DriverInstanceId => "stub-driver-1"; - /// Gets the driver type. + /// public string DriverType => "Stub"; - /// Initializes the driver with the specified configuration JSON. - /// The driver configuration JSON. - /// Cancellation token for the operation. + /// public Task InitializeAsync(string driverConfigJson, CancellationToken cancellationToken) { Interlocked.Increment(ref InitializeCount); @@ -216,24 +222,20 @@ public sealed class DriverInstanceActorTests : RuntimeActorTestBase return Task.CompletedTask; } - /// Reinitializes the driver with the specified configuration JSON. - /// The driver configuration JSON. - /// Cancellation token for the operation. + /// public Task ReinitializeAsync(string driverConfigJson, CancellationToken cancellationToken) { Interlocked.Increment(ref ReinitializeCount); return Task.CompletedTask; } - /// Shuts down the driver. - /// Cancellation token for the operation. + /// public Task ShutdownAsync(CancellationToken cancellationToken) => Task.CompletedTask; - /// Gets the health status of the driver. + /// public DriverHealth GetHealth() => new(DriverState.Healthy, DateTime.UtcNow, null); - /// Gets the memory footprint of the driver. + /// public long GetMemoryFootprint() => 0; - /// Flushes optional caches in the driver. - /// Cancellation token for the operation. + /// public Task FlushOptionalCachesAsync(CancellationToken cancellationToken) => Task.CompletedTask; } @@ -244,9 +246,7 @@ public sealed class DriverInstanceActorTests : RuntimeActorTestBase /// Gets the list of write requests received. public List Writes { get; } = new(); - /// Writes the specified requests. - /// The write requests. - /// Cancellation token for the operation. + /// public Task> WriteAsync( IReadOnlyList writes, CancellationToken cancellationToken) { @@ -266,17 +266,12 @@ public sealed class DriverInstanceActorTests : RuntimeActorTestBase /// Gets the number of subscribers to OnDataChange. public int OnDataChangeSubscriberCount => OnDataChange?.GetInvocationList().Length ?? 0; - /// Subscribes to the specified full references. - /// The full references to subscribe to. - /// The publishing interval. - /// Cancellation token for the operation. + /// public Task SubscribeAsync( IReadOnlyList fullReferences, TimeSpan publishingInterval, CancellationToken cancellationToken) => Task.FromResult(_handle); - /// Unsubscribes from the specified subscription handle. - /// The subscription handle. - /// Cancellation token for the operation. + /// public Task UnsubscribeAsync(ISubscriptionHandle handle, CancellationToken cancellationToken) => Task.CompletedTask; @@ -292,7 +287,7 @@ public sealed class DriverInstanceActorTests : RuntimeActorTestBase private sealed class StubHandle : ISubscriptionHandle { - /// Gets the diagnostic ID of the subscription. + /// public string DiagnosticId => "stub-sub"; } } diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Health/HealthProbeActorTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Health/HealthProbeActorTests.cs index f07caa95..4c343e2c 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Health/HealthProbeActorTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Health/HealthProbeActorTests.cs @@ -16,6 +16,7 @@ namespace ZB.MOM.WW.OtOpcUa.Runtime.Tests.Health; public sealed class HealthProbeActorTests : RuntimeActorTestBase { /// Verifies that the DB health probe actor returns reachable status against an in-memory database. + /// A task that represents the asynchronous test operation. [Fact] public async Task DbHealthProbeActor_returns_reachable_against_in_memory_db() { @@ -92,6 +93,7 @@ public sealed class HealthProbeActorTests : RuntimeActorTestBase } /// Verifies that the historian adapter actor returns sink status via GetStatus. + /// A task that represents the asynchronous test operation. [Fact] public async Task HistorianAdapterActor_returns_sink_status_via_GetStatus() { @@ -115,16 +117,14 @@ public sealed class HealthProbeActorTests : RuntimeActorTestBase /// Gets the list of enqueued alarm historian events. public ConcurrentBag Enqueued { get; } = []; - /// Enqueues an alarm historian event. - /// The event to enqueue. - /// The cancellation token. + /// public Task EnqueueAsync(AlarmHistorianEvent evt, CancellationToken cancellationToken) { Enqueued.Add(evt); return Task.CompletedTask; } - /// Gets the current status of the historian sink. + /// public HistorianSinkStatus GetStatus() => new( QueueDepth: Enqueued.Count, DeadLetterDepth: 0, diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Observability/OtOpcUaTelemetryHookTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Observability/OtOpcUaTelemetryHookTests.cs index 62e08ee6..f623322c 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Observability/OtOpcUaTelemetryHookTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Observability/OtOpcUaTelemetryHookTests.cs @@ -128,6 +128,7 @@ public sealed class OtOpcUaTelemetryHookTests : RuntimeActorTestBase /// Gets count of measurements with the specified tag key-value pair. /// Tag key. /// Tag value. + /// The count of matching measurements. public int WithTag(string key, string value) { lock (_gate) @@ -177,10 +178,7 @@ public sealed class OtOpcUaTelemetryHookTests : RuntimeActorTestBase private sealed class ConstEval(object? value) : IVirtualTagEvaluator { - /// Evaluates the virtual tag with a constant value. - /// Virtual tag ID. - /// Expression to evaluate. - /// Dependency values. + /// public VirtualTagEvalResult Evaluate(string virtualTagId, string expression, IReadOnlyDictionary dependencies) => VirtualTagEvalResult.Ok(value); } @@ -189,30 +187,15 @@ public sealed class OtOpcUaTelemetryHookTests : RuntimeActorTestBase { /// Gets the write count. public int Writes { get; private set; } - /// Records a value write. - /// The OPC UA node identifier. - /// The value being written. - /// The OPC UA quality status. - /// The source timestamp in UTC. + /// public void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc) => Writes++; - /// Records an alarm state write. - /// The alarm node identifier. - /// Whether the alarm is active. - /// Whether the alarm is acknowledged. - /// The time the alarm occurred in UTC. + /// public void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime occurredUtc) => Writes++; - /// Ensures folder exists (stub implementation). - /// The folder node identifier. - /// The parent folder node identifier. - /// The display name for the folder. + /// public void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName) { } - /// Ensures variable exists (stub implementation). - /// The variable node identifier. - /// The parent folder node identifier. - /// The display name for the variable. - /// The OPC UA built-in type name. + /// public void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType) { } - /// Rebuilds address space (recorded via span). + /// public void RebuildAddressSpace() { /* recorded via span */ } } } diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/OpcUa/OpcUaPublishActorTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/OpcUa/OpcUaPublishActorTests.cs index f89033a2..060097e2 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/OpcUa/OpcUaPublishActorTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/OpcUa/OpcUaPublishActorTests.cs @@ -160,36 +160,21 @@ public sealed class OpcUaPublishActorTests : RuntimeActorTestBase public List<(string AlarmNodeId, bool Active, bool Acknowledged, DateTime Ts)> Alarms => AlarmQueue.ToList(); - /// Records a value update. - /// The OPC UA node identifier. - /// The attribute value. - /// The OPC UA quality code. - /// The timestamp of the update. + /// public void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime ts) => ValueQueue.Enqueue((nodeId, value, quality, ts)); - /// Records an alarm state update. - /// The OPC UA alarm node identifier. - /// Whether the alarm is active. - /// Whether the alarm is acknowledged. - /// The timestamp of the update. + /// public void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime ts) => AlarmQueue.Enqueue((alarmNodeId, active, acknowledged, ts)); - /// Ensures a folder exists (no-op in test). - /// The OPC UA folder node identifier. - /// The parent folder node identifier, or null for root. - /// The display name of the folder. + /// public void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName) { } - /// Ensures a variable exists (no-op in test). - /// The OPC UA variable node identifier. - /// The parent folder node identifier, or null for root. - /// The display name of the variable. - /// The OPC UA built-in type name. + /// public void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType) { } - /// Records a rebuild call. + /// public void RebuildAddressSpace() => Interlocked.Increment(ref RebuildCalls); } @@ -199,8 +184,7 @@ public sealed class OpcUaPublishActorTests : RuntimeActorTestBase private readonly ConcurrentQueue _q = new(); /// Gets the recorded service levels. public byte[] Levels => _q.ToArray(); - /// Records a service level publish. - /// The service level value to publish. + /// public void Publish(byte serviceLevel) => _q.Enqueue(serviceLevel); } } diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/OpcUa/ServiceLevelEndToEndTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/OpcUa/ServiceLevelEndToEndTests.cs index 72b43ced..24d589c4 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/OpcUa/ServiceLevelEndToEndTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/OpcUa/ServiceLevelEndToEndTests.cs @@ -25,6 +25,7 @@ public sealed class ServiceLevelEndToEndTests : RuntimeActorTestBase private static CancellationToken Ct => CancellationToken.None; /// Verifies that the primary cluster leader sets Server ServiceLevel to 240. + /// A task that represents the asynchronous operation. [Fact] public async Task Primary_leader_drives_Server_ServiceLevel_to_240() { @@ -65,6 +66,7 @@ public sealed class ServiceLevelEndToEndTests : RuntimeActorTestBase } /// Verifies that the secondary node sets Server ServiceLevel to 100. + /// A task that represents the asynchronous operation. [Fact] public async Task Secondary_drives_Server_ServiceLevel_to_100() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/ScriptedAlarms/ScriptedAlarmStatePersistenceTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/ScriptedAlarms/ScriptedAlarmStatePersistenceTests.cs index a8c3c317..a3262a6a 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/ScriptedAlarms/ScriptedAlarmStatePersistenceTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/ScriptedAlarms/ScriptedAlarmStatePersistenceTests.cs @@ -13,6 +13,7 @@ namespace ZB.MOM.WW.OtOpcUa.Runtime.Tests.ScriptedAlarms; public sealed class ScriptedAlarmStatePersistenceTests : RuntimeActorTestBase { /// Verifies that alarm state transitions write to the state store with the correct lastAckUser value. + /// A task that represents the asynchronous operation. [Fact] public async Task Transition_writes_to_state_store_with_lastAckUser() { @@ -39,6 +40,7 @@ public sealed class ScriptedAlarmStatePersistenceTests : RuntimeActorTestBase } /// Verifies that actor restart restores persisted state so pending acknowledgment is not dropped. + /// A task that represents the asynchronous operation. [Fact] public async Task PreStart_restores_persisted_state_so_restart_does_not_drop_pending_ack() { @@ -64,6 +66,7 @@ public sealed class ScriptedAlarmStatePersistenceTests : RuntimeActorTestBase } /// Verifies that alarm boots to inactive state when no persisted state exists. + /// A task that represents the asynchronous operation. [Fact] public async Task PreStart_with_no_persisted_state_boots_inactive() { @@ -80,6 +83,7 @@ public sealed class ScriptedAlarmStatePersistenceTests : RuntimeActorTestBase } /// Verifies that EF-based alarm actor state store correctly persists and restores state through the config database. + /// A task that represents the asynchronous operation. [Fact] public async Task EfAlarmActorStateStore_round_trip_persists_via_ConfigDb() { @@ -118,6 +122,7 @@ public sealed class ScriptedAlarmStatePersistenceTests : RuntimeActorTestBase } /// Verifies that loading an alarm state for a missing ID returns null. + /// A task that represents the asynchronous operation. [Fact] public async Task EfAlarmActorStateStore_load_for_missing_id_returns_null() { @@ -136,17 +141,11 @@ public sealed class ScriptedAlarmStatePersistenceTests : RuntimeActorTestBase /// Gets all saved alarm state snapshots in order. public List Snapshots => _saves.ToList(); - /// Loads the alarm state snapshot for the specified alarm ID. - /// The alarm ID. - /// The cancellation token. - /// The alarm state snapshot if found, null otherwise. + /// public Task LoadAsync(string alarmId, CancellationToken ct) => Task.FromResult(_byId.TryGetValue(alarmId, out var v) ? v : null); - /// Saves the alarm state snapshot. - /// The alarm state snapshot to save. - /// The cancellation token. - /// A completed task. + /// public Task SaveAsync(AlarmActorStateSnapshot snapshot, CancellationToken ct) { _byId[snapshot.AlarmId] = snapshot; diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/ServiceCollectionExtensionsTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/ServiceCollectionExtensionsTests.cs index bdcd7798..01263355 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/ServiceCollectionExtensionsTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/ServiceCollectionExtensionsTests.cs @@ -18,6 +18,7 @@ namespace ZB.MOM.WW.OtOpcUa.Runtime.Tests; public sealed class ServiceCollectionExtensionsTests { /// Verifies that WithOtOpcUaRuntimeActors spawns driver host and DB health probe actors. + /// A task that represents the asynchronous operation. [Fact] public async Task WithOtOpcUaRuntimeActors_spawns_driver_host_and_db_health_probe() { @@ -72,6 +73,7 @@ public sealed class ServiceCollectionExtensionsTests private sealed class InMemoryConfigDbFactory(string dbName) : IDbContextFactory { /// Creates a new in-memory database context. + /// A new instance backed by an in-memory database. public OtOpcUaConfigDbContext CreateDbContext() { var opts = new DbContextOptionsBuilder() @@ -84,22 +86,19 @@ public sealed class ServiceCollectionExtensionsTests /// Fake cluster role information for testing. private sealed class FakeClusterRoleInfo : IClusterRoleInfo { - /// Gets the local node ID. + /// public NodeId LocalNode { get; } = NodeId.Parse("test-node"); - /// Gets the local roles. + /// public IReadOnlySet LocalRoles { get; } = new HashSet(["driver"]); - /// Determines whether the local node has the specified role. - /// The role to check. + /// public bool HasRole(string role) => LocalRoles.Contains(role); - /// Gets the members with the specified role. - /// The role to query. + /// public IReadOnlyList MembersWithRole(string role) => Array.Empty(); - /// Gets the leader node for the specified role. - /// The role to query. + /// public NodeId? RoleLeader(string role) => null; - /// Raised when the role leader changes. + /// public event EventHandler? RoleLeaderChanged { add { _ = value; } diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/VirtualTags/DependencyMuxActorTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/VirtualTags/DependencyMuxActorTests.cs index 2c237ab6..f0587ef9 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/VirtualTags/DependencyMuxActorTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/VirtualTags/DependencyMuxActorTests.cs @@ -155,6 +155,7 @@ public sealed class DependencyMuxActorTests : RuntimeActorTestBase /// The identifier of the virtual tag. /// The expression to evaluate. /// A dictionary of dependency values keyed by reference. + /// A containing the integer sum of all dependency values. public ZB.MOM.WW.OtOpcUa.Commons.Engines.VirtualTagEvalResult Evaluate( string id, string expression, IReadOnlyDictionary deps) { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/VirtualTags/VirtualTagActorTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/VirtualTags/VirtualTagActorTests.cs index af001e59..31ce6527 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/VirtualTags/VirtualTagActorTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/VirtualTags/VirtualTagActorTests.cs @@ -86,11 +86,7 @@ public sealed class VirtualTagActorTests : RuntimeActorTestBase /// Test evaluator that sums integer dependency values. private sealed class SumEvaluator : IVirtualTagEvaluator { - /// Evaluates the expression by summing integer dependencies. - /// The tag identifier. - /// The expression string. - /// The dependency values. - /// The sum of integer values in the dependencies. + /// public VirtualTagEvalResult Evaluate(string id, string expr, IReadOnlyDictionary deps) { var sum = deps.Values.OfType().Sum(); diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/Audit/AuditActorTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/Audit/AuditActorTests.cs index 19f8a09a..2467b6cb 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/Audit/AuditActorTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/Audit/AuditActorTests.cs @@ -76,6 +76,7 @@ public sealed class AuditActorTests private sealed class StubAccessor(string? value) : IAuditActorAccessor { + /// public string? CurrentActor { get; } = value; } } diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/Audit/HttpAuditActorAccessorTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/Audit/HttpAuditActorAccessorTests.cs index ce082d5f..754b4630 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/Audit/HttpAuditActorAccessorTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/Audit/HttpAuditActorAccessorTests.cs @@ -110,6 +110,7 @@ public sealed class HttpAuditActorAccessorTests private sealed class HttpContextAccessorStub(HttpContext? context) : IHttpContextAccessor { + /// Gets or sets the HTTP context for the stub. public HttpContext? HttpContext { get; set; } = context; } } diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/AuthEndpointsIntegrationTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/AuthEndpointsIntegrationTests.cs index 5d38e025..9e7704d1 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/AuthEndpointsIntegrationTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/AuthEndpointsIntegrationTests.cs @@ -41,6 +41,7 @@ public sealed class AuthEndpointsIntegrationTests : IAsyncLifetime private static CancellationToken Ct => TestContext.Current.CancellationToken; /// Initializes the test host and server. + /// A task that represents the asynchronous operation. public async ValueTask InitializeAsync() { var dbName = $"auth-int-tests-{Guid.NewGuid():N}"; @@ -106,6 +107,7 @@ public sealed class AuthEndpointsIntegrationTests : IAsyncLifetime } /// Disposes the test host and server. + /// A task that represents the asynchronous operation. public async ValueTask DisposeAsync() { await _host.StopAsync(TestContext.Current.CancellationToken); @@ -122,6 +124,7 @@ public sealed class AuthEndpointsIntegrationTests : IAsyncLifetime }; /// Tests that login with valid credentials returns 204 and sets cookie. + /// A task that represents the asynchronous operation. [Fact] public async Task Login_with_valid_credentials_returns_204_and_sets_cookie() { @@ -134,6 +137,7 @@ public sealed class AuthEndpointsIntegrationTests : IAsyncLifetime } /// Tests that login with invalid credentials returns 401. + /// A task that represents the asynchronous operation. [Fact] public async Task Login_with_invalid_credentials_returns_401() { @@ -145,6 +149,7 @@ public sealed class AuthEndpointsIntegrationTests : IAsyncLifetime } /// Tests that login when LDAP throws returns 503. + /// A task that represents the asynchronous operation. [Fact] public async Task Login_when_ldap_throws_returns_503() { @@ -156,6 +161,7 @@ public sealed class AuthEndpointsIntegrationTests : IAsyncLifetime } /// Tests that ping anonymous returns 401. + /// A task that represents the asynchronous operation. [Fact] public async Task Ping_anonymous_returns_401() { @@ -166,6 +172,7 @@ public sealed class AuthEndpointsIntegrationTests : IAsyncLifetime } /// Tests that ping after cookie login returns 200. + /// A task that represents the asynchronous operation. [Fact] public async Task Ping_after_cookie_login_returns_200() { @@ -181,6 +188,7 @@ public sealed class AuthEndpointsIntegrationTests : IAsyncLifetime } /// Tests that token after cookie login returns jwt. + /// A task that represents the asynchronous operation. [Fact] public async Task Token_after_cookie_login_returns_jwt() { @@ -202,6 +210,7 @@ public sealed class AuthEndpointsIntegrationTests : IAsyncLifetime /// A system-wide DB row for a group the user holds grants an extra role on top of /// the appsettings baseline; the merged role surfaces in the issued JWT's Role claims. + /// A task that represents the asynchronous operation. [Fact] public async Task Login_merges_db_role_grant_into_claims() { @@ -238,6 +247,7 @@ public sealed class AuthEndpointsIntegrationTests : IAsyncLifetime /// still SUCCEEDS but the user is granted ZERO role claims. They are authenticated (can prove /// identity) yet authorized for nothing role-gated until the mapper recovers — the safe /// fail-closed behaviour, not a fail-open with a stale role set. + /// A task that represents the asynchronous operation. [Fact] public async Task Login_when_role_mapper_throws_signs_in_with_no_role_claims() { @@ -307,6 +317,7 @@ public sealed class AuthEndpointsIntegrationTests : IAsyncLifetime /// Also asserts that the old short-name literals "Username" and "DisplayName" are NOT emitted /// (the pre-Task-1.5 strings that would indicate the migration was incomplete). /// + /// A task that represents the asynchronous operation. [Fact] public async Task Login_emits_canonical_ZbClaimTypes_on_cookie_principal() { @@ -367,6 +378,7 @@ public sealed class AuthEndpointsIntegrationTests : IAsyncLifetime /// docs for the rationale and the caveat that /// applies if a JwtBearer scheme is ever added). /// + /// A task that represents the asynchronous operation. [Fact] public async Task Token_payload_uses_canonical_zb_claim_keys() { @@ -420,6 +432,7 @@ public sealed class AuthEndpointsIntegrationTests : IAsyncLifetime } /// Tests that logout clears the cookie. + /// A task that represents the asynchronous operation. [Fact] public async Task Logout_clears_the_cookie() { @@ -439,6 +452,7 @@ public sealed class AuthEndpointsIntegrationTests : IAsyncLifetime } /// Anonymous browser GET of a protected route redirects to /login with a ReturnUrl. + /// A task that represents the asynchronous operation. [Fact] public async Task Root_anonymous_browser_GET_redirects_to_login() { @@ -457,6 +471,7 @@ public sealed class AuthEndpointsIntegrationTests : IAsyncLifetime /// via the X-Requested-With header — the ASP.NET cookie handler's IsAjaxRequest /// heuristic). The framework still writes a Location header alongside the 401; /// AJAX clients ignore it. + /// A task that represents the asynchronous operation. [Fact] public async Task Root_anonymous_xhr_GET_returns_401() { @@ -479,11 +494,7 @@ public sealed class AuthEndpointsIntegrationTests : IAsyncLifetime private sealed class StubLdapAuthService : ILdapAuthService { - /// Authenticates a user asynchronously using the stub service. - /// The username to authenticate. - /// The password to verify. - /// The cancellation token. - /// The authentication result. + /// public Task AuthenticateAsync(string username, string password, CancellationToken ct = default) { if (username == "ldap-down") @@ -517,10 +528,12 @@ public sealed class AuthEndpointsIntegrationTests : IAsyncLifetime /// private sealed class StubLdapGroupRoleMappingService : ILdapGroupRoleMappingService { + /// Gets the seeded group-to-role mapping rows available for lookup. public List Rows { get; } = []; + /// Gets or sets a value indicating whether the service should simulate a fault. public bool Throws { get; set; } - /// Returns seeded rows whose group matches one of . + /// public Task> GetByGroupsAsync( IEnumerable ldapGroups, CancellationToken cancellationToken) { @@ -531,15 +544,15 @@ public sealed class AuthEndpointsIntegrationTests : IAsyncLifetime return Task.FromResult(matched); } - /// Not exercised by these tests. + /// public Task> ListAllAsync(CancellationToken cancellationToken) => throw new NotSupportedException(); - /// Not exercised by these tests. + /// public Task CreateAsync(LdapGroupRoleMapping row, CancellationToken cancellationToken) => throw new NotSupportedException(); - /// Not exercised by these tests. + /// public Task DeleteAsync(Guid id, CancellationToken cancellationToken) => throw new NotSupportedException(); } diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/CanonicalAdminRolesTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/CanonicalAdminRolesTests.cs index 854b1739..6ff73943 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/CanonicalAdminRolesTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/CanonicalAdminRolesTests.cs @@ -27,6 +27,9 @@ public sealed class CanonicalAdminRolesTests { // --- (a) the mapper mints the CANONICAL role claim for each native group ---------------- + /// Verifies that the mapper yields the canonical role claim for each native LDAP group. + /// The canonical role name to test. + /// A task that represents the asynchronous operation. [Theory] [InlineData("Viewer")] // was ConfigViewer [InlineData("Designer")] // was ConfigEditor @@ -42,6 +45,10 @@ public sealed class CanonicalAdminRolesTests result.Roles.ShouldContain(canonicalRole); } + /// Verifies that a system-wide DB row role renders as the canonical role string. + /// The admin role enum value to test. + /// The expected canonical role string. + /// A task that represents the asynchronous operation. [Theory] [InlineData(AdminRole.Viewer, "Viewer")] [InlineData(AdminRole.Designer, "Designer")] @@ -61,6 +68,8 @@ public sealed class CanonicalAdminRolesTests // --- (b)/(c) the REAL registered authorization policies enforce on the canonical values --- + /// Verifies that the Deployments role check authorizes Designer and Administrator roles. + /// A task that represents the asynchronous operation. [Fact] public async Task Deployments_role_check_authorizes_Designer_and_Administrator() { @@ -76,6 +85,8 @@ public sealed class CanonicalAdminRolesTests (await authz.AuthorizeAsync(UserInRole("Viewer"), policy)).Succeeded.ShouldBeFalse(); } + /// Verifies that the FleetAdmin policy authorizes only the Administrator role. + /// A task that represents the asynchronous operation. [Fact] public async Task FleetAdmin_policy_authorizes_only_Administrator() { @@ -88,6 +99,8 @@ public sealed class CanonicalAdminRolesTests (await authz.AuthorizeAsync(UserInRole("Viewer"), "FleetAdmin")).Succeeded.ShouldBeFalse(); } + /// Verifies that the DriverOperator policy authorizes Operator and Administrator roles. + /// A task that represents the asynchronous operation. [Fact] public async Task DriverOperator_policy_authorizes_Operator_and_Administrator() { @@ -139,16 +152,31 @@ public sealed class CanonicalAdminRolesTests private sealed class FakeMappingService(IReadOnlyList rows) : ILdapGroupRoleMappingService { + /// Returns all seeded rows that belong to any of the specified LDAP groups. + /// The LDAP groups to look up. + /// The cancellation token. + /// A task that resolves to the matching role mappings. public Task> GetByGroupsAsync( IEnumerable ldapGroups, CancellationToken cancellationToken) => Task.FromResult(rows); + /// Returns all seeded role mapping rows. + /// The cancellation token. + /// A task that resolves to all role mappings. public Task> ListAllAsync(CancellationToken cancellationToken) => Task.FromResult(rows); + /// Not supported in this stub. + /// The row to create. + /// The cancellation token. + /// Never returns; always throws. public Task CreateAsync(LdapGroupRoleMapping row, CancellationToken cancellationToken) => throw new NotSupportedException(); + /// Not supported in this stub. + /// The identifier of the row to delete. + /// The cancellation token. + /// Never returns; always throws. public Task DeleteAsync(Guid id, CancellationToken cancellationToken) => throw new NotSupportedException(); } diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/OtOpcUaGroupRoleMapperTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/OtOpcUaGroupRoleMapperTests.cs index bd6559b7..f93e5035 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/OtOpcUaGroupRoleMapperTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/OtOpcUaGroupRoleMapperTests.cs @@ -28,6 +28,8 @@ public sealed class OtOpcUaGroupRoleMapperTests return new OtOpcUaGroupRoleMapper(options, new FakeMappingService(dbRows)); } + /// Verifies that the mapper maps a configured group and drops unmapped groups. + /// A task that represents the asynchronous test operation. [Fact] public async Task Maps_config_group_and_drops_unmapped_group() { @@ -39,6 +41,8 @@ public sealed class OtOpcUaGroupRoleMapperTests result.Scope.ShouldBeNull(); } + /// Verifies that a system-wide DB row adds a role on top of the config baseline. + /// A task that represents the asynchronous test operation. [Fact] public async Task System_wide_db_row_adds_role_on_top_of_config_baseline() { @@ -53,6 +57,8 @@ public sealed class OtOpcUaGroupRoleMapperTests result.Scope.ShouldBeNull(); } + /// Verifies that a cluster-scoped DB row is ignored by the mapper. + /// A task that represents the asynchronous test operation. [Fact] public async Task Cluster_scoped_db_row_is_ignored() { @@ -72,6 +78,8 @@ public sealed class OtOpcUaGroupRoleMapperTests result.Roles.ShouldBeEmpty(); } + /// Verifies that the mapper output matches the expected RoleMapper.Map + Merge result for representative inputs. + /// A task that represents the asynchronous test operation. [Fact] public async Task Reproduces_RoleMapper_Map_plus_Merge_for_representative_inputs() { @@ -102,16 +110,31 @@ public sealed class OtOpcUaGroupRoleMapperTests /// In-memory stand-in for the EF-backed DB service; returns the configured rows verbatim. private sealed class FakeMappingService(IReadOnlyList rows) : ILdapGroupRoleMappingService { + /// Returns all seeded rows that belong to any of the specified LDAP groups. + /// The LDAP groups to look up. + /// The cancellation token. + /// A task that resolves to the matching role mappings. public Task> GetByGroupsAsync( IEnumerable ldapGroups, CancellationToken cancellationToken) => Task.FromResult(rows); + /// Returns all seeded role mapping rows. + /// The cancellation token. + /// A task that resolves to all role mappings. public Task> ListAllAsync(CancellationToken cancellationToken) => Task.FromResult(rows); + /// Not supported in this stub. + /// The row to create. + /// The cancellation token. + /// Never returns; always throws. public Task CreateAsync(LdapGroupRoleMapping row, CancellationToken cancellationToken) => throw new NotSupportedException(); + /// Not supported in this stub. + /// The identifier of the row to delete. + /// The cancellation token. + /// Never returns; always throws. public Task DeleteAsync(Guid id, CancellationToken cancellationToken) => throw new NotSupportedException(); } diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/OtOpcUaLdapAuthServiceTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/OtOpcUaLdapAuthServiceTests.cs index d0b9354b..fc397744 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/OtOpcUaLdapAuthServiceTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/OtOpcUaLdapAuthServiceTests.cs @@ -22,6 +22,7 @@ public sealed class OtOpcUaLdapAuthServiceTests new(options, inner, NullLogger.Instance); /// DevStubMode on → stub Administrator success WITHOUT hitting the library. + /// A task that represents the asynchronous operation. [Fact] public async Task DevStubMode_grants_Administrator_without_calling_the_library() { @@ -38,6 +39,7 @@ public sealed class OtOpcUaLdapAuthServiceTests } /// Enabled=false → denial, no library call (master switch wins over DevStubMode). + /// A task that represents the asynchronous operation. [Fact] public async Task Disabled_denies_without_calling_the_library_even_with_devstub() { @@ -53,6 +55,7 @@ public sealed class OtOpcUaLdapAuthServiceTests /// Real path: a library success surfaces its Groups; Roles are left empty for the /// downstream mapper (the library returns groups, not roles). + /// A task that represents the asynchronous operation. [Fact] public async Task Real_path_success_surfaces_groups_and_leaves_roles_for_the_mapper() { @@ -73,6 +76,7 @@ public sealed class OtOpcUaLdapAuthServiceTests } /// Real path: a library failure folds into a fail-closed error string. + /// A task that represents the asynchronous operation. [Fact] public async Task Real_path_failure_folds_into_error() { @@ -90,6 +94,7 @@ public sealed class OtOpcUaLdapAuthServiceTests /// Insecure transport without AllowInsecure fails closed at the auth boundary WITHOUT /// reaching the library — preserving the bespoke service's login-time guard after UseTls→Transport. + /// A task that represents the asynchronous operation. [Fact] public async Task Insecure_transport_without_AllowInsecure_fails_closed_without_calling_library() { @@ -107,6 +112,9 @@ public sealed class OtOpcUaLdapAuthServiceTests } /// Empty username/password are rejected up front without a library call. + /// The username to test. + /// The password to test. + /// A task that represents the asynchronous operation. [Theory] [InlineData("", "pw")] [InlineData("user", "")] @@ -124,8 +132,14 @@ public sealed class OtOpcUaLdapAuthServiceTests /// Records whether the library service was invoked and returns a canned result. private sealed class RecordingLibService(LibLdapAuthResult result) : LibILdapAuthService { + /// Gets a value indicating whether the library service was called. public bool Called { get; private set; } + /// Authenticates the user, records that the call was made, and returns the canned result. + /// The username to authenticate. + /// The password to authenticate. + /// Cancellation token for the operation. + /// A task that resolves to the canned . public Task AuthenticateAsync(string username, string password, CancellationToken ct) { Called = true; diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/RoleMapperTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/RoleMapperTests.cs index 20ae93d0..1af7b17a 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/RoleMapperTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/RoleMapperTests.cs @@ -62,6 +62,7 @@ public sealed class RoleMapperTests roles.ShouldBe(new[] { "Administrator" }); } + /// Verifies that Merge unions baseline roles with system-wide DB roles. [Fact] public void Merge_unions_baseline_and_systemwide_db_roles() { @@ -76,6 +77,7 @@ public sealed class RoleMapperTests result.ShouldNotContain("Designer"); // cluster-scoped row ignored (global-only) } + /// Verifies that Merge with no DB rows returns the baseline roles unchanged. [Fact] public void Merge_with_no_db_rows_returns_baseline() => RoleMapper.Merge(["Administrator"], []).ShouldBe(["Administrator"]); diff --git a/tests/Tooling/ZB.MOM.WW.OtOpcUa.Analyzers.Tests/UnwrappedCapabilityCallAnalyzerTests.cs b/tests/Tooling/ZB.MOM.WW.OtOpcUa.Analyzers.Tests/UnwrappedCapabilityCallAnalyzerTests.cs index a6eaf861..83b79895 100644 --- a/tests/Tooling/ZB.MOM.WW.OtOpcUa.Analyzers.Tests/UnwrappedCapabilityCallAnalyzerTests.cs +++ b/tests/Tooling/ZB.MOM.WW.OtOpcUa.Analyzers.Tests/UnwrappedCapabilityCallAnalyzerTests.cs @@ -83,6 +83,7 @@ namespace ZB.MOM.WW.OtOpcUa.Core.Resilience { """; /// Verifies that a direct ReadAsync call in the Server namespace trips the diagnostic. + /// A task that represents the asynchronous operation. [Fact] public async Task Direct_ReadAsync_Call_InServerNamespace_TripsDiagnostic() { @@ -107,6 +108,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { } /// Verifies that a wrapped ReadAsync call inside a CapabilityInvoker lambda passes cleanly. + /// A task that represents the asynchronous operation. [Fact] public async Task Wrapped_ReadAsync_InsideCapabilityInvokerLambda_PassesCleanly() { @@ -131,6 +133,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { } /// Verifies that a direct write without wrapper trips the diagnostic. + /// A task that represents the asynchronous operation. [Fact] public async Task DirectWrite_WithoutWrapper_TripsDiagnostic() { @@ -154,6 +157,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { } /// Verifies that a discovery call without wrapper trips the diagnostic. + /// A task that represents the asynchronous operation. [Fact] public async Task Discovery_Call_WithoutWrapper_TripsDiagnostic() { @@ -176,6 +180,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { } /// Verifies that a call outside of a lambda but inside an invoker call still trips the diagnostic. + /// A task that represents the asynchronous operation. [Fact] public async Task Call_OutsideOfLambda_ButInsideInvokerCall_StillTripsDiagnostic() { @@ -210,6 +215,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { // ----------------------------------------------------------------------- /// Verifies that a direct SubscribeAsync call trips the diagnostic. + /// A task that represents the asynchronous operation. [Fact] public async Task Direct_SubscribeAsync_Call_TripsDiagnostic() { @@ -234,6 +240,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { } /// Verifies that a wrapped SubscribeAsync call inside a CapabilityInvoker lambda passes cleanly. + /// A task that represents the asynchronous operation. [Fact] public async Task Wrapped_SubscribeAsync_InsideCapabilityInvokerLambda_PassesCleanly() { @@ -264,6 +271,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { // ----------------------------------------------------------------------- /// Verifies that a direct SubscribeAlarmsAsync call trips the diagnostic. + /// A task that represents the asynchronous operation. [Fact] public async Task Direct_SubscribeAlarmsAsync_Call_TripsDiagnostic() { @@ -287,6 +295,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { } /// Verifies that a wrapped SubscribeAlarmsAsync call inside a CapabilityInvoker lambda passes cleanly. + /// A task that represents the asynchronous operation. [Fact] public async Task Wrapped_SubscribeAlarmsAsync_InsideCapabilityInvokerLambda_PassesCleanly() { @@ -316,6 +325,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { // ----------------------------------------------------------------------- /// Verifies that a direct ReadRawAsync call on an interface trips the diagnostic. + /// A task that represents the asynchronous operation. [Fact] public async Task Direct_ReadRawAsync_OnInterface_TripsDiagnostic() { @@ -339,6 +349,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { } /// Verifies that a direct ReadProcessedAsync call on an interface trips the diagnostic. + /// A task that represents the asynchronous operation. [Fact] public async Task Direct_ReadProcessedAsync_OnInterface_TripsDiagnostic() { @@ -362,6 +373,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { } /// Verifies that a direct ReadAtTimeAsync call on an interface trips the diagnostic. + /// A task that represents the asynchronous operation. [Fact] public async Task Direct_ReadAtTimeAsync_OnInterface_TripsDiagnostic() { @@ -388,6 +400,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { } /// Verifies that a wrapped ReadRawAsync call inside a CapabilityInvoker lambda passes cleanly. + /// A task that represents the asynchronous operation. [Fact] public async Task Wrapped_ReadRawAsync_InsideCapabilityInvokerLambda_PassesCleanly() { @@ -417,6 +430,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { // ----------------------------------------------------------------------- /// Verifies that a direct HistoryRead call on a concrete driver trips the diagnostic. + /// A task that represents the asynchronous operation. [Fact] public async Task Direct_HistoryRead_OnConcreteDriver_TripsDiagnostic() { @@ -451,6 +465,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { // ----------------------------------------------------------------------- /// Verifies that a synchronous GetHostStatuses call is not flagged. + /// A task that represents the asynchronous operation. [Fact] public async Task Synchronous_GetHostStatuses_IsNotFlagged() { @@ -475,6 +490,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { // ----------------------------------------------------------------------- /// Verifies that a concrete driver with a renamed read method still trips the diagnostic. + /// A task that represents the asynchronous operation. [Fact] public async Task ConcreteDriver_RenamedReadMethod_StillTripsDiagnostic() { @@ -520,6 +536,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { // ----------------------------------------------------------------------- /// Verifies that a wrapped WriteAsync call inside ExecuteWriteAsync passes cleanly. + /// A task that represents the asynchronous operation. [Fact] public async Task Wrapped_WriteAsync_InsideExecuteWriteAsync_PassesCleanly() { @@ -549,6 +566,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { // ----------------------------------------------------------------------- /// Verifies that a guarded call inside a nested non-wrapper lambda trips the diagnostic. + /// A task that represents the asynchronous operation. [Fact] public async Task GuardedCall_InsideNestedNonWrapperLambda_TripsDiagnostic() { @@ -578,6 +596,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { } /// Verifies that a guarded call inside a wrapper lambda inside a non-wrapper lambda passes cleanly. + /// A task that represents the asynchronous operation. [Fact] public async Task GuardedCall_InsideWrapperLambda_InsideNonWrapperLambda_PassesCleanly() { @@ -615,6 +634,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { // ======================================================================= /// Verifies that a guarded call inside an alarm surface invoker method wrapped by CapabilityInvoker passes cleanly. + /// A task that represents the asynchronous operation. [Fact] public async Task GuardedCall_InsideAlarmSurfaceInvokerMethod_WrappedByCapabilityInvoker_PassesCleanly() { @@ -653,6 +673,7 @@ namespace ZB.MOM.WW.OtOpcUa.Core.Resilience.Test { // ======================================================================= /// Verifies that a non-guarded async call does not trip the diagnostic. + /// A task that represents the asynchronous operation. [Fact] public async Task NonGuardedAsyncCall_DoesNotTrip() { @@ -681,6 +702,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { // ======================================================================= /// Verifies that a compilation without guarded interface references emits no diagnostics. + /// A task that represents the asynchronous operation. [Fact] public async Task Compilation_WithoutGuardedInterfaceReferences_EmitsNoDiagnostics() { @@ -709,6 +731,7 @@ namespace SomeOther { // ======================================================================= /// Verifies that a direct ReadAtTimeAsync call on a concrete driver inheriting the DIM trips the diagnostic. + /// A task that represents the asynchronous operation. [Fact] public async Task Direct_ReadAtTimeAsync_OnConcreteDriverInheritingDIM_TripsDiagnostic() { @@ -745,6 +768,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { } /// Verifies that a direct ReadEventsAsync call on a concrete driver inheriting the DIM trips the diagnostic. + /// A task that represents the asynchronous operation. [Fact] public async Task Direct_ReadEventsAsync_OnConcreteDriverInheritingDIM_TripsDiagnostic() { @@ -775,6 +799,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { } /// Verifies that a direct ReadAtTimeAsync call on a concrete driver overriding the DIM trips the diagnostic. + /// A task that represents the asynchronous operation. [Fact] public async Task Direct_ReadAtTimeAsync_OnConcreteDriverOverridingDIM_TripsDiagnostic() { @@ -807,6 +832,7 @@ namespace ZB.MOM.WW.OtOpcUa.Server { } /// Verifies that a wrapped ReadAtTimeAsync DIM call inside a CapabilityInvoker lambda passes cleanly. + /// A task that represents the asynchronous operation. [Fact] public async Task Wrapped_ReadAtTimeAsync_DIM_InsideCapabilityInvokerLambda_PassesCleanly() {