mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-28 22:55:49 +08:00
aas
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
651beaee1e |
Fully support AliasNames in ConsoleReferenceServer (#4012) (#4321)
# Description The CTT AliasName conformance units skip or fail against the ConsoleReferenceServer. `AliasName Base` skips with *"No instance of AliasNameType has been found in the address space"*; `Category Tags`, `Category Topics` and `Hierarchy` fail. Part 17 support landed in #3779, but aliases live only inside the `IAliasNameStore` — nothing is browsable, no `AliasNameCategoryType` is nested under another, and the Topics aliases point at the Server object rather than at a `PublishedDataSetType` instance. Part 17 §6.2 clients — the CTT among them — discover aliases by browsing, not only by calling `FindAlias`. This PR materializes the store contents as real nodes and fills the three gaps. ### SDK — `Opc.Ua.Server` * **`DiagnosticsNodeManager.MaterializeRegisteredAliasNameNodesAsync`** (new, opt-in) walks every registered `IAliasNameStore` and creates one `AliasNameType` instance per alias — BrowseName carrying the alias name, `AliasFor` references to its targets, and the inverse `HasAlias` reference on each local target (via `externalReferences`, since targets usually live in another node manager) — plus an `AliasNameCategoryType` instance for every store category the standard NodeSet does not already ship. Categories whose NodeId belongs to another node manager are skipped, so an `AliasNameNodeManager` that also registers its store with the server-wide registry does not get its NodeIds claimed twice. Opt-in: servers that only need the well-known `FindAlias` to answer from their store pay nothing. The nodes are a snapshot taken at address-space creation — later mutations change what `FindAlias` returns but do not add or remove nodes. * **Optional Part 17 methods on the well-known categories.** The same pass instantiates whatever a category declares through `AliasNameCapabilities`: `FindAliasVerbose` (§6.3.3), `AddAliasesToCategory` (§6.3.4), `DeleteAliasesFromCategory` (§6.3.5) and `LastChange` (§6.3.1). Mutations require a `SecurityAdmin` caller on a `SignAndEncrypt` channel; the gate previously private to `AliasNameNodeManager` moved to `AliasNameMethodDispatcher` so both paths share one implementation. * **`LastChange`** is now tracked per category rather than only for the standard `Aliases` node, and the duplicated seeding logic collapsed into one helper. ### Reference server * A nested **`Devices`** category under `TagVariables` holds `Pump1_Status` and `Heater_Power`. `FindAlias` on `TagVariables` still returns all six names through subtree recursion, while `FindAlias` on `Devices` returns exactly two — which is what the Hierarchy unit checks. * `ReferenceServerConfigurationNodeManager` creates three **`PublishedDataSet` instances** under `PublishedDataSets` (`i=17371`) and the Topics aliases now target them, satisfying the *"is a Dataset"* check. These are address-space instances only — no PubSub runtime is added, and the CTT only inspects the target's type definition. ## Note for reviewers — the reserved NodeIds The optional children are created at the NodeIds the OPC Foundation reserves for them (`Aliases.FindAliasVerbose` = `i=24054`, `TagVariables.LastChange` = `i=32854`, …). Those identifiers are allocated in `StandardTypes.csv` but are **not** declared in the ModelDesign and **not** present in the published NodeSet, because the standard address space does not instantiate optional children. The source generator therefore emits no parent-to-instance mapping and no `MethodIds` constants for them, so `DiagnosticsNodeManager.ReservedChildIds` supplies the nine method ids and the two missing `LastChange` ids explicitly, each traceable to its registry row. The 18 argument-property ids do have generated `VariableIds` constants and are referenced through those. I deliberately did **not** "fix" this by declaring the children in `tools/Opc.Ua.SourceGeneration.Core/Design/StandardTypes.xml`: that file is a verbatim copy of upstream `UA-ModelCompiler/Design.v105/StandardTypes.xml` (the only differences today are upstream being newer), so a local edit is lost on the next sync and the NodeIds would silently revert to factory-minted ones. `docs/AliasNames.md` records this. Worth raising upstream separately: `StandardTypes.csv` allocates `Aliases_FindAliasVerbose` and siblings while the ModelDesign never declares those children, so the ModelCompiler cannot emit ids it has itself reserved. If that is ever reconciled, `ReservedChildIds` becomes redundant and deletes cleanly. ## Related Issues - Fixes #4012 ## 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. — **not done: verified on net8.0 only** (Server 4710, InformationModel 1099, Core 4443, plus the client and features alias suites; AOT build clean). Needs a run against net framework and net10 before this leaves draft. - [ ] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. - [ ] I have addressed **all** PR feedback received. ### Tests `tests/Opc.Ua.InformationModel.Tests/Aliasname*Tests.cs` mirror the four CTT conformance units and no longer `Assert.Ignore` where the CTT reported failures — the alias suite is 37 tests with no skips. New `AliasnameOptionalMethodsTests` covers the optional methods, their reserved NodeIds, the anonymous-caller denial, an add/find/delete round trip and `LastChange` advancing on mutation. One pre-existing test changed premise: `EnumerateSubCategoriesAsyncOnLeafReturnsEmptyAsync` asserted that `TagVariables` has no sub-categories, which this PR deliberately makes false. It now targets `Topics` (still a leaf) and a new test guards the nesting. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: romanett <7413710+romanett@users.noreply.github.com> Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com> |
||
|
|
d547747e22 |
Generate ReferenceNodeManager address space from a NodeSet2 model (#4220)
# Description The CTT/compliance `ReferenceNodeManager` built its ~6,000 node address space entirely in imperative C#. This PR migrates it to a source-generated `[NodeManager]` partial driven by an exported NodeSet2 model (`ReferenceServer.NodeSet2.xml`), mirroring the `MinimalBoilerServer` sample and completing the goal of issue #4204. The address space is now materialized from the model by the source generator. The large orchestration body was kept but converted from create-based to **find-instead-of-create**: each `Create*` primitive now looks up the model-materialized node via `FindPredefinedNode<T>` and re-applies runtime values, DataAccess metadata, and write handlers, rather than constructing nodes from scratch. This keeps the deterministic value seeding and behavior wiring intact while the structure comes from the model. ## Notable details for reviewers - **Idempotent reference wiring.** The mesh references in the `References` folder are already present in the model, so the historic manual `AddReference` calls would throw on duplicates. Added a small `AddReferenceIfMissing` helper so those calls are safe. - **Non-string NodeIds.** The `NodeIds` folder assigns numeric / GUID / opaque NodeIds. The model exports those nodes with their final ids, so find-by-string-path no longer works; added `CreateVariableById(NodeId)` to locate them by their real id. - **Generator bug fix (worth a careful look).** `ModelDesignExtensions.GetValueRankAsCode` reconstructed `ValueRank` as `dimensions.Length + 1`, so a node with `ArrayDimensions="3,3"` was emitted as `ValueRank = 3`. This affected **any** NodeSet2 with a fixed multi-dimensional array (rank-3 matrices, `BadTypeMismatch` on whole-matrix writes, wrong decoded dimensions). Corrected to `dimensions.Length` and updated the unit tests that had codified the off-by-one. ## Validation - `ReferenceServerTest`: 44/44 on net10.0 and net48 - `Opc.Ua.SourceGeneration.Core.Tests`: pass on net8.0/net9.0/net10.0/net48 - Integration on net10.0: ComplexTypes, Client, History, Sessions, Subscriptions all pass - Clean build, no new analyzer warnings ## Related Issues - Fixes #4204 ## Checklist _Put an `x` in the boxes that apply. You can complete these step by step after opening the PR._ - [ ] 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. - [ ] I have added all necessary documentation. - [x] I have verified that my changes do not introduce (new) build or analyzer warnings. - [x] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. - [ ] 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: Copilot App <223556219+Copilot@users.noreply.github.com> |
||
|
|
9375a2431b |
Retire the IServerInternal bind mutators and freeze the bind phase (#4197)
## Stacked on #4193 Base is `marcschier/server-context`, which is itself based on #4188. **Review #4188 and #4193 first.** GitHub retargets each base automatically as the stack merges. Closes #4184. ## What this does `IServerInternal` published twelve `Set*` binding methods plus `CreateServerObjectAsync`, so any holder of the interface could rewire a running server. This takes them off the interface and makes a late bind fail loudly. ## The audit changed the shape of the work The plan this came from said "design a frozen bind phase". **One already exists.** `StandardServer` has a seventeen-method `Create*` factory seam, and `DependencyInjectionStandardServer` overrides seven of those to resolve from the container: ```csharp protected override IRoleManager CreateRoleManager(IServerInternal server, ApplicationConfiguration configuration) => m_services.GetService<IRoleManager>() ?? base.CreateRoleManager(server, configuration); ``` `StandardServer` then carries each factory result into the datastore: ```csharp ServerInternal.SetRoleManager(CreateRoleManager(m_serverInternal, configuration)); ``` So an integrator already had two supported routes — override the virtual, or register in DI. `Set*` on the interface was a **third route that bypassed the ordering**, and the only one that let a caller rebind a running server. The job was therefore to stop publishing plumbing, not to build a phase. ## Measurement Every call site of the twelve mutators plus `CreateServerObjectAsync`: | Area | Count | Where | |---|---:|---| | `Opc.Ua.Server` | **11** | `StandardServer.cs:3729–3844`, one contiguous block | | samples | **1** | `ReferenceServer.cs:219`, inside a `CreateMasterNodeManager` override | | tests | 20 | `ServerInternalDataTests` (18), `SubscriptionTests` (2) — **all on the concrete `ServerInternalData`** | `SetIdentityRegistry` had **zero** call sites anywhere. The earlier estimate for this work was 268 files. That number counted files *referencing* `IServerInternal`, which this change never touches: the interface survives and only the mutators move off it. ## Commits ### `e719a88a` Add the missing `CreateUserManagement` factory seam The one gap, and the reason this had to land first. User management was the only subsystem with **no factory seam** — the only way to populate `IServerInternal.UserManagement` was `SetUserManagement`, which is what `ReferenceServer` called from inside a `CreateMasterNodeManager` override. Removing the mutator without a replacement would have broken that scenario with no migration path. The DI path at `OpcUaServerBuilderExtensions.cs:1178` builds a `UserNamePasswordAuthenticator` from a registered `IUserManagement` — a different thing entirely that never reaches the Part 18 §5 model on the address space. Purely additive. `ReferenceServer` moves onto the override. The test covers the **override** seam rather than the DI seam deliberately: registering an `IUserManagement` in the container also switches on the username/password authenticator, so a DI-based test exercises two unrelated paths and fails for reasons unrelated to binding. ### `cbcfda47` Remove `SetIdentityRegistry` Zero call sites, despite a doc comment advertising it as the integrator seam. The supported route has always been `ServerIdentityRegistryExtensions.RegisterDefaultAuthenticators`, which *adds* authenticators to the default registry rather than replacing it. `IdentityRegistry` is read five times outside `Opc.Ua.Server`, so the default is what every consumer already uses. **The analyzer confirmed the reading**: with the setter gone, `RCS1170` fired on the property — it is provably never reassigned after construction, so it is now read-only. ### `ea0937a2` Take the bind mutators off `IServerInternal` The mutators stay `public` on `ServerInternalData`, which owns the state. Only the interface loses them. The eleven call sites switch from the `ServerInternal` property to the `m_serverInternal` field, already typed `ServerInternalData?` — the same one-token change `GetStatus` and `EnterState` took in #4193. **No test changed.** All twenty test call sites already used the concrete type, which is the clearest evidence these were never interface-level API. `CS1574` found six doc comments across `IRoleManager`, `RoleManager`, `IUserManagement`, `UserManagement`, `IServerInternal` and `SessionManager` that pointed integrators at the mutators. They now point at the factory seam. ### `f3c4b9d3` Refuse to bind after the server has started Each bind goes through a guard; `StandardServer` closes the phase once the session manager is in place. A late `Set*` throws `ServiceResultException(BadInvalidState)` **naming the operation** and pointing at the factory seam — otherwise the failure is indistinguishable from a configuration problem. Rebinding *during* startup stays legal, which is what let both audit hazards resolve with no behaviour change: - **`SetNodeManager` also binds three more managers** (`DiagnosticsNodeManager`, `ConfigurationNodeManager`, `CoreNodeManager`). Intentional — they are reached through the master node manager and have no independent lifetime — and now documented rather than silent. - **`SetSessionManager` unhooks the previous manager's handlers**, which reads as written-for-repeat-calls. Nothing rebinds it, but the freeze does not make the path dead either, since binding twice during startup is still allowed. Left as-is; removing it is a separate question. ## Tests Four new tests on the freeze: the refusal itself across four representative mutators, that the message names the operation, that rebinding stays legal until the phase closes, and that closing twice is harmless. Plus one hosted-server test for the `CreateUserManagement` seam. | Suite | net10.0 | net48 | |---|---|---| | `Opc.Ua.Server.Tests` | 4033 passed, 1 failed | 230 passed, 1 failed (filtered) | | `Opc.Ua.Redundancy.Server.Tests` | 573 passed | — | | `Opc.Ua.Redundancy.Kubernetes.Tests` | 97 passed | n/a (net8/net10 only) | The single failure is `ConfigureApplicationBuildsSharedClientAndServerConfigurationAsync`, a local certificate-store problem verified to reproduce unchanged at HEAD with these changes stashed. Not caused by this PR. ## Docs `docs/MigrationGuide.md` gains a table mapping every removed mutator to the `Create*` override and the DI registration that replace it, plus the `SetIdentityRegistry` removal with its rationale. ## Net effect across #4193 and this PR `IServerInternal` goes from ~460 lines to **~200**. It no longer publishes locks, dead members, or the ability to rewire a running server. ## Reviewer questions - `CompleteBindPhase()` is public on `ServerInternalData` but deliberately **not** on `IServerInternal` — only the server owning the startup sequence should decide when binding is over. Reasonable, or should it be `internal`? - `SetSessionManager`'s unhook path is now unreachable in practice but still legal during startup. Leave it, or remove it and assert single-bind for that one subsystem? --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e9c13dd0-a6e3-4ad2-8ad6-1c3fe6323041 Copilot-Session: b3c0e146-5b9e-4d08-a0f6-db3f940484b1 Copilot-Session: d18f87e9-2484-44ce-98ce-463ba6cf5f63 |
||
|
|
537b10c8b3 |
Fix History User Access Levels and add CTT coverage nodes (#4026)
# Description Improve Reference Server coverage for OPC UA Compliance Test Tool scenarios and document the latest CTT findings. ## Reference Server coverage - Historize the configured AccessRights nodes and seed them with the same deterministic mixed-quality history as the other test nodes while preserving their access controls. - Add a historian node that intentionally does not support ServerTimestamp. - Add a SelectionListType String instance whose Selections property is String[] as required by Part 5, with localized descriptions and RestrictToList enforcement. - Add an EUR CurrencyUnit property and verify its structured value and binary encoding. - Add a node with explicit read/write base access bits plus NonatomicRead and NonatomicWrite AccessLevelEx flags. - Ensure the first ByteString array elements meet the CTT minimum-length prerequisite. ## Latest CTT findings - Confirm run-23 A&C residuals are CTT re-entrant Read and branch-unaware Retain checks rather than server regressions. - Confirm NumberOfTransitions includes Uncertain values as required by Part 13. - Document the CTT CurrencyUnit decoder dropping a correctly encoded Exponent. ## Related Issues - Follow-up to #3960 and #4002. ## 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. - [ ] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. - [x] I have addressed **all** PR feedback received. Focused affected tests pass on .NET 10 and .NET Framework 4.8. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Marc Schier <marcschier@hotmail.com> |
||
|
|
7cec2d614d | Move repository files to the consolidated layout |