Overlay a NodeSet2 document onto a generated model in RuntimeNodeSets - #882
Merged
Merged
Conversation
…#878) `INodeManagerBuilder.Import(UANodeSet, INodeSetImportFactoryProvider?)` lets a generated node manager overlay a NodeSet2 document that is only known at runtime onto the model it was generated from. No sample used it. `Workshop/RuntimeNodeSets` gains that as its second half rather than becoming a new sample: the existing half already answers "a document the server has no code for", and the two belong next to each other. The sample now reads as the two ways a NodeSet2 document reaches a running server. Server - `ModelDesign.xml`/`.csv`: `StationType` (Throughput, Status, Reset) and a `SiteType` whose two Station children are the placeholder slots. The identifiers are fixed because an overlay is written against them. - `NodeSets/Site.Layout.NodeSet2.xml` replaces the `Station1` placeholder and adds a `Station3` below `Site`, a node the manager already owns. `NodeSets/Site.Instrumentation.NodeSet2.xml` hangs a `Reset` and a `Temperature` off `Station3` - a parent which only exists in the other document of the same batch. - `SiteNodeManager` imports both and then wires, and resolves the imported nodes by the NodeId their document gives them. `SiteOverlayIds` holds the identifiers the generator cannot emit, because it never sees the documents. - `SiteOverlayNodeManagerFactory` derives from the generated factory to hand the manager the document library out of the container. Client - `BrowseSiteModelAsync` reports, for every node, whether the TypeDefinition of its parent declares that browse name. That is what a Client can see of an overlay: an imported node is an ordinary node, so the evidence is that the address space says more than the types do, and that a declared slot was answered with something else. The window shows it in a new pane. Tests - `RuntimeNodeSetsOverlayNodeManagerTests` (tier 1.5, 11 tests): the batch links across documents, the replacement takes the slot and the placeholder leaves the address space with the references retargeted, an imported instance carries exactly the children its document declares, and behaviour wired onto an imported Method answers like behaviour wired onto a generated one. - One tier 1.7 test for the client model. The README documents the reconciliation rules against that working reference, including the two refusals: the `BadInvalidState` for wiring a node the import displaces (provoked and quoted verbatim, message and log line), and the one a source-generated manager cannot reach. Notable: placeholder replacement needs the slot declared on the *type*. An instance of a plain `ua:BaseObjectType` has no generated state class and therefore no slots, and the same document then adds a second child with the same BrowseName instead of replacing - silently, with no error anywhere. 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 #878.
What
UA-.NETStandard#4432 added
INodeManagerBuilder.Import(UANodeSet, INodeSetImportFactoryProvider?), which lets agenerated node manager overlay a NodeSet2 document that is only known at runtime onto the
model it was generated from. No sample used it.
The issue left one thing open — a new
Workshop/NodeSetOverlayor a second half ofWorkshop/RuntimeNodeSets. This is the second half. The existing sample already answers"a document the server has no code for at all",
Importanswers "a document that extendsa model the server does have code for", and a reader is much better served by the two
side by side than by two samples that each explain half of a decision. The sample now
reads as the two ways a NodeSet2 document reaches a running server.
Server
ModelDesign.xml/.csvStationType(Throughput, Status,Reset(ClearFaults) : Accepted) and aSiteTypewhose twoStationchildren are the placeholder slots. The identifiers are pinned because an overlay is written against them.NodeSets/Site.Layout.NodeSet2.xmlStation1placeholder, and adds aStation3belowSite— a node the manager already owns.NodeSets/Site.Instrumentation.NodeSet2.xmlResetand aTemperatureoffStation3: a parent which only exists in the other document of the same batch.SiteNodeManagerSiteOverlayIdsSiteOverlayNodeManagerFactoryThe address space that comes out:
Site/Station1Site.LayoutSite/Station2Site/Station3Site.LayoutSite/Station3/ResetSite.InstrumentationSite/Station3/TemperatureSite.InstrumentationClient
BrowseSiteModelAsyncreports, for every node, whether the TypeDefinition of its parentdeclares that browse name. That is what a client can actually see of an overlay — an
imported node is an ordinary node, and no Browse response says which document it came
from — so the evidence is that the address space says more than the types do, and that a
declared slot was answered with something else. The window shows it in a new pane beside
the existing one.
Tests
RuntimeNodeSetsOverlayNodeManagerTests(tier 1.5, 11 tests): the batch links acrossdocuments, the replacement takes the slot and the placeholder leaves the address space
with its references retargeted, an imported instance carries exactly the children its
document declares, and behaviour wired onto an imported Method answers like behaviour
wired onto a generated one.
Tiers 0, 1, 1.5, 1.7 and 2 pass for this sample; the full tier 1.5 (192) and tier 1.7
(422) suites pass; the solution builds with no new warnings.
Notes for the reviewer
Placeholder replacement needs the slot declared on the type. My first model design
declared
Station1/Station2on theSiteinstance ofua:BaseObjectType. Nogenerated state class means no slots, so
TryReplaceExplicitlyDefinedChildsaw an ordinarychild, refused to evict it, and the import added a second
Station1beside the first —silently, with no error anywhere.
SiteTypefixed it, and the README and the ModelDesigncomment both say so, because this is the trap a reader will hit first.
Import factories match Variables by TypeDefinition. The documents give
Throughput,StatusandTemperaturethe baseBaseDataVariableType, so they import as plainBaseDataVariableStates and the generatedStationState.Throughputproperty staysnull.Only the stations (
StationType) andStation3/Reset(MethodDeclarationId) are typed.The sample proves that typing rather than asserting it:
builder.Node<StationState>(...)answers
BadTypeMismatchotherwise, so a server which failed to materialize the typedstate would not start.
The two refusals. The
BadInvalidStatefor wiring a node the import displaces wasprovoked and is quoted verbatim in the README, message and server log line — it names the
configured descendant, not the replaced root, and the tier 1.5 readiness gate reports
only a
TaskCanceledException, so getting the text needs a standalone host. The otherrefusal (sealing with an unregistered import batch) is unreachable from a source-generated
manager, since the emitted partial always completes the batch; the README says where it
does apply rather than contriving a way to trigger it.
🤖 Generated with Claude Code