Add an Alias Names sample (OPC UA Part 17) - #849
Conversation
A server/client pair which demonstrates OPC 10000-17: how a server publishes
a searchable index of the tag names people actually use for its signals, and
how a client turns one of those names into the NodeId it stands for.
The address space is laid out by structure the way engineering documentation
describes a plant, and the tag list names the same signals the way the control
system does. Neither naming scheme can be dropped, and a client which knows
only the tag name cannot browse for it, because the name is nowhere in the
address space. ModelDesign.xml and PlantTags.cs are deliberately unrelated
files: that separation is what the sample is about.
The server publishes the index both ways Part 17 clause 9 allows, so the
client can put them side by side:
* a store registered with IAliasNameStoreRegistry, which makes the standard
well known TagVariables object (i=23479) answer FindAlias - a client needs
no prior knowledge of the server at all;
* an AliasNameNodeManager serving a browsable, nested PlantTags/{Reactor,
Boiler} category tree, which unlike the standard nodes can carry the
optional Methods FindAliasVerbose, AddAliasesToCategory,
DeleteAliasesFromCategory and LastChange.
The client browses the plant and names each node from the inventory using an
AliasNameResolver, searches a category by wildcard and reads the nodes it
resolved, and adds or deletes aliases at runtime. The mutation buttons stay
enabled for every account on purpose: the sample server grants SecurityAdmin
to one account, and seeing an anonymous session refused with
BadUserAccessDenied is as much the point as seeing an administrator succeed.
Two limits of the pinned SDK version are documented rather than worked around.
2.0.0-preview.2 does not ship the alias node materialization pass, so the
standard well known categories can only serve FindAlias and browsable
AliasNameType nodes (clause 6.2) are unavailable; the sample declares
AliasNameCapabilities.None on the standard descriptor and demonstrates the
optional Methods on the application defined categories instead.
Tier 1.5 tests cover the behaviour over a real session. One expectation is
recorded through the existing KnownIssue mechanism rather than asserted: a
change in a nested category advances that category's LastChange but not its
ancestors', contrary to clause 6.3.1, so a client watching only the root
misses it. The test is written the right way round and reports itself as
ignored until the stack fixes it.
Registered in the sample catalog, the server and client test factories, the
solution, the Run/Stop scripts, both READMEs and docs/TESTING.md.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
@copilot merge current master and bump sdk Version for the sample. Consder the changes made to the sdk since preview2 |
…dard-issue-822-186094 Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>
…packages Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>
…ixed known issue Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>
Done in Merge and bump.
What preview.2 could not do and preview.4 can. Both limits the PR description called out are gone, so the sample no longer documents them as gaps:
Verified with tier 0 (183/183) and a headless harness driving the real sample server on Linux; the WinForms tiers need the Windows runner, and the workflow on this branch is waiting for approval. |
Proposed changes
A server/client pair which demonstrates OPC 10000-17, Alias Names: how a server publishes a searchable index of the tag names people actually use for its signals, and how a client turns one of those names into the NodeId it stands for.
The address space is laid out by structure the way engineering documentation describes a plant (
Plant/Reactor/TemperatureMeasurement), and the tag list names the same signals the way the control system does (TIC101_PV). Neither naming scheme can be dropped: renaming the nodes would break every client which addresses the plant by structure, and a client which knows onlyTIC101_PVcannot browse for it, because the name is nowhere in the address space.ModelDesign.xmlandPlantTags.csare deliberately unrelated files — that separation is what the sample is about.The issue was a title and a link to
docs/AliasNames.md, so the shape below is a proposal; happy to adjust the scope.Server
Publishes the index both ways Part 17 §9 allows, so the client can put them side by side:
TagVariables(i=23479), fixed by the specPlantTags+ nestedReactor/Boiler, in the server's own namespaceIAliasNameStoreRegistryAliasNameNodeManagerOpenStandardTagVariablesknows the NodeIdAliasesobjectFindAliasonlyFindAlias,FindAliasVerbose,AddAliasesToCategory,DeleteAliasesFromCategory,LastChangeClient
Browses the plant and names each node from the inventory via an
AliasNameResolver(the reverse mapping), searches a category by wildcard, and reads the nodes it resolved — the "Resolves to" and "Value" columns are what prove the search answered with a usable address rather than a matching string.The mutation buttons stay enabled for every account on purpose. The server grants
SecurityAdminto one account (secadmin), and seeing an anonymous session refused withBadUserAccessDeniedis as much the point as seeing an administrator succeed. Searching needs no privilege — a tag list is not a secret.Related Issues
Types of changes
Checklist
Further comments
Two SDK limits documented rather than worked around
The doc the issue links to lives on UA-.NETStandard
master, which is ahead of the2.0.0-preview.2this repository pins. preview.2 ships the full client and serverAliasNamesAPI (including PubSub), but notDiagnosticsNodeManager.MaterializeRegisteredAliasNameNodesAsyncorAliasNameNodeManagerOptions.MaterializeAliasNodes— those appear around2.0.262. Consequences:FindAlias, since the published NodeSet instantiates no other Method node. The sample therefore declaresAliasNameCapabilities.Noneon its standard descriptor and demonstrates the optional Methods on the application-defined categories, which do get them.AliasNameTypenodes (§6.2, what the CTT browses for) are unavailable.I built against the pinned version rather than bumping the SDK, which felt well outside this issue. Both gaps are called out in the sample README, so they read as "not yet" rather than "not done". Happy to revisit if the repo is about to move to a newer preview.
One spec deviation found, recorded as a known issue
A change in a nested category advances that category's
LastChangebut not its ancestors', so a client watching only thePlantTagsroot does not see that a tag changed inReactor. Part 17 §6.3.1 asks for the ancestor to move as well (and the SDK doc claims it does).Rather than assert the broken behaviour, this uses the suite's existing
KnownIssuemechanism: the expectation is written the right way round and reports itself as ignored until the stack fixes it, at which point the test fails and asks for the note to be removed. Worth filing upstream if maintainers agree with the reading.Verification
FindBTNwas enabled, and disconnects cleanly.AliasNamesNodeManagerTests: 10 passed, 1 ignored (the known issue above). Full tier 1.5 suite still 131 passed, so no regressions elsewhere.Notes for reviewers
Three non-obvious things that shaped the code, all found by running the tests:
Create, whereserver.NamespaceUrisexists — not in the server constructor.AliasNameNodeManagersilently skips descriptors whose NodeId lies outside the namespace it owns.InMemoryAliasNameStoresearches a category together with its whole subtree. Each tag is seeded into its unit only; seeding the root as well returned every tag twice. A client which knows how the plant is divided narrows the search, one which does not asks the root.AliasNameVerboseDataTypecarries no reference type — it hasAliasNameCategoryIdandServerUris. A local target still yields one empty-stringServerUrisslot rather than an empty array.Not covered, and listed as such in the README
The well-known
Topicscategory (§9.4), aliases pointing into another server viaServerUri, a customIAliasNameStoreover a database or MES, theReferenceTypeFilterargument ofFindAlias, and all of Annex D (the PubSub schema for distributingLastChangebetween servers).🤖 Generated with Claude Code