Finish the fluent node manager migration (#876) - #880
Merged
Merged
Conversation
The hooks the migration was waiting for landed with UA-.NETStandard#4418 and #4442. Four of the five node managers of samples #876 move onto them; the fifth, HistoricalAccess, is deliberately left out of this change. Workshop/DataAccess and Workshop/PerfTest move off AsyncCustomNodeManager. Both serve an address space which is computed rather than stored, so neither gets a ModelDesign - there is no type model to declare. They derive from FluentNodeManagerBase and drive the builder themselves, the way StateMachines and AlarmCondition already do, and their GetManagerHandleAsync/ValidateNodeAsync pairs become virtual node families registered with ResolveNodes. PerfTest needs two families: only the register variables carry a read handler, and a family which wires one may only ever resolve variables. That read handler is new and is what the move requires: the server now holds on to the node of a monitored variable, and the register never writes into that node - it queues into the monitored item directly - so a read would otherwise answer with the value the variable was synthesized with. Samples/Opc.Ua.Sample/MemoryBuffer loses GetManagerHandleAsync and its four monitored item overrides. The tags are a virtual node family; the create hook keeps the three refusals the sample promises and hands the stack a factory for the item the buffer publishes into, which the stack then registers, modifies and deletes like any other. GenerateFactory = false goes with them: the partial can read the ApplicationConfiguration now, so the hand-written factory has no reason left to exist. Workshop/Aggregation loses its handle resolver and validator to a virtual node family, and its CreateMonitoredItemsAsync and OnDeleteMonitoredItemsCompleteAsync overrides to the builder-level batch hooks. The batched Read, Write and Call forwarding and the modify and monitoring mode hooks stay service overrides - they turn one service call into one downstream call, which the per-node fluent handlers would undo. The class remark said they were waiting for those hooks; it now says what they are actually there for. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…torical-access-0208f6
The last of the five node managers of #876. Same shape as DataAccess and PerfTest: no ModelDesign, because there is no type model to declare for a file archive, so the manager derives from FluentNodeManagerBase and drives the builder itself. The folders and the items which exist at startup stay predefined nodes. What becomes a virtual node family is the tail which is not registered up front: a file which turns up in the archive root is turned into an item on the first request which names it. The family carries the two monitored item hooks as well, which is what the archive items need to know that somebody is watching them; the live variables fall through them as they did before. ReadAsync stays a service override, and now says why: it refreshes a stale archive from disk before any attribute of an item is read, and holds the archive lock across the read. A per-node read handler would only see the Value attribute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Everything nuget.org reports as outdated: Microsoft.CodeAnalysis.NetAnalyzers 10.0.400 -> 10.0.401 Microsoft.EntityFrameworkCore.Design 10.0.11 -> 10.0.12 Microsoft.EntityFrameworkCore.SqlServer 10.0.11 -> 10.0.12 Microsoft.Extensions.DependencyInjection.Abstractions 10.0.11 -> 10.0.12 Microsoft.Extensions.Hosting 10.0.11 -> 10.0.12 Microsoft.Extensions.Logging.Abstractions 10.0.11 -> 10.0.12 Microsoft.Extensions.Logging.Console 10.0.11 -> 10.0.12 Microsoft.NET.Test.Sdk 18.8.1 / 18.9.0 -> 18.10.0 NUnit3TestAdapter 6.2.0 / 6.3.0 -> 6.3.0 The two test packages had drifted apart between the test projects; both are now the same version everywhere. The OPC UA stack itself is left alone. It is pinned in one place in targets.props to a preview from the OPC Foundation GitHub Packages feed, which needs a token to query, so what the latest preview is cannot be established here - and a bump of it is its own change. The analyzer bump reports no new rule: a rebuild of the whole solution before and after it produces the same set of warning codes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The last package left after the previous commit: the OPC UA stack itself, pinned in one place in targets.props. 2.0.301.4181 -> 2.0.312.3680-preview (SDK master 0e600c0f), eleven master builds on. Everything it broke, it broke by having grown the thing the samples were filling in for, so each fix is a deletion: * `SampleServerBuilderExtensions` is gone. Its own summary said it held "the registrations the samples make on the server builder of the stack which the stack itself has no method for" - the stack now has all of them, so every call resolved to two candidates. `AddNodeManagers` grew a better shape on the way: the loaded ApplicationConfiguration is handed to the callback instead of being resolved from the container, which is what `ConfiguredNodeManagerFactories` and the loop over it in `SampleServerFactory` existed for. Both are gone too, and the hosted server adds those node managers itself. * `NodeBrowser` grew the async browse seam the Aggregation `Browser` was waiting for, in so many words: "There is no async browser seam in the stack to migrate to yet. NextAsync is the real implementation and is what the sample would call once the stack offers one." It is an override now, and the asynchronous server browse and translate paths await it instead of blocking a request worker on the call to the aggregated server. `Next` stays for the synchronous consumers and says so. * The model generator emits a protected constructor taking the namespace URIs, which is what TestData had a hand-written constructor and `GenerateFactory = false` for. Its two-argument sibling chains to it with `null`, which was ambiguous against the sample constructor. The sample names its second namespace in `AdditionalNamespaceUris` now, takes the generated factory, and creates the test system in an override of the three-argument `LoadPredefinedNodesAsync` - one step earlier than Configure, which is where the typed node states need it. Verified on the packages of the nuget-publish run which produced this version (run 34435263403, artifact opcua-packages-34435263403 used as a local folder feed, because GitHub Packages cannot be queried without a token): the whole solution rebuilds with the same set of warning codes as before the bump, and all five test tiers pass unchanged - 192/126/445/65/247. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #876. The hooks the migration was waiting for landed with
OPCFoundation/UA-.NETStandard#4418 and #4442, so all five node managers that issue lists move
onto them. Master is merged in, and every NuGet package in the repository is updated - the OPC
UA preview included - in two commits of their own.
Moved off
AsyncCustomNodeManagerWorkshop/DataAccess,Workshop/PerfTestandWorkshop/HistoricalAccess. All three serve anaddress space which is computed rather than stored, over standard OPC UA types, so none of them
gets a ModelDesign - there would be no type model to declare in one. They derive from
FluentNodeManagerBaseand drive the builder themselves (CreateFluentBuilder→Configure→RegisterAuthoredNodesAsync→CompleteConfigureAsync→SealConfigurationAsync), the wayStateMachinesandAlarmConditionalready do, and theirGetManagerHandleAsync/ValidateNodeAsyncpairs become virtual node families registered withResolveNodes.PerfTest needs two families: only the register variables carry a read handler, and a family
which wires one may only ever resolve variables. That read handler is new, and it is what the
move requires. The server now holds on to the node of a monitored variable, but the register
never writes into that node - it queues the changed value straight into the monitored item,
bypassing the sampling machinery - so a read of a subscribed variable would otherwise answer
with the value the variable was synthesized with.
HistoricalAccess keeps its folders and its startup items as predefined nodes; what becomes a
family is the tail which is not registered up front, a file which turns up in the archive root
and is turned into an item on the first request naming it. The family carries the two monitored
item hooks as well, which the archive items need and the live variables fall through.
Overrides whose stated reason is gone
Samples/Opc.Ua.Sample/MemoryBufferlosesGetManagerHandleAsyncand its four monitored itemoverrides. The tags are a virtual node family; the create hook keeps the three refusals the
sample promises (filter, index range, data encoding) and hands the stack a factory for the item
the buffer publishes into, which the stack then registers, modifies and deletes like any other
monitored item.
GenerateFactory = falsegoes with them: the partial can read theApplicationConfigurationnow, so the hand-written factory has no reason left to exist.Workshop/Aggregationloses its handle resolver and validator to a virtual node family - whichalso wires the browser that walks the remote address space - and its
CreateMonitoredItemsAsyncand
OnDeleteMonitoredItemsCompleteAsyncoverrides to the builder-level batch hooks.What deliberately stays a service override
Aggregation's batched Read, Write and Call forwarding and its modify and monitoring mode hooks:
they turn one service call into one downstream service call, which the per-node fluent handlers
would undo - the opposite of what an aggregating server is for. The class remark used to say
they were waiting for #4397/#4398/#4399; it now says what they are actually there for.
HistoricalAccess's
ReadAsync: it refreshes a stale archive from disk before any attribute ofan item is read, and holds the archive lock across the read so the simulation cannot change the
value fields halfway through it. A per-node read handler would only see the Value attribute.
Package updates
Two commits. The first takes everything nuget.org reports as outdated:
Microsoft.CodeAnalysis.NetAnalyzersto 10.0.401, theMicrosoft.Extensions.*andMicrosoft.EntityFrameworkCore.*packages to 10.0.12,Microsoft.NET.Test.Sdkto 18.10.0 andNUnit3TestAdapterto 6.3.0 - the last two had drifted apart between the test projects and arenow the same version everywhere. After it, every package in the repository other than the OPC UA
stack sits on its latest stable release.
The second moves the stack itself, pinned in one place in
targets.props:2.0.301.4181-preview→2.0.312.3680-preview(SDK master0e600c0f), eleven master builds on.Everything it broke, it broke by having grown the thing the samples were filling in for, so each
fix is a deletion:
SampleServerBuilderExtensionsis gone. Its own summary said it held "the registrations thesamples make on the server builder of the stack which the stack itself has no method for" - the
stack now has all of them, so every call resolved to two candidates.
AddNodeManagersgrew abetter shape on the way: the loaded
ApplicationConfigurationis handed to the callback insteadof being resolved from the container, which is what
ConfiguredNodeManagerFactoriesand the loopover it in
SampleServerFactoryexisted for. Both are gone too.NodeBrowsergrew the async browse seam the AggregationBrowserwas waiting for, in so manywords ("There is no async browser seam in the stack to migrate to yet").
NextAsyncis anoverride now, so the asynchronous browse and translate paths await the call to the aggregated
server instead of blocking a request worker on it.
TestData had a hand-written constructor and
GenerateFactory = falsefor. The sample names itssecond namespace in
AdditionalNamespaceUrisnow, takes the generated factory, and creates thetest system in an override of the three-argument
LoadPredefinedNodesAsync.Verified against the packages of the
nuget-publishrun which produced that version (run34435263403, itsopcua-packages-34435263403artifact used as a local folder feed, becauseGitHub Packages cannot be queried without a token). A reviewer with feed access should confirm
2.0.312.3680-previewis the version they want - it is simply the newest master build at thetime of writing.
Verification
Whole solution rebuilds with the same set of warning codes as before either bump, 0 errors.
All five test tiers, on the new SDK:
SampleNodeManagers.Tests- 192 passed, 1 pre-existing skipSampleServers.Tests- 126 passedSampleClientModels.Tests- 445 passed, 3 skippedSampleClients.Tests- 65 passedSampleConfiguration.Tests- 247 passed🤖 Generated with Claude Code