mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-21 09:06:37 +08:00
15f9a5ebcc
# Description `MachineryClient` browses the `Machines` folder, the `Components` add-in, the `MachineryBuildingBlocks` organizer, the energy resources and the process values with `HierarchicalReferences` and subtypes, and turned every returned `ReferenceDescription` into a `MachineEntry`. Browse returns one description per reference, not per node, and `HasEventSource` and its subtype `HasNotifier` are hierarchical references. A machine that the `Machines` folder organizes and that is also registered as a notifier of the folder - how a server exposes its event hierarchy below the `Server` object once alarms are attached - was therefore returned twice by `EnumerateMachinesAsync` and `DiscoverMachinesAsync`. The same held for a component that is an event source of its `Components` folder and for a process value that is an event source of `Monitoring`. The fix: - The shared browse helper reports each target NodeId once and keeps the first description. Its `BrowseName`, `DisplayName` and `TypeDefinition` belong to the node and are the same for every reference that leads to it. - All hierarchical references are still followed. Restricting the browse to `Organizes` would lose machines, components and process values a server references with another hierarchical reference type such as `HasComponent`, so an address space without duplicate references returns exactly what it returned before. - `EnumerateProcessValuesAsync` looks below the machinery item and below its `Monitoring`; a process value reachable from both is now reported once as well. The public API does not change. ## Changes - `src/Opc.Ua.Machinery.Client/MachineryClient.cs`: `BrowseChildrenAsync` skips a NodeId it has already returned; the remarks of `EnumerateMachinesAsync` say so. - `src/Opc.Ua.Machinery.Client/MachineryClient.ProcessValues.cs`: `EnumerateProcessValuesAsync` skips a process value below `Monitoring` it has already returned below the item. Every enumeration of the client goes through `BrowseChildrenAsync`: `EnumerateMachinesAsync`, `DiscoverMachinesAsync`, `EnumerateComponentsAsync`, `EnumerateBuildingBlocksAsync`, `EnumerateEnergyResourcesAsync`, `EnumerateMeteringPointsAsync`, `EnumerateSubMetersAsync`, `EnumerateProcessValuesAsync`, and the child walks of `ReadLifetimeCountersAsync`, `EnumerateEquipmentAsync`, `ReadMeteringPointAsync` and `ReadPublishedResultsAsync`. ## Tests - **New, end-to-end** (`MachineryEndToEndTests.NodesThatAreAlsoEventSourcesOfTheirParentAreEnumeratedOnceAsync`): the test press links the machine to `Machines` with `HasNotifier`, the `HydraulicUnit` component to `Components` and the oil-temperature process value to `Monitoring` with `HasEventSource`, each in addition to its organizing reference, and expects each node once from `EnumerateMachinesAsync`, `DiscoverMachinesAsync`, `EnumerateComponentsAsync` and `EnumerateProcessValuesAsync` over `opc.tcp`. - **New, unit** (`MachineryClientTests.ANodeReachedThroughTwoReferencesIsEnumeratedOnceAsync`): a browse result that carries the same machine under `Organizes` and `HasNotifier`. - **Fail without the fix:** with `src/Opc.Ua.Machinery.Client` from master, all four assertions of the end-to-end test fail - two machines, two NodeIds, two components and two process values instead of one each. - `Opc.Ua.Machinery.Tests` passed 167/167 on net10.0 and 167/167 on net8.0 (macOS). - `Opc.Ua.Machinery.Client` builds for net48, net8.0, net9.0 and net10.0 with 0 warnings. - `dotnet format` (whitespace, style, analyzers) on the changed files: nothing on the changed lines (the `ENDOFLINE` findings concern the whole of every file in a checkout with LF line endings). - Changed lines covered: 8/8 (100 %). - Fork CI (full matrix incl. macOS) and CodeQL: [CI run 37201766371](https://github.com/biancode/UA-.NETStandard/actions/runs/37201766371) and [CodeQL run 37201768280](https://github.com/biancode/UA-.NETStandard/actions/runs/37201768280), both green. ## Notes for review - No extra request: the de-duplication happens on the result of the browse the client already made. - Of the sibling companion clients, `PumpsClient.EnumeratePumpsAsync` and `ScalesClient.EnumerateScalesAsync` already de-duplicate across their two roots. `EnumeratePumpsUnderAsync`, `EnumerateScalesUnderAsync`, `EnumerateSystemScalesAsync` and the DI device discovery follow the same browse pattern without de-duplication; they are not changed here. ## Related Issues - Fixes #4670 ## Checklist - [x] I have signed the [CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf) and read the [CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md) doc. - [x] I have added tests that prove my fix is effective or that my feature works and increased code coverage. - [x] I have added all necessary documentation. - [x] I have verified that my changes do not introduce (new) build or analyzer warnings. - [ ] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. (Affected suite on net10.0 and net8.0 locally on macOS; .NET Framework is covered by the fork CI's Windows net48 legs.) - [ ] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. - [ ] I have addressed **all** PR feedback received. Co-authored-by: Marc Schier <marcschier@users.noreply.github.com>