mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-27 21:55:54 +08:00
data-channels-quic-experimental
312
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
72e4a83f77 |
Bind native WebTransport management through authenticated UASC
Keep the Part 32 Services baseline separate. Forward the same-session management provider through the admission lease, enforce protected MSG correlation and refuse Services on native management. Cover framing, encrypted composition, profile selection and discovery with 71 new regression cases. Document remaining wire and native/browser qualification gaps. |
||
|
|
41d4348afb | Merge master into data channel branch | ||
|
|
43bde775a2 | Pin Data Channels documentation to updated draft | ||
|
|
f6c2977033 | Separate context and transport initiator roles | ||
|
|
609a535033 | Route configured UADP discovery requests | ||
|
|
80c16f32cd | Reject invalid UADP discovery types | ||
|
|
482cd34c89 | Add UADP discovery subtype routing | ||
|
|
c9d910bb71 | Reject ambiguous secured discovery routes | ||
|
|
42b2986224 | Cover publisher-only UADP discovery routing | ||
|
|
2855b3f25a | Correct peer STOP implementation status | ||
|
|
0dfae76f41 | Add host-dispatched UAFX endpoint lifecycle | ||
|
|
1d6db20b1d | Implement Data Channel PubSub and UAFX foundations | ||
|
|
3c5aa421d3 |
Client: server redundancy over Reverse Connect member connections (#4885)
# Description ## Depends on This pull request depends on #4883 (application-supplied reverse connection sources) and includes its commits. #4884 (ServiceLevel-driven failover, member probing and read statuses) is already merged into master. Current master and the concurrent dependency merges are preserved without rewriting branch history. Review the complete PR diff, not only the last commit. Master's ManagedSession logging event IDs 32-35 are preserved; unused reverse-connection cleanup uses event ID 36. ## Problem With Reverse Connect, each member of a non-transparent redundant Server set opens its own connections to the Client. The existing redundancy path assumed dialed connections for endpoint resolution, ServiceLevel probes and recovery, preventing a Client from using the members' incoming connections consistently. ## Change - `IReverseConnectionAvailability : IReverseConnectionSource` exposes `GetAvailableEndpointUrls(string serverUri)`. `ReverseConnectManager` advertises unaccepted held connections and excludes a connection as soon as it is accepted, including inside the accepting callback. - Optional `ServerRedundancyOptions.MemberConnections` selects the reverse-connect member resolver. Endpoint matching preserves the ServerUri, security mode, security policy and URL scheme checks. - Member probing, failover and subsequent recovery use the member source, including for a custom resolver or explicit target whose endpoint does not set `ReverseConnect.Enabled`. - The initial Server retains its authenticated endpoint and original source, manager or outbound transport. A-to-B-to-A failover uses that original path consistently for resolution, availability, probing and recovery. - Switching to another member waits for at least `PeerDiscoveryTimeout`, including after the previous reconnect budget expires. The reverse-source dependency's cancellation and exhausted-budget handling are preserved. - Without `MemberConnections`, existing dialed member behavior remains unchanged. ## Tests The real reference-server fixture covers source, manager and outbound initial connections, each with marked and unmarked member endpoints. All six combinations exercise member failover, transport closure and member recovery, then handler-selected failback to the initial Server with probing enabled. They assert readable ServiceLevel, fresh member connection requests and no initial-Server waits through the member source. The manager lifecycle regression asserts that a held connection is available before acceptance and unavailable inside the accepting callback. Existing resolver, handler, reactive failover, unavailable-member and reverse-source budget/cancellation tests remain enabled. ## Repair Evidence, 2026-10-23 Local Windows developer checks with .NET SDK 10.0.402, Release/net10.0, before the later concurrent master integration: - Initial-source/member routing: 6 passed. - Combined dependency budgets, routing and accepted availability: 15 passed. - Affected client redundancy, resolver, handler and manager lifecycle suites: 198 passed. - Every listed run completed with 0 build warnings and 0 build errors. - The dependency's WoT retained-input fixture repair passed the original case and both injected-conflict cases (3 tests), plus 15 neighboring retained-input and retry-boundary cases. It checks exactly one correct input per bounded publication attempt and retains the exact-one cold-recovery assertion. No production WoT behavior is changed by that repair. The newly integrated baseline is being checked separately; the PR comments and GitHub checks report its results. These prior results do not claim completion of current-head full CI. ## Earlier Evidence The original PR description recorded these results for historical head `dc471de5d`, not the current head: client builds on net10.0/net9.0/net8.0/net48 with no warnings or errors; affected classes 107/107 on net10.0 and net48; Sessions 80/80 on net10.0; the full Client project 3311 executed and passed out of 3315 total; changed-file whitespace/style/analyzer checks clean. Those historical checks have not been relabeled as current-head evidence. ## Operational Notes ServiceLevel probing consumes member connections. Members must continue opening connections while they are probed and recovered. A previously resolved member remains selectable while its probes obtain connections; a member whose probe cannot obtain a connection and reports none available becomes unavailable. The initial Server's original transport is not subject to the separate member-source availability filter. ## Checklist - [x] I have signed the CLA and read CONTRIBUTING. - [x] Regression tests cover the repaired behavior. - [x] Reverse Connect documentation describes initial-source failback, unmarked endpoints and accepted-connection availability. - [ ] Current-head full solution tests on .NET Framework and .NET 10 have completed successfully. - [ ] All required current-head CI checks and CodeQL checks have passed. - [ ] All review threads have been addressed and reviewed. --------- Co-authored-by: Marc Schier <marcschier@users.noreply.github.com> |
||
|
|
53e9e25717 |
Add explicit published WoT Binding 1.00 consumer interoperability (#4892)
# Description Implement the approved published OPC 10101 v1.00 consumer- Baseline: [OPC 10101 v1.00, published 2026-01-08](https://reference.opcfoundation.org/specs/OPC-10101/v1.00). Reviewed plaintext SHA-256: `4ec1170c1c33c63907e7810e6ab0537d62b55735bc2d1191ec7f57bbf9926d93`. Later draft vocabulary is not normative 1.00 evidence. - Explicit `WotBindingProcessingMode.PublishedOneZero` in planner/extractor and MCP options. Current remains the default; no heuristic selection from vocabulary/revision. Original TDs need no invented revision annotation. - Published numeric namespaces use original effective document context, not the connected NamespaceArray or unrelated later planning context. - Actions require action-level array-valued `uav:componentOf` (§7.1.3). Native Core ownership/endpoint checks select one valid Object receiver; a valid explicit form receiver can disambiguate. No retired scalar form fallback. - Native `InputArguments`/`OutputArguments` establish object-schema argument ordering, DataTypes and ranks without later `argumentLayout`/`fieldOrder` or JSON-order guesses. Native integer widths constrain actual values too. - Shared Method/receiver resolution pins the same pair for MCP authorization and dispatch. Direct/captured channels retain preflight/session checks. - Planning uses the live source-routed resolver document alongside local declarations from NodeSet import; serialized `resolvedFrom` markers and local containment cannot manufacture source ownership authority. Read/write/observe/default operations, property mapping annotations, exact channel/auth/allOf constraints and external token-provider applicability have version-qualified evidence. Documentation distinguishes converter identifier options from runtime processing selection. The [25-requirement traceability ledger](https://github.com/OPCFoundation/UA-.NETStandard/blob/a24ed5319f099980e9e67d536c4268de24d3ae49/tests/Opc.Ua.Types.Tests/Wot/Assets/wot-binding-traceability.json) includes concrete tests, assertion-level evidence and qualifications. Locator completeness is not implementation completeness. ## Related Issues - Approved interoperability follow-up to merged #4877; differential excludes its safe receiver/provenance implementation. - #4869 remains outside scope and is not closed. ## 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. - [ ] 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. - [ ] 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. - [ ] I have addressed **all** PR feedback received. |
||
|
|
d3004c4084 | Validate complete UAFX configuration before flow creation | ||
|
|
f9c2ddf142 | Add pinned UAFX Part 81 generated model contracts | ||
|
|
9ce8192ebe | Cover UADP source isolation and rollover and align Data Channel guides | ||
|
|
c5718a0b40 | Merge master preserving Data Channel routing and metadata isolation | ||
|
|
cd1b3bcaa2 | Implement revised Data Channel bindings and address PR feedback | ||
|
|
f3980f9e72 |
Client: managed session recovery over application-supplied reverse connections (#4883)
# Description
## Problem
With Reverse Connect (OPC 10000-6, 7.1.3) the server opens the
transport connection and sends `ReverseHello`; the client never dials.
To
re-establish a lost session (OPC 10000-4, 6.7: a new SecureChannel, then
`ActivateSession` for the existing session, then republish or
`TransferSubscriptions`) the client therefore needs a fresh
server-initiated
connection for every recovery attempt.
A `ManagedSession` can obtain such connections only from a
`ReverseConnectManager`:
- The `ITransportWaitingConnection` passed to
`ManagedSession.CreateAsync` is
single-use and consumed by the initial connect. Without a manager every
later
recovery fails with `BadSecureChannelClosed` ("A fresh reverse
connection is
required.") until the reconnect policy gives up.
- Recovery calls `ReverseConnectManager.WaitForConnectionAsync`
directly. The
manager owns its listeners and has no extension point, so connections an
application accepts itself cannot be fed into it.
Applications that accept reverse connections themselves cannot use the
managed
recovery today. Typical reasons are admission decisions before the OPC
UA
handshake (source network, the server URI and endpoint URL from
`ReverseHello`), a bounded number of held connections per server, or
connections accepted by another process and handed over. Such an
application
has to drive `ManagedSession.ReconnectAsync(ITransportWaitingConnection,
...)`
itself and install a reconnect policy that refuses every attempt of the
managed
loop, which duplicates the backoff and budget of the managed loop and
races its
keep-alive based failure detection.
## API
```csharp
namespace Opc.Ua.Client
{
public interface IReverseConnectionSource
{
ValueTask<ITransportWaitingConnection> WaitForConnectionAsync(
Uri endpointUrl, string? serverUri, CancellationToken ct);
}
public class ReverseConnectManager : IDisposable, IAsyncDisposable, IReverseConnectionSource
{
// explicit implementation; delegates to WaitForConnectionAsync(Uri, string?, CancellationToken)
}
public sealed record ManagedSessionOptions
{
public IReverseConnectionSource? ReverseConnectionSource { get; init; }
}
public sealed class ManagedSessionBuilder
{
public ManagedSessionBuilder UseReverseConnect(IReverseConnectionSource source, Uri serverUri);
}
}
```
## Behaviour
1. Initial connect: an explicit initial `connection` argument is still
used
first. Otherwise the session awaits the source (after the connect gate
admitted the attempt, like the manager path). When the endpoint is
refreshed
before connecting, discovery runs over one connection from the source
and the
session over the next one, as with a manager.
2. Recovery: every place that awaits the manager during recovery
(reconnect,
in-place recreate, network-redundancy alternate, endpoint refresh,
explicit
`ReconnectAsync` without a connection, mirrored-session reactivation)
awaits
the source instead. A recovery attempt that runs under the reconnect
budget
waits at most for the remaining `MaxTotalReconnectTime`; when the budget
runs
out the source's token is cancelled and the attempt fails with
`BadTimeout`,
so the state machine leaves `Reconnecting` as it does after a failed
dial. A
source that throws ends that attempt like a failed dial; the next
attempt
follows the policy's delay.
3. Precedence: `ManagedSession.CreateAsync(options, ...,
reverseConnectManager)`
with `options.ReverseConnectionSource` set throws `ArgumentException`.
The two
`UseReverseConnect` builder overloads replace each other.
4. Ownership: the session closes a connection it received after the wait
was
cancelled (closing the session, disposal, budget exhausted), so a source
does
not have to track abandoned connections. A source that returns `null`
fails
the attempt with `BadUnexpectedError` instead of falling back to an
outbound
connection.
5. Sessions without the option behave as before; the manager paths are
unchanged (the manager's initial connect still goes through the session
factory's manager overload).
## Tests
`tests/Opc.Ua.Client.Tests/Session/ManagedSessionReverseConnectionSourceTests.cs`
(unit, no server):
- `CreateAsyncRejectsASourceCombinedWithAManagerAsync`
- `BuilderUseReverseConnectWithSourceSetsTheSourceAndServerUri`
- `BuilderUseReverseConnectWithAManagerReplacesTheSourceAsync`
- `BuilderUseReverseConnectRejectsNullArguments`
- `ReverseConnectManagerWaitsThroughTheSourceInterfaceAsync`
- `CancelledConnectCancelsTheSourceAndClosesALateConnectionAsync`
`tests/Opc.Ua.Sessions.Tests/ManagedSessionReverseConnectionSourceTests.cs`
(in-process reference server that keeps dialing the client's reverse
connect
listener; the source wraps that listener's `ReverseConnectManager` and
records
every call):
- `InitialConnectAwaitsTheSourceOnceAsync`
- `RecoveryReactivatesTheSessionOverAConnectionFromTheSourceAsync`:
closes the
transport under the SecureChannel; the session reactivates the same
`SessionId` over a new connection from the source, one source call per
attempt, and the subscription continues with higher sequence numbers
(not
recreated).
- `EachRecoveryAttemptAwaitsTheSourceOnceFollowingThePolicyAsync`: the
source
fails the first two recovery calls; exactly four calls, spaced by the
policy's delay.
- `ASourceThatNeverYieldsEndsRecoveryWhenTheBudgetIsExhaustedAsync`: one
wait
is cancelled when the 3 s budget is used up and the session reaches
`Disconnected`.
- `DisposingWhileRecoveryWaitsCancelsTheSourceAsync`
- `ReverseConnectManagerRecoveryIsUnchangedAsync`: regression for the
manager
path.
## Compatibility
Additive only: a new interface, a new init-only option, a new builder
overload
and an explicit interface implementation on `ReverseConnectManager`. No
existing signature changes. `IManagedSessionFactory` is unchanged;
callers of
`ConnectAsync(endpoint, configure)` can call `UseReverseConnect(source,
...)` in
`configure`.
## Related Issues
- No tracking issue; the problem and the design are described above.
## 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. (Run
locally: the affected test classes on net48 and net10.0 and the whole
Opc.Ua.Client.Tests project on net10.0; not the whole UA.slnx.)
- [ ] 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>
|
||
|
|
5d6c10a6e9 |
Align WoT projections with Binding and Connectivity specs (#4877)
# Description Align WoT Binding, Connectivity, and required Projections support with the merged official specification baseline. Projection-local affordances use ordinary TD/TM conversion and Binding planning. The canonical View retains its identity and organizes converter-resolved local/imported Nodes without cloning imported Nodes. Generated local identities are resource-scoped; authored identities are preserved. Security/schema closure retains projection ownership. Counts include local Binding-owned Nodes plus View-owned Nodes across dry-run, commit, unchanged refresh and restart. Runtime configuration forwards the converter-resolved active-root namespace for multi-model closures. Recovery rejects absent/inconsistent committed canonical View evidence rather than resetting publication. Local+imported recovery preserves committed membership/digest when desired inputs have changed. Fixtures use short isolated storage roots for net48/MAX_PATH. Security names use exact UTF-8 percent encoding; dependency-support names retain base64url. OPC UA forms with omitted content type select the registered octet-stream codec. Historical fixture bytes remain unchanged with explicit current derived expectations. UA Binary serialization remains unsupported/unclaimed: the raw-byte/text codec is not UA Binary. Dependency observation disclosure checks management policy, source group/exact-Version and every captured local target origin/group/exact-Version. Existing native role/channel-restriction validation is reused, including namespace defaults and the virtual role hook. Denied/unavailable targets reject the entire value with generic access failure, no partial graph or target-identity diagnostics. A pruned target makes remote disclosure fail closed without erasing the stored committed graph. Both committed snapshots and completed attempts are tested. Explicit-node tests do not establish separate namespace-default evidence. The current capture path generates local registry pins, not arbitrary federated/provider targets; remote authorization is outside this implemented capability and is not claimed. ## Related Issues - Related to #4869 only historically. Performance work is explicitly out of current scope; this PR does not fix or close it. ## 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. - [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. - [ ] I have addressed **all** PR feedback received. |
||
|
|
533937830b |
Restore UaLens Linux rendering (#4891)
# Description Restore the Linux rendering checks temporarily excluded in #4822 by updating all eight OpenUSD pins to the public **0.16.6-alpha** release. The upstream fix keeps shared GPU surfaces where supported. When Avalonia/Mesa cannot import external images or semaphores, it presents completed Vulkan frames through host-memory readback instead. The renderer, scene, and acceptance criteria remain unchanged. - Update the complete OpenUSD package family and its verified-version assertion together. - Restore both live embedded-viewport workflow cases, the native workflow smoke case, and the native viewport artifact case used by both distribution jobs. - Remove the temporary-exclusion notices and document the host-memory presentation path and physical-GPU qualification limits. All 21 resolved OpenUSD packages were independently verified on nuget.org at source commit `0ebd61af4651457d903ed30f40965e8dcb060255`, including the Metal and native RID graph. No private feed or replaced DLLs are used. [Upstream release](https://github.com/marcschier/openusd-dotnet/releases/tag/v0.16.6-alpha). ### Validation The eight-file patch was carried unchanged onto `master` at `97cc145811bc2d9cef11e685392696435b10df36`. The following Windows/.NET 10 checks were rerun on that base: | Scope | Result | | --- | --- | | Live viewport and diagnostic workflows | Passed: 14, Failed: 0, Skipped: 0 | | OpenUSD package-version policy | Passed: 1, Failed: 0, Skipped: 0 | | Viewport and rendered Vision adapters | Passed: 103, Failed: 0, Skipped: 0 | | Retained-camera provider and blank-frame guards | Passed: 45, Failed: 0, Skipped: 0 | <details> <summary>Windows commands and results</summary> These options and reporting paths were used for each invocation: ```powershell $options = @( '-c', 'Release', '-f', 'net10.0', '-p:CustomTestTarget=net10.0', '-p:UseArtifactsOutput=true', '-p:ArtifactsPath=D:\git\UA-.NETStandard_master\TestResults\ua-stack-feedback20261016', '-m:2' ) $results = 'C:\Users\mschier\.copilot\session-state\abd33a2e-7139-44e1-9912-6e4451102b4e\files\openusd0166-pr-results' # Passed: 14, Failed: 0, Skipped: 0 dotnet test tests\Opc.Ua.Lens.Workflow.Tests\Opc.Ua.Lens.Workflow.Tests.csproj @options --filter 'FullyQualifiedName~OpenUsdViewportWindowTests|FullyQualifiedName~DiagnosticCommandTests' --logger 'trx;LogFileName=workflow.trx' --results-directory $results # Passed: 1, Failed: 0, Skipped: 0 dotnet test tests\Opc.Ua.Tools.Tests\Opc.Ua.Tools.Tests.csproj @options --filter 'FullyQualifiedName~OpenUsdPackageFamilyUsesOneVerifiedPrerelease' --logger 'trx;LogFileName=policy.trx' --results-directory $results # Passed: 103, Failed: 0, Skipped: 0 dotnet test tests\Opc.Ua.Lens.Tests\Opc.Ua.Lens.Tests.csproj @options --filter 'FullyQualifiedName~OpenUsdViewport|FullyQualifiedName~RenderedVisionCaptureTests' --logger 'trx;LogFileName=adapters.trx' --results-directory $results # Passed: 45, Failed: 0, Skipped: 0 dotnet test tests\Opc.Ua.Vision.Tests\Opc.Ua.Vision.Tests.csproj @options --filter 'FullyQualifiedName~RetainedSceneCameraCaptureProviderTests|FullyQualifiedName~BlankFrameGuardTests' --logger 'trx;LogFileName=capture.trx' --results-directory $results ``` The selected builds completed without new compiler/analyzer warnings. `git diff --check` and the following formatting check passed: ```powershell dotnet format whitespace --folder --include tests\Opc.Ua.Tools.Tests\PackageVersionPolicyTests.cs tests\Opc.Ua.Lens.Artifacts.Tests\PublishedArtifactTests.cs tests\Opc.Ua.Lens.Workflow.Tests\Companions\OpenUsdViewportWindowTests.cs tests\Opc.Ua.Lens.Workflow.Tests\Desktop\DiagnosticCommandTests.cs --verify-no-changes --verbosity quiet ``` </details> The same patch was qualified before the base update on Ubuntu 24.04.5, Mesa 25.2.8, and Xvfb, with no GPU device mounted: | Scope | Result | | --- | --- | | Original live telemetry, picking, retained-camera capture, edit, resize, and drain workflows | Passed: 14, Failed: 0, Skipped: 0 | | Published Linux NativeAOT startup and native viewport | Passed: 2, Failed: 0, Skipped: 0 | | Freshly packed and installed Linux managed tool startup and native viewport | Passed: 2, Failed: 0, Skipped: 0 | Both artifact forms produced nonblank 256x192 Vulkan captures with different decoded-pixel hashes after the scene edit, exit code zero, and both required cleanup/smoke markers. No assertions, deadlines, or workflow selectors were weakened. <details> <summary>Linux commands and results before the base update</summary> The source was mounted read-only at `/repo`; isolated build outputs were under `/work`, and retained results were under `/evidence`. These commands ran in PowerShell inside the Ubuntu container: ```powershell $options = @('-c', 'Release', '-f', 'net10.0', '-p:CustomTestTarget=net10.0', '-p:UseArtifactsOutput=true', '-p:ArtifactsPath=/work/build', '-m:2') $env:LD_LIBRARY_PATH = '/work/build/bin/Opc.Ua.Lens.Workflow.Tests/release_net10.0:/work/build/bin/Opc.Ua.Lens.Workflow.Tests/release_net10.0/runtimes/linux-x64/native' # Passed: 14, Failed: 0, Skipped: 0 xvfb-run --auto-servernum '--server-args=-screen 0 1600x1000x24 -dpi 96' dotnet test /repo/tests/Opc.Ua.Lens.Workflow.Tests/Opc.Ua.Lens.Workflow.Tests.csproj @options --filter 'FullyQualifiedName~OpenUsdViewportWindowTests|FullyQualifiedName~DiagnosticCommandTests' --logger 'trx;LogFileName=workflow-linux.trx' --results-directory /evidence/results -- NUnit.NumberOfTestWorkers=0 RunConfiguration.TreatNoTestsAsError=true # Passed: NativeAOT publish, with trim/AOT diagnostics enabled dotnet publish /repo/tools/Opc.Ua.Lens/Opc.Ua.Lens.csproj -c Release -f net10.0 -p:CustomTestTarget=net10.0 -p:UseArtifactsOutput=true -p:ArtifactsPath=/work/aotbuild -r linux-x64 --self-contained true -p:PackAsTool=false -p:PublishAotEnabled=true -p:SuppressTrimAnalysisWarnings=false -p:TrimmerSingleWarn=false -m:2 -o /work/native -bl:/evidence/native.binlog $env:UALENS_ARTIFACT_EXECUTABLE = '/work/native/UaLens' $env:UALENS_ARTIFACT_LOG_DIRECTORY = '/evidence/native-linux' $env:UALENS_ARTIFACT_X11_VULKAN = 'true' $env:UALENS_ARTIFACT_ALLOW_SOFTWARE_GL = 'false' # Passed: 2, Failed: 0, Skipped: 0 xvfb-run --auto-servernum '--server-args=-screen 0 1600x1000x24 -dpi 96' dotnet test /repo/tests/Opc.Ua.Lens.Artifacts.Tests/Opc.Ua.Lens.Artifacts.Tests.csproj @options --filter 'FullyQualifiedName~PublishedArtifactTests' --logger 'trx;LogFileName=native-linux.trx' --results-directory /evidence/results -- NUnit.NumberOfTestWorkers=0 RunConfiguration.TreatNoTestsAsError=true # Passed: restore, pack the selector and all three RID payloads, and isolated install $pack = @('-p:CustomTestTarget=net10.0', '-p:UseArtifactsOutput=true', '-p:ArtifactsPath=/work/managedbuild', '-p:PublishAotEnabled=false', '-p:PublishAot=false', '-p:PublishTrimmed=false', '-p:PackAsTool=true') dotnet restore /repo/tools/Opc.Ua.Lens/Opc.Ua.Lens.csproj -p:Configuration=Release @pack dotnet pack /repo/tools/Opc.Ua.Lens/Opc.Ua.Lens.csproj -c Release --no-restore -p:TargetFramework=net10.0 @pack -m:2 -o /work/managed-packages -bl:/evidence/managed.binlog dotnet tool install OPCFoundation.NetStandard.Opc.Ua.Lens --version 2.0.2-preview.100.g30f255be63 --tool-path /work/installed/tool --configfile /work/installed/NuGet.config --no-cache $env:UALENS_ARTIFACT_EXECUTABLE = '/work/installed/tool/ualens' $env:UALENS_ARTIFACT_LOG_DIRECTORY = '/evidence/installed-linux' # Passed: 2, Failed: 0, Skipped: 0 xvfb-run --auto-servernum '--server-args=-screen 0 1600x1000x24 -dpi 96' dotnet test /repo/tests/Opc.Ua.Lens.Artifacts.Tests/Opc.Ua.Lens.Artifacts.Tests.csproj @options --filter 'FullyQualifiedName~PublishedArtifactTests' --logger 'trx;LogFileName=installed-linux.trx' --results-directory /evidence/results -- NUnit.NumberOfTestWorkers=0 RunConfiguration.TreatNoTestsAsError=true ``` The install configuration contained only the freshly packed `/work/managed-packages` source; installation used an isolated tool cache. The Linux SDK was 10.0.401, allowed by the repository's minimum-SDK policy. </details> **Not run:** full `UA.slnx` suites on .NET Framework and .NET 10, physical-GPU/display qualification, or downstream macOS/Windows NativeAOT artifacts for this follow-up. Linux artifact results above predate the move onto current `master`; the PR's CI must qualify the final head. Coverage was not recollected, and no solution-wide or current-head cross-platform CI pass is claimed. ## Related Issues - Fixes #4890. - Follow-up to #4822. No new application architecture or public UA API is introduced; the compatibility repair is in the released OpenUSD dependency. ## Checklist - [ ] 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. - [ ] 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. - [ ] I have addressed **all** PR feedback received. The existing rendering tests are restored, not replaced with weaker cases. The remaining checklist items are not asserted by the focused local evidence above. |
||
|
|
be9ccf912c |
Client: evaluate the durable subscription lifetime on every physical create (#4882)
# Description
## Problem
`ISubscription.SetAsDurableAsync(TimeSpan lifetime, CancellationToken
ct)` marks a subscription durable with the Server method
`SetSubscriptionDurable` (OPC 10000-5, "SetSubscriptionDurable Method";
OPC 10000-4, "Durable Subscriptions"). `SetSubscriptionDurable` must be
called before the subscription has monitored items; a conforming server
rejects it afterwards with `Bad_InvalidState`.
`LogicalSubscription` stores the `TimeSpan` and installs an
`OnAfterCreateAsync` hook on each partition. The partition state machine
runs the hook after every `CreateSubscription` and before the first
`CreateMonitoredItems`, so durability survives every later physical
create:
- recreate after a service call reports `BadSubscriptionIdInvalid`
(`Subscription.ApplyStateChangesAsync` → `ResetToRecreateAsync`),
- recreate after `TransferSubscriptions` fails on session recreate
(`SubscriptionManager.RecreateSubscriptionsAsync` → `RecreateAsync`) or
on load (`RestoreTransferAsync` → `ResetToRecreateAsync`),
- recreate after a status-change timeout or an unsolicited
`Good_SubscriptionTransferred`,
- a new secondary partition (`TryApplyDurableToNewPartition`).
The hook always re-applies the lifetime fixed by the first call. An
application whose durable lifetime must not exceed a bound that shrinks
over time (for example an authorization or retention window that ends at
a known instant, after which the server must no longer queue
notifications) cannot keep that bound after a recreate, and it has no
way to supply a new value at the right moment:
- calling `SetAsDurableAsync` again after the recreate is rejected once
monitored items exist;
- `ISubscriptionNotificationHandler.OnSubscriptionStateChangedAsync` is
dispatched fire-and-forget, so a call made from it can arrive after the
first `CreateMonitoredItems`;
- `SubscriptionOptions` has no durable setting and `OnAfterCreateAsync`
is internal.
## API
```csharp
namespace Opc.Ua.Client.Subscriptions
{
public interface ISubscription
{
// existing
ValueTask<TimeSpan> SetAsDurableAsync(TimeSpan lifetime, CancellationToken ct = default);
// new
ValueTask<TimeSpan> SetAsDurableAsync(Func<TimeSpan?> lifetime, CancellationToken ct = default);
}
}
```
## Behaviour
- The provider is evaluated once for the call itself; the value is sent
to every partition that is already created (unchanged: the call returns
the minimum revised lifetime of those partitions, or the requested value
when none is created yet).
- The provider is stored and evaluated again by the existing
after-create hook before every later physical create, between
`CreateSubscription` and the first `CreateMonitoredItems` of that
create.
- A `null` result requests no durability for that create: no
`SetSubscriptionDurable` is sent and the recreated subscription is a
regular one. A `null` result for the call itself returns `TimeSpan.Zero`
without contacting the server.
- An exception thrown by the provider during a later create is handled
exactly like a rejected `SetSubscriptionDurable` call today: the state
machine logs a warning, reports `SubscriptionState.Modified`, and the
subscription continues (its monitored items are created). An exception
during the call itself propagates to the caller.
- `SetAsDurableAsync(TimeSpan)` now forwards to `SetAsDurableAsync(() =>
lifetime)`; its behaviour is unchanged.
- `docs/Subscriptions.md` describes the provider and corrects the
statement that the hook is cleared after invocation (on master it stays
installed).
Not included: a notification reporting the outcome (revised lifetime or
status) of each re-applied `SetSubscriptionDurable`. Today a failed
re-apply is only logged and surfaced as `SubscriptionState.Modified`;
this could be a follow-up if maintainers want it.
## Tests
New tests in
`tests/Opc.Ua.Client.Tests/Subscription/SubscriptionTests.DurableLifetime.cs`
drive a real partition (`Subscription`) behind
`SubscriptionManager`/`LogicalSubscription` against mocked services and
record `CreateSubscription`, `SetSubscriptionDurable` (requested hours),
`CreateMonitoredItems`, `TransferSubscriptions` and
`DeleteSubscriptions` in call order. The provider returns 10 h for the
initial call and 7 h afterwards:
-
`DurableLifetimeProviderIsEvaluatedOnRecreateAfterSubscriptionIdInvalidAsync`:
`ModifySubscription` returns `BadSubscriptionIdInvalid`; the recreate
sends 7 h between `CreateSubscription` and the first
`CreateMonitoredItems`.
-
`DurableLifetimeProviderIsEvaluatedOnRecreateAfterFailedTransferAsync`:
`TransferSubscriptions` on session recreate returns
`BadSubscriptionIdInvalid`; delete, create, 7 h, items.
- `DurableLifetimeProviderIsEvaluatedForNewPartitionAsync`:
`MaxMonitoredItemsPerPartition = 1`; the second item mints a partition
that sends 7 h before its items.
-
`DurableLifetimeProviderReturningNullSendsNoSetSubscriptionDurableAsync`:
provider returns `null` on recreate; no `SetSubscriptionDurable`, items
are created.
-
`DurableLifetimeProviderThatThrowsLeavesRecreatedSubscriptionRunningAsync`:
provider throws on recreate; no `SetSubscriptionDurable`, items are
created, subscription stays created.
- `DurableFixedLifetimeIsReappliedOnRecreateAsync`: regression of the
`TimeSpan` overload (10 h again on recreate).
Unit tests in `LogicalSubscriptionTests`:
`SetAsDurableWithProviderEvaluatesItOnceForAllCreatedPartitionsAsync`,
`SetAsDurableWithProviderReturningNullSendsNothingAsync`,
`SetAsDurableWithNullProviderThrowsAsync`.
Negative control: with the hook evaluating the provider once at
installation (the previous fixed-value behaviour), the
recreate-after-`BadSubscriptionIdInvalid`, failed-transfer, null and
throwing-provider tests fail (4 of 10).
Local results (Windows, .NET SDK 10.0.402, Release):
| Run | Result |
| --- | --- |
| `Opc.Ua.Client` build, net48/net8.0/net9.0/net10.0 | 0 warnings, 0
errors |
| `SubscriptionTests` + `LogicalSubscriptionTests`, net10.0 | 105/105
passed |
| `SubscriptionTests` + `LogicalSubscriptionTests`, net48 | 105/105
passed |
| `Opc.Ua.Client.Tests`, namespace `Opc.Ua.Client.Subscriptions`,
net10.0 | 353/353 passed |
| `Opc.Ua.Subscriptions.Tests` `SubscriptionManagerCoverageTests` +
`StreamingSubscriptionCoverageTests`, net10.0 | 72/72 passed |
| `Opc.Ua.Subscriptions.Durable.Tests` `SubscriptionDurableTests` +
`DurableSubscriptionTest*` (in-process reference server with durable
subscriptions enabled), net10.0 | 21/21 passed |
## Related observation (not changed here)
A secondary partition minted by `SubscriptionManager.Add`'s partition
factory starts its state machine in the `Subscription` constructor,
before the factory calls `TryApplyDurableToNewPartition`. When
`CreateSubscription` completes before the factory returns (observed with
a synchronously completing mock), `RunAfterCreateHookAsync` finds no
hook and the new partition is created without `SetSubscriptionDurable`,
for the `TimeSpan` overload as well. With a real round trip the hook is
installed first;
`DurableLifetimeProviderIsEvaluatedForNewPartitionAsync` therefore holds
the secondary's `CreateSubscription` response until the factory has
returned. Installing the hook before the partition's first state-manager
pass would remove the window.
## Compatibility
- Source and binary compatible for callers of `ISubscription`.
- `ISubscription` gains a member. Custom implementations of the public
interface (outside the stack; the stack's own implementations are
internal) must add the overload; a default interface implementation is
not possible because the library also targets .NET Framework 4.8. The
three test doubles in the repository were updated.
- No wire, configuration or server change.
## Related Issues
- Independent of #4881; with #4881 the lifetime provider is also applied
to a new partition that is created before the partition factory returns.
## 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. (Run
locally: the affected test classes on net48 and net10.0 and the whole
Opc.Ua.Client.Tests project on net10.0; not the whole UA.slnx.)
- [ ] 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>
|
||
|
|
fb4eece24d |
Fix OpenUSD sample discovery and migrate artifacts to xRegistry (#4880)
# Description
_Describe the changes here to communicate to the maintainers why they
should accept this pull request. By default - this will become the
Commit message after merging and thus define history._
Fix OpenUSD sample discovery and integrate artifact delivery with the
xRegistry companion model.
- Load xRegistry before OpenUSD in the sample node managers, mount
stages with `Organizes`, and align root-layer identifiers with served
`.usda` files.
- Bootstrap immutable logical Resources and exact Versions in
`Artifacts`. Stage `Assets` folders provide non-owning `Organizes`
views, and explicit container identifiers allow sharing.
- Update the binding model to draft 0.9.0 while preserving existing
NodeIds and the `HasAddIn` correction.
- Disambiguate default container identifiers by stage instance, publish
the required read-only capabilities map, and preserve standard and
xRegistry BrowseName namespaces.
- Add registry, file-delivery, sharing, collision, qualified-browse, and
live-discovery regressions. Update the OpenUSD documentation.
Review fixes are in
|
||
|
|
df6adec1af |
Refresh server benchmarks for 2.0.2: GC modes, processor placement, delivery (#4868)
Closes #4842. Measures the items that `docs/Benchmarks.md` listed under *Open measurements* and makes every published table reproducible from checked-in files. All runs: `master` |
||
|
|
6fe5399510 |
Add UaLens desktop engineering workspace (#4822)
# Description Add **UaLens**, an Avalonia desktop engineering workspace for OPC UA exploration, monitoring, typed editing, administration and guided companion-specification tasks. This is a **new draft PR for `pisrapp` against `master`**, replacing closed, unmerged #4462 without reopening it. The shared prerequisites (#4801, #4802, #4803, #4804 and #4806) have all merged; their implementation is no longer part of this PR's delta. ### Scope - Multi-document workspace with connection, security and identity controls; browsing, subscriptions, events, alarms, history, writes and method calls. - Offline NodeSet2 exploration, typed scalar/array/matrix editing, certificate/GDS and account/role tools. - UaLens companion workflows, repository sample launching and the Aspire AppHost. - PubSub commissioning and capture/replay integration, plus the UaLens-facing OpenUSD viewport and rendered Vision workflows. - UaLens projects, test assemblies, UI assets, guides, solution/dependency entries, desktop test wiring and delivery workflows. Head **`6f46c51fcfef275059bbe77ef39c64c19ebc1053`** is one UaLens-only commit directly above master **`44a8cb277df450162c29313e2f4c862e7c683752`**. The rebase required no conflicts. All **967 UaLens file blobs**, the full-index binary patch, commit-message text and author/co-author attribution are unchanged from the previously published layer. There is **no implementation delta under `src/`**. The original history is retained in recovery refs, including `refs/pr-stack/backups/master20261017/ualens`. No feature was disabled, and no export/path safeguards, native acceptance criteria, test assertions or CI gates were weakened. The four previously recorded blank-line-at-EOF findings in the UaLens dialog files remain unchanged; they are not reported as newly fixed. ### Validation Fresh validation ran on the **exact PR head `6f46c51f`**, using the existing pinned SDK **10.0.401**, normal repository analyzers and physical/non-linked build outputs: ```powershell $d = 'D:\git\UA-.NETStandard_master\TestResults\ualens-528-deep-repro\sdk\dotnet.exe' $b = 'D:\git\UA-.NETStandard_master\TestResults\ua-stack-feedback20261016' $r = 'C:\Users\mschier\.copilot\session-state\abd33a2e-7139-44e1-9912-6e4451102b4e\files\ualens-new-draft20261017-net10' $env:DOTNET_ROOT = Split-Path -Parent $d $env:DOTNET_ROOT_X64 = $env:DOTNET_ROOT $env:DOTNET_CLI_TELEMETRY_OPTOUT = '1' $env:PATH = $env:DOTNET_ROOT + [IO.Path]::PathSeparator + $env:PATH & $d test 'tests\Opc.Ua.Lens.Tests\Opc.Ua.Lens.Tests.csproj' --no-restore --artifacts-path $b -c Release -f net10.0 -p:CustomTestTarget=net10.0 --filter 'TestCategory!=LongRunning&TestCategory!=Stress' --logger 'trx;LogFileName=ualens-new-draft.trx' --results-directory $r --blame-hang --blame-hang-timeout 3m --blame-hang-dump-type none --verbosity minimal # PASS, exit0: Passed: 4524, Failed: 0, Skipped: 0. ``` The saved TRX contains **4524 Passed and 44 explicit/prerequisite NotExecuted records** (4568 total). The repository verdict helper confirms zero failed tests, fixture setup/teardown failures or run-level errors, without toleration. No compiler/analyzer diagnostics were emitted. Logs, TRX and `ualens-new-draft20261017-validation.json` retain this exact-head evidence. **Not run for this new draft:** the full `UA.slnx` net48/net10 matrix, fresh coverage, other desktop TFMs, application-desktop/native workflow suites, NativeAOT, GPU/per-RID/installed-runtime or package-release qualification. UaLens targets .NET8+; no net48 UaLens execution is claimed. Remote CI and CodeQL are not yet claimed green. Historical native diagnostics and successful managed tests are not substitutes for rendered-frame or release acceptance. The previous PR and retained session artifacts preserve the full extraction, review, failed-attempt and diagnostic history. Historical intermittent CI failures were not silently reclassified as root-cause fixes; no old diagnostic grants or producer/release operations were reused for this PR. ## Related Issues _Reference all GitHub issues this PR addresses. If there is no issue yet, open one and link it here._ _If this is a relatively large or complex change, a design must have been discussed in the related tracking issue and signed off (which becomes the Architectural Decision Record (ADR))._ - References #4461, the existing closed UaLens design/qualification tracking issue; no new design approval is implied. - Replaces closed, unmerged #4462 for the remaining UaLens work. - Builds on merged prerequisites #4801, #4802, #4803, #4804 and #4806; this PR does not close them. ## 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. - [ ] 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. - [ ] I have addressed **all** PR feedback received. The test/coverage item remains unchecked because fresh coverage was not collected. This draft does not claim CLA confirmation, full-matrix clearance, native qualification or completed review. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Alexander Köpke <alexander.koepke@microsoft.com> Co-authored-by: Copilot <Copilot@users.noreply.github.com> |
||
|
|
4d00467f7e |
Add missing component to PumpType instances (#4867)
This pull request introduces improvements to how components and their references are managed and exposed in both the Machine Vision and Recycling sample domains. The main focus is on ensuring that components use the correct `HasComponent` reference type, and that pumps expose mandatory two-state status labels with appropriate properties and descriptions. Additional tests are included to verify these behaviors. **Component Reference Improvements:** * Updated the `WithComponents` method in the Vision documentation and implementation to use `HasComponent` references instead of `Organizes`, ensuring that components are referenced in compliance with OPC UA modeling best practices. [[1]](diffhunk://#diff-c885bd41583e603a034705800d1cd03213b4f3c8b1e1611b31121652e6548c5aL340-R341) [[2]](diffhunk://#diff-89611f4e76aef466943f80ed4079b1275d4ee2016610ad2407f7c8932f2d07e0L252-R252) * Modified the Machine Vision asset builder and related tests to verify that components are correctly referenced with `HasComponent`, and that shared components are properly managed without reparenting or replacing. [[1]](diffhunk://#diff-c265154f7bd0bbcbaa003aa050270e193e47ba941b41f9158a49f799efbcdf14L506-R517) [[2]](diffhunk://#diff-c265154f7bd0bbcbaa003aa050270e193e47ba941b41f9158a49f799efbcdf14R528-R535) **Pump Status Label Enhancements:** * Refactored pump status variable initialization in the Recycling sample to ensure that the mandatory two-state properties (`FalseState` and `TrueState`) are set with the labels "Stopped" and "Running", and that a descriptive text is provided. * Updated the Recycling sample documentation to clarify that each pump's status exposes the mandatory `FalseState` and `TrueState` properties as "Stopped" and "Running", reflecting the synthetic pump state. **Testing:** * Added a comprehensive test to verify that every circulation and drain pump exposes readable mandatory two-state status labels (`FalseState` and `TrueState`) with the correct values and reference types.# Description _Describe the changes here to communicate to the maintainers why they should accept this pull request. By default - this will become the Commit message after merging and thus define history._ ## Related Issues _Reference all GitHub issues this PR addresses. If there is no issue yet, open one and link it here._ _If this is a relatively large or complex change, a design must have been discussed in the related tracking issue and signed off (which becomes the Architectural Decision Record (ADR))._ - Fixes #github-issue-number, ... ## Checklist _Put an `x` in the boxes that apply. You can complete these step by step after opening the PR._ - [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. - [ ] 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. - [ ] I have addressed **all** PR feedback received. |
||
|
|
f6b27025a6 |
Classify sensitive log parameters with Microsoft.Extensions.Compliance data classification (#4864)
## Proposed changes Fixes #4857. The stack's log messages wrote network addresses (endpoint, discovery and reverse connect URLs, host names, IP addresses, ports), application and user identities, file paths and exception texts as plain parameters. Applications had no reliable way to keep them out of general-purpose logs. This PR replaces the stack's own redaction mechanism with the standard .NET [data classification and redaction](https://learn.microsoft.com/dotnet/core/extensions/data-redaction) packages. Each sensitive `[LoggerMessage]` parameter is classified. The application decides per classification what reaches its logs: erase it, replace it with an HMAC token so entries stay correlatable, or pass it through. ### Taxonomy `Opc.Ua.Redaction.OpcUaDataClassifications` defines the `OpcUa` taxonomy and one attribute per classification: | Classification | Used for | Parameters | |---|---|---| | `[NetworkAddress]` | URLs, host and domain names, IP/MAC addresses, end points, ports | 120 | | `[ApplicationIdentity]` | certificates, subject, issuer, thumbprint, ApplicationUri | 103 | | `[FilePath]` | file, directory and certificate store paths | 67 | | `[SensitiveText]` | exception or remote-supplied text passed as a parameter | 55 | | `[UserIdentity]` | user names, token subjects | 7 | Correlation ids (`ChannelId`, `SessionId`, ...), status codes and NodeIds stay unclassified. ### Pipeline and defaults - Opc.Ua.Core now references `Microsoft.Extensions.Telemetry(.Abstractions)` 10.11.0, so every project above Core uses the `Microsoft.Gen.Logging` generator. Opc.Ua.Client already did, through Http.Resilience. The packages ship net462 and netstandard2.0 builds, so net48 is supported. - **A plain `ILoggerFactory` logs `(null)` in place of classified values.** Classified values are only written by the redaction-aware logger. - The new `ILoggingBuilder.EnableOpcUaRedaction()` turns that logger on. It is `EnableRedaction()` with the discriminator off; otherwise every unredacted value gets `:ParamName` appended. `AddOpcUa().AddLogging(...)` and `DefaultTelemetry.Create(...)` call it, as do the samples, so they log the same information as before until redactors are registered. - `services.AddOpcUa()` alone does not change the host's logging. Generic-host apps call `builder.Logging.EnableOpcUaRedaction()`. This is documented as a behavior-breaking change in `docs/migrate/2.0.x/telemetry.md`. ### Obsolete API `Redact`, `RedactionWrapper<T>`, `IRedactionStrategy`, `RedactionStrategies` and `SimpleRedactionStrategy` are `[Obsolete]`. They still work, but the stack no longer uses them. ### Not classified - The `Exception` passed to a log call. Applications can drop exception messages with `LoggerEnrichmentOptions.IncludeExceptionMessage`. - Exception and `ServiceResult` message texts. - `ServiceResult`/`StatusCode` parameters and logged data values. - The few log calls whose level is decided at run time. - `Opc.Ua.Vision.OpenUsd`, which references only Opc.Ua.Types and cannot see the attributes. ### Generator behavior differences These are visible to log consumers and to tests: - `IFormattable` values such as `NodeId` are stored in the structured state as invariant strings, not as objects. - The generated log state is a thread-local object that is cleared after the call. - Classified values are pooled redactor objects, valid only during the `Log` call. Tests that inspected log state after the fact now capture it during the call. The shared `RecordingLoggerProvider` snapshots classified values. ## Related Issues - Fixes #4857 ## Types of changes - [ ] Bugfix (non-breaking change which fixes an issue) - [x] Enhancement (non-breaking change which adds functionality) - [ ] Test enhancement (non-breaking change to increase test coverage) - [x] Breaking change (fix or feature that would cause existing functionality to not work as expected; plain logger factories drop classified values) - [x] Documentation Update (if none of the other choices apply) ## Checklist - [x] I have read the CONTRIBUTING doc. - [x] I have signed the CLA. - [x] I ran tests locally with my changes, all passed. - [x] I fixed all failing tests in the CI pipelines. - [x] I fixed all introduced issues with CodeQL and LGTM. - [x] I have added tests that prove my fix is effective or that my feature works and increased code coverage. - [x] I have added necessary documentation (if appropriate). - [ ] Any dependent changes have been merged and published in downstream modules. ## Further comments New tests: - `LogClassificationCoverageTests` (Tools.Tests) scans all 104 stack assemblies and fails if an address, identity or path parameter of a `[LoggerMessage]` method is unclassified. - `DataClassificationLoggingTests` (Core.Tests) pins erasing and HMAC redaction, pass-through, the plain-factory drop, and both stack defaults. Local results on net10.0: | Suite | Passed | Failed | |---|---|---| | Core.Tests | 6,489 | 0 | | Client.Tests | 3,231 | 0 | | Tools.Tests | 7,210 | 0 | | PubSub.Tests | 1,536 | 0 | | Core.Diagnostics.Tests | 571 | 0 | | Configuration.Tests | 241 | 0 | - **Server.Tests:** the full run predates the final test fixes and had 12 failures. All 12 were in the fixtures fixed afterwards, which now pass: RoleStateBinding (45/45) and the FileDirectory and compatibility-logging fixtures (74/74 in total). The full Core.Tests and Server.Tests runs were not repeated after those fixes. - **Builds:** `UA.slnx` builds with 0 errors for net10.0 and net48, and the whitespace check passes. - **NativeAOT:** not confirmed locally, because the machine has no C++ linker. A trimmed publish of `Opc.Ua.Aot.Tests` reported no trim or AOT warnings from the stack, the new packages or the generated logging code; its only errors come from the TUnit test framework (`FSharp.Core` not found). The `aot-test` CI job has to confirm the native build. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Marc Schier <marcschier@users.noreply.github.com> |
||
|
|
5899354441 |
Clarify documentation, migration guidance, and SDK selection (#4823)
# Description Remove stock phrasing and correct misleading or inconsistent technical documentation. The initial two passes covered 367 Markdown files, followed by an interactive review of 169 editorial flags. The U5 interpretation originally deferred in that review has since received the upstream Mantis answer recorded by #4843; remaining server/spec follow-ups stay explicit in the CTT log. - Correct package responsibilities, migration steps, API names, ownership, protocol descriptions and links. Keep bundled migration guides synchronized. Preserve historical conclusions as historical records with separate re-check notes. - Provide at least two rendered TOC levels throughout `docs/`. Convert appropriate architecture figures to Mermaid without changing code examples, transcripts or historical quotations. - Address the October 19 feedback with concrete setup/migration instructions, corrected certificate paths and PubSub names, shorter repeated caveats, consistent XML comments, and a full review of the Concepts guide. Retain #4839's API corrections. - Integrate #4841's runtime/performance guidance and review incoming/recent prose. Preserve dated measurements rather than claiming new benchmark runs; clarify allocation ratios, startup-only processor settings, JIT warm-up and the benchmark commit's scope. Missing raw measurement artifacts and repeat ranges remain tracked by #4842. - Merge master through `e8c5d800a` in `32c6394b7`. Resolve the CTT log conflict using the updated Mantis states and OPC answers, retaining historical framing and re-checks for C28/C32/C43. - Address the latest naming/index feedback in `adb5bf693`: rename the guides to `docs/WotCon.md` and `docs/WotNodeSetConversion.md`; move the provider and DataType-declaration contracts into the conversion guide and remove `docs/WoT.md`. Rename `samples/DI` to `samples/Di` and `samples/Isa95` to `samples/ISA95`. Add content-index READMEs to Di, FluentApi, ISA95, Lds and PubSub; update solution entries, project references, Docker/CI paths, test fixture paths and documentation links with exact casing. **WoT guide scope.** This branch retains the detailed conversion guide that #4758 had replaced with an anchor stub. Conversion limits, verified document sets, preservation rules and provider ownership now live together in `WotNodeSetConversion.md`; binding/runtime guidance remains in `WotBindings.md` and `WotCon.md`. The design-rationale/test-ledger prose was shortened, the oversized argument-table cell moved into its section, and existing anchors preserved. **SDK policy is a separate, explicit user-directed change.** `global.json` sets a stable minimum of `10.0.400`, `rollForward: latestMajor`, and `allowPrerelease: false`. Newer major SDKs are permitted; CI's explicit SDK pins are unchanged. README, GettingStarted, DeveloperGuide and Recycling instructions reflect the possible analyzer differences. The image-contract regression fix accepts compatible stable SDK images instead of requiring exact equality with the floor. The latest four rename/index threads are resolved after publication. Three older threads remain open: universal obsolete-removal timing, unconditional nonce-strength wording, and a blanket Vision NativeAOT claim. Existing implementation/evidence constraints are explained in their inline replies. The two-level TOCs remain as requested. ## Validation - `git diff --check` and staged diff checks: passed. The untouched upstream AutoID NodeSet retains its provenance bytes, including two existing license-header trailing spaces. - `.\.azurepipelines\validate-migration-plugin.ps1`: passed; 15 bundled migration documents and manifests synchronized. - Documentation/path validation: **101** documents retain two rendered TOC levels (**2,228** entries), with idempotent formatting. **52** new relative links and their anchors passed exact-case checks. No tracked references to the old guide/sample paths remain. Both relocated provider-contract sections are preserved in the destination. - `dotnet sln UA.slnx list`: passed; shows the renamed Di/ISA95 projects and the existing FluentApi/Lds/PubSub projects. Updated solution and pump ProjectReference paths also passed exact-case existence checks. - Latest focused commands, run from the repository root: ```powershell # Passed: 61 channel send-order and reconnect tests. dotnet test tests\Opc.Ua.Core.Tests\Opc.Ua.Core.Tests.csproj -p:CustomTestTarget=net10.0 --filter 'FullyQualifiedName~UaSCBinaryChannelSendOrderTests|FullyQualifiedName~TcpReconnectKeyAgreementTests' --verbosity minimal # Passed: 5 pump datasheet tests; builds the renamed pump sample. dotnet test tests\Opc.Ua.Di.Tests\Opc.Ua.Di.Tests.csproj -p:CustomTestTarget=net10.0 --filter 'FullyQualifiedName~PumpDatasheetConformanceTests' --verbosity minimal # Passed: 4 repository NodeSet path/round-trip tests, including the moved pump models. dotnet test tests\Opc.Ua.Types.Tests\Opc.Ua.Types.Tests.csproj -p:CustomTestTarget=net10.0 --filter 'FullyQualifiedName~RepositorySourceSurvivesSerializedLinkedPartitionsAsync' --verbosity minimal # Passed: build, zero warnings/errors. dotnet build samples\ISA95\MinimalIsa95Server\MinimalIsa95Server.csproj -p:CustomTestTarget=net10.0 -f net10.0 --verbosity minimal # Passed: dependent sample-test project build, zero warnings/errors. dotnet build tests\Opc.Ua.WotCon.Samples.Tests\Opc.Ua.WotCon.Samples.Tests.csproj -p:CustomTestTarget=net10.0 -f net10.0 --verbosity minimal ``` Earlier focused integration checks passed 2,620 tests across AutoID, event queues/scheduling, source generation, MCP/package profiles and WoT. Both reviewed Mermaid diagrams rendered locally without network access. These checks are not a full solution run. The full solution has **not** been rerun for this review. Earlier `dotnet test UA.slnx -p:CustomTestTarget=net10.0 --verbosity quiet` did not pass: three AOT projects rejected VSTest and 20 Vision tests failed during graphics initialization with `OutOfMemoryException`. Remaining long-running tests were stopped at the user's request. A focused Vision rerun passed 558 tests and reproduced those 20 failures. The earlier `dotnet test UA.slnx -p:CustomTestTarget=net48 --verbosity quiet` was blocked by the former SDK pin and was not retried after the policy change. The user approved proceeding with those limitations documented; current remote CI is not claimed green. ## Related Issues - Follow-up #4842 tracks the requested 2.0.2+ benchmark/tuning and diagnostic-tool documentation refresh. #4841 supplies documentation and earlier results, not all newly requested measurements. - #4758 explains the conversion guide's earlier consolidation; the current provider contracts are now consolidated into `WotNodeSetConversion.md`. - #4839 supplies the reviewed API corrections; #4843 supplies the updated CTT issue record. - The original prose cleanup and SDK policy were user-requested without a source issue. No new runtime architecture is introduced by these documentation edits. ## 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. - [ ] 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. - [ ] 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. - [ ] I have addressed **all** PR feedback received. --------- Co-authored-by: Roman Ettlinger <romanett98@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0bd7aaaad6 |
AutoID: keep every reader reachable from DeviceSet, and let a reader be a component of a machine (#4853)
# Description Follow-up to the AutoID companion (#4825). Two changes to where the AutoID server places its readers: 1. **`IAutoIdReaderBuilder.WithParent` is made conformant with OPC 10000-100 4.9.** Until now it placed a reader below another node *instead of* `DeviceSet`, and the docs suggested "a component of a machine" as the use case. A machine in the Machinery `Machines` folder is not reachable from `DeviceSet`, so such a reader could not be located from `DeviceSet`, which DI 4.9 requires for every `ComponentType` instance (every AutoID device type derives from DI `DeviceType`). Now the reader is still placed below the parent (`HasComponent`, unchanged), and: - when `DeviceSet` already reaches the parent through hierarchical references (for example another reader, or any device it organizes), nothing else is added: the reader is a component of a composed device and DI 4.9 wants "only the root instance" referenced from `DeviceSet`; - otherwise `DeviceSet` also organizes the reader (`Organizes`, both directions), so it stays locatable. 2. **New opt-in `IAutoIdReaderBuilder.AsComponentOf(BaseObjectState machine)`**, available only when AutoID is composed with Machinery (`AddAutoIdToMachinery`, or a DI owner that loads the Machinery model). It makes the reader an identifiable component of the machine per OPC 40001-1 11: - the reader is placed below the machine's `Components` add-in (`HasComponent`, as `MachineComponentsType` declares for `<Component>`); - it gets the `2:Identification` add-in (`HasAddIn`) typed `MachineryComponentIdentificationType`, which holds no copies: it references the reader's own DI nameplate properties with `HasProperty` (`Manufacturer`, `SerialNumber`, and those of `Model`, `ManufacturerUri`, `ProductCode`, `HardwareRevision`, `SoftwareRevision`, `DeviceRevision`, `DeviceClass`, `ProductInstanceUri` that are set, plus `AssetId`/`ComponentName` when published; `DeviceManual` and `RevisionCounter` are left out as 40001-1 10.2 recommends); - the reader stays a device: the same `DeviceSet` rule as for `WithParent` applies with `Components` as the parent, so in the usual case (a machine in `Machines` that `DeviceSet` does not reach) `DeviceSet` organizes it directly. It is not added to `Machines`. - Checked before anything is registered, each failing with `Bad_ConfigurationError` and a message naming the way out: the server has no Machinery model (names `AddAutoIdToMachinery`), the machine is not a node of the AutoID owner, the machine has no `Components` add-in (names `WithComponents`), or the reader nameplate lacks `Manufacturer` and/or `SerialNumber` (mandatory in `MachineryItemIdentificationType`). `WithParent` and `AsComponentOf` exclude each other (`InvalidOperationException`). Default behaviour is unchanged: a reader built without `WithParent`/`AsComponentOf` is organized by `DeviceSet` and not added to `Machines`. Rollback on failure covers the new references: a failed build removes the `DeviceSet` reference, the component and its add-in, so the name can be used again. ## Specification basis | Statement | Source | | --- | --- | | "The DeviceSet Object is the starting point to locate Devices. It shall either directly or indirectly reference all instances of a subtype of ComponentType with a Hierarchical Reference. For complex Devices that are composed of various components that are also Devices, only the root instance shall be referenced from the DeviceSet Object. The components of such complex Devices shall be locatable by following Hierarchical References from the root instance." | OPC 10000-100 (DI) 1.05.0, 4.9 | | AutoID device types are "Subtype of DeviceType defined in OPC 10000-100". OPC 30010 does not mention `DeviceSet`, Machinery or `Machines`. | OPC 30010 (AutoID) 1.01, 6.1 Table 7 | | "Each Machine providing this building block provides the Components Object, which directly references all identifiable components of a Machine." / "Note: That does not preclude that a Machine is organizing its components in various other hierarchies as well." | OPC 40001-1 (Machinery) 1.04.2, 11.1 | | "The MachineComponentsType provides HasComponent References to all identifiable components of a Machine"; Table 27: `0:HasComponent Object <Component> 0:BaseObjectType OP`; Table 28: `<Component> 0:HasAddIn Object 2:Identification MachineryItemIdentificationType M`; "As all identifiable components shall contain the Identification Object". | OPC 40001-1 1.04.2, 11.2 | | `MachineryItemIdentificationType`: `2:Manufacturer` M, `2:SerialNumber` M, all other members O. | OPC 40001-1 1.04.2, 8.3 Table 16 | | `MachineryComponentIdentificationType` is the component subtype of `MachineryItemIdentificationType` (adds `2:DeviceRevision`); "It is not recommended to use the optional Properties 2:RevisionCounter and 2:DeviceManual"; a missing component `2:ProductInstanceUri` "shall be omitted". | OPC 40001-1 1.04.2, 10.2 | | "it is recommended to keep this approach and let several References lead to the same Node, rather than duplicating the Node" / Robotics example: identification Properties "are only referenced from the Identification functional group". | OPC 40001-1 1.04.2, 7.3 and Annex B.2 (informative) | | "It is expected that domain-specific companion specifications using this specification will define what assets of the domain in what usage are considered to be Machines or components of Machines." | OPC 40001-1 1.04.2, 11.1 (Note) | | Organizes "can be used to span multiple hierarchies"; more than one hierarchical path to a Node is allowed. | OPC 10000-3 1.05, 7.11, 7.5 | **SDK decisions** (not specified by OPC 30010, which predates Machinery): - A reader is never added to `Machines`: no specification classifies an AutoID device as a machine (40001-1 11.1 leaves that to the domain specifications, AutoID is silent). - A reader becomes a component of a machine only when the application asks for it (`AsComponentOf`). - `WithParent` keeps its parent and adds the `DeviceSet` reference rather than rejecting parents outside `DeviceSet` (see below). - The component `Identification` references the reader's nameplate instead of copying it. - AutoID does not add Machinery conformance units; the Machinery owner advertises them for what its builders created. ### Why `WithParent` adds a reference instead of rejecting DI 4.9 asks for reachability, not for a particular parent, and OPC 10000-3 allows several hierarchical paths to one node. Adding `Organizes` from `DeviceSet` satisfies the "shall" while keeping the parent the application asked for, and it is the same reference type `DeviceSet` already uses for every reader (and the DI node manager for every device). Rejecting such parents with `Bad_ConfigurationError` would break existing callers that hang a reader below a machine or folder. When `DeviceSet` already reaches the parent, no reference is added, so the "only the root instance" rule of 4.9 holds for composed devices. Reachability is decided once when the reader is built, by following the forward hierarchical references of the owner's nodes. ### Why `AsComponentOf` keeps the reader referenced from `DeviceSet` Machinery does not require machines to be in `DeviceSet` (the stock builders put them in `Machines` only), so a reader that is only below `Components` would again be unreachable from `DeviceSet`. The same reachability rule as for `WithParent` applies: if the machine is itself reachable from `DeviceSet` (for example a machine that is also a DI device organized by `DeviceSet`), the reader is reached through it and gets no direct reference, otherwise `DeviceSet` organizes it. ### Shape of `AsComponentOf` It takes the machine's `BaseObjectState`, like `WithParent(NodeState)` and `IMachineryBuildContext.BindMachine(TState, ...)`; with the stock builders that is `IMachineHandle<TState>.State`. It requires the machine's `Components` add-in to exist (`IMachineBuilder.WithComponents`, or a companion-declared one bound with `WithComponents(components, ...)`) instead of creating one, so the Machinery builders stay the only place that creates Machinery building blocks (`MachineryBuildingBlocks` organization, facets). The reader's NodeState parent is the `Components` object, so the DI node manager attaches it with `HasComponent` and the existing registration and rollback code applies unchanged. ## Behaviour change for existing callers - `WithParent(parent)` with a parent that `DeviceSet` does not reach (for example a machine in `Machines`, or an application folder): the reader is now *also* browsable from `DeviceSet` (one extra `Organizes` reference, both directions), and an `AutoIdClient.EnumerateReadersAsync()` (which browses `DeviceSet`) now finds it. Its parent, NodeId and `HasComponent` reference are unchanged. - `WithParent(parent)` with a parent `DeviceSet` reaches (for example another reader): unchanged. - Default placement: unchanged. - `IAutoIdReaderBuilder` gains a member. The interface is implemented only inside the package (instances come from `AutoIdRuntime.Reader`), and the package is new in 2.0. ## Changes - `src/Opc.Ua.AutoID.Server/AutoIdReaderPlacement.cs` (new, internal): `DeviceSet` lookup, the reachability check, the `Organizes` reference and its removal, `Components` resolution and validation, the nameplate check, and the `Identification` add-in. - `src/Opc.Ua.AutoID.Server/AutoIdRuntime.cs`: the builder's nameplate delegate runs once (validated before anything is registered, then copied into the DI builder); `AsComponentOf` resolves `Components` as the parent before the device is created; after registration the add-in is added and the `DeviceSet` reference kept; rollback removes the reference. - `src/Opc.Ua.AutoID.Server/Builders/IAutoIdReaderBuilder.cs`, `AutoIdReaderBuilder.cs`: `AsComponentOf`, mutual exclusion with `WithParent`, XML docs stating the exact behaviour. - `docs/AutoID.md`: new "Placement" subsection under "Hosting readers" (what DI 4.9 and Machinery 11 require, what is an SDK decision, a table per builder call, an example), the composition table rows for readers and `Machines`, the test fixture table. `src/Opc.Ua.AutoID.Server/NugetREADME.md`: a short paragraph. - Tests (below). ## Tests - `AutoIdReaderLifecycleTests` (stand-alone owner): `ReadersCanHangBelowAnExplicitParentAsync` now also asserts that `DeviceSet` does not organize a reader below another reader; `AReaderBelowAParentOutsideTheDeviceSetIsStillOrganizedByItAsync`; `AFailedCreationBelowAnOutsideParentLeavesNoDeviceSetReferenceAsync` (rollback); `AStandaloneReaderCannotBeAComponentOfAMachineAsync` (`Bad_ConfigurationError` naming `AddAutoIdToMachinery`); argument checks for `AsComponentOf(null)` and both call orders of `WithParent`/`AsComponentOf`. - `AutoIdCompositionTests` (Machinery owner, in process): `AReaderBecomesAComponentOfAMachineAndStaysADeviceAsync` (parent is `Components`, `HasComponent`, `DeviceSet` organizes it, `Machines` does not, `Identification` with `HasAddIn` and the component type, `HasProperty` to the reader's own `Manufacturer`/`SerialNumber`/`Model` nodes, no `DeviceManual`, owned subtree); `AComponentNeedsAMachineWithComponentsAndRegistersNothingOtherwiseAsync`; `AComponentNeedsTheMandatoryIdentificationAsync`; `AComponentNeedsAMachineOfTheOwnerAsync`; `AFailedComponentCreationIsRolledBackAsync`. - `AutoIdPlacementEndToEndTests` (new, hosted Machinery + AutoID over a real session): a default reader, a reader below that reader, a reader `AsComponentOf` a machine and a reader `WithParent` the machine. Browsing `DeviceSet` finds the three roots and not the nested reader, the outer reader reaches the nested one; `MachineryClient.DiscoverMachinesAsync` returns only the machine and `Machines` contains no reader; `MachineryClient.EnumerateComponentsAsync` lists only the component reader, `ReadIdentificationAsync` reads `Manufacturer`, `SerialNumber`, `ProductCode` through the add-in, which is `HasAddIn`-referenced and typed `MachineryComponentIdentificationType` and whose `HasProperty` targets are the reader's own nameplate nodes; `AutoIdClient.EnumerateReadersAsync` finds the three roots. - The existing default assertions (`TheReadersShareTheDeviceSetWithThePumpAndStayOutOfTheMachinesFolderAsync`, `TheMachineryAndPumpsClientsStillFindTheirInstancesAsync`, `ComposedReadersLiveBelowTheSharedDeviceSetAsync`) are unchanged. - Fail-without: with the runtime wiring reverted to master (builder surface kept so the tests compile), 10 of the new tests fail: all `AsComponentOf` cases, both `WithParent` cases outside `DeviceSet` and the three session tests that look for the component or the extra `DeviceSet` reference. Removing only the rollback of the `DeviceSet` reference makes both rollback tests fail. `NoReaderBecomesAMachineAsync` and the existing default tests pass with and without the change, as intended. Local results (macOS arm64, SDK 10.0.402, head `238ed9b69` on master `9bfc780e1`; the same three commits are now based on master `ac24123dd` as `c6d70909b`, no AutoID file changed in between): `Opc.Ua.AutoID.Tests` net10.0 318/318 passed; `Opc.Ua.AutoID.Server` all TFMs and the test project with `CustomTestTarget=net48` build with 0 warnings, 0 errors (net48 tests run on Windows CI; there is no mono host here). `dotnet format whitespace/style/analyzers --verify-no-changes` on the changed files: no new findings beyond the conventions of the surrounding test files. ## Commits 1. `fix(autoid): keep every reader reachable from DeviceSet` 2. `feat(autoid): make a reader a component of a machine` 3. `docs(autoid): document where readers are placed` The branch is based on OPCFoundation/master `ac24123dd` (#4852, the last master commit with a green build). Fork CI was not repeated on this head: earlier fork runs of this branch failed only in test projects outside this PR (the master build was broken by the NUnit 5 migration #4840 and fixed in #4852; master is currently broken again in `Opc.Ua.Core.Tests` by #4849). The upstream checks of this PR test it against the current master. ## Related Issues - Follow-up to #4825 (tracking issue #4824). - #4823 (open) edits prose in other paragraphs of `docs/AutoID.md` (table of contents, the DI-owner paragraph, providers). This PR adds a subsection and changes two rows of the composition table and one row of the test table; no hunk overlaps. The table of contents is deliberately not touched here because #4823 adds the subsection entries for "Hosting readers"; the "Placement" entry can be added there or after both are merged. ## 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. (Local build of the changed projects: 0 warnings, 0 errors.) - [ ] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. (Locally the AutoID test project on net10.0; net48 is built but runs only on Windows CI.) - [ ] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. (Pending the upstream checks; see the CI note above.) - [x] I have addressed **all** PR feedback received. --------- Co-authored-by: Marc Schier <marcschier@users.noreply.github.com> |
||
|
|
a51f08ad6f |
AutoID client: read a device's status without browsing (#4854)
# Description A follow-up to #4825. It adds `AutoIdClient.RefreshDeviceAsync(AutoIdDeviceSnapshot, CancellationToken)`, which reads the variables of a reader device that change at runtime again through the NodeIds a snapshot already carries. It makes one Read and no Browse. ## Why `ReadDeviceAsync` browses the children of the device on every call. OPC 30010 makes most device variables and methods optional, so the browse is how the client finds out what this device publishes. `AutoIdReadOptions.StatusOnly` only narrows the Read. This is the point the Copilot review raised on #4825 ([discussion](https://github.com/OPCFoundation/UA-.NETStandard/pull/4825#discussion_r4308032841)). |
||
|
|
2836da7ec8 |
Fix sessionless ResultTransfer ownership bypass and other issues. (#4834)
# Description Correct ownership, endpoint validation, policy propagation, subscription delivery, file handling and generated-model behavior across the stack, tools and samples. Add focused regression tests and update the corresponding guides and sample documentation. ### Previously included corrections - Require an active, matching session to generate or access transient ResultTransfer file handles. Cover both sessionless generation and reads against a handle owned by an active session, using live session owners in the Machinery tests. - Keep physical filesystem operations within their configured mount and apply the same confinement checks throughout file operations. - Validate LDS semaphore paths consistently during server registration and registration-store maintenance. ### Individual issues addressed | # | Component | Issue and correction | |---|---|---| | 1 | AI jobs and transfers | Jobs and transfers did not consistently retain their creator's identity and session. Apply ownership checks to descendants, file handles, subscriptions and execution, and recheck deployment permissions before dispatch. | | 2 | Password-management client | Password-bearing calls could use signing-only channels or follow a replacement connection. Capture an encrypted transport generation and keep dispatch bound to it; preserve ordinary non-password edits. | | 3 | GDS and certificate-push clients | Connections omitted hostname verification. Validate the original configured DNS name or IP address before session activation. | | 4 | Certificate push | Private-key uploads could use signing-only calls. Require an encrypted, generation-bound dispatch while preserving certificate-only updates and restoring the caller's identity afterward. | | 5 | MQTT client | The .NET Framework custom-trust callback could discard TLS hostname, certificate-availability and usage errors. Retain those checks while supporting configured issuer trust. | | 6 | DTLS handshake | Certificate trust was not tied to the configured peer address. Match the certificate's DNS or IP identity to the intended endpoint. | | 7 | Replicated address spaces | Serialized nodes lost per-node and descendant permission metadata. Preserve role permissions, user-role permissions and access restrictions, including updates to existing replica nodes. | | 8 | MCP PubSub imports | Imports without groups skipped checks on the effective broker destination. Check the actual endpoint before resolving credentials or activating the connection. | | 9 | Reference-server accounts | A disabled username account could authenticate again with its previous roles. Reject disabled accounts during fresh authentication. | | 10 | Console reference client | The sample disabled hostname checks when connecting to an otherwise trusted peer. Retain the original requested host and validate it before activation. | | 11 | Capture replay and export | Replay and raw export could accept non-capture files or include key material. Require complete PCAP/PCAPNG containers and exclude key sidecars and decryption-secret blocks. | | 12 | PubSub adapter fallback | The fallback client configuration automatically accepted peer certificates. Apply normal trust and hostname validation before activation or reads. | | 13 | Kafka transport | Explicit TLS enablement was checked but did not reach both final Confluent configurations. Use the same effective protocol for validation, producers and consumers. | | 14 | PubSub actions | Configuration rebuilds could widen a connection-local action handler's scope. Preserve its original connection association and reject mismatched dispatch. | | 15 | PubSub readers | Reader-level message-protection settings were accepted but ignored. Apply the effective group and reader settings before accepting and publishing received values. | | 16 | PubSub key groups | Different `SecurityGroupId` values could share one connection-wide key context. Reject mixed groups on a connection and require separate connections for distinct groups. | | 17 | MCP session registrations | Session-name replacement or reuse could retain another registration's credential bindings. Bind access to the registration, caller and authenticated endpoint, and preserve that identity when borrowing sessions. | | 18 | Viewer HTTP host | Requests reached the viewer's OPC UA session without consistent Host, Origin and port checks. Validate the request destination and browser origin before using the session, while retaining supported local MCP clients. | | 19 | Vision file operations | File operations did not consistently apply protected-file checks, and remote handles lacked caller and connection context. Apply the shared checks and use host-side opaque handles bound to caller, target and generation. | | 20 | DTLS record integrity | The integrity-only MAC used a truncated sequence number. Authenticate the full reconstructed sequence number and epoch so records remain distinct across rollover. | | 21 | AMB client sample | Connection setup omitted hostname validation. Preserve the originally requested host and verify the peer identity before activation. | | 22 | Robot sample writes | The R2 `SpeedOverride` callback could target R1 state. Bind each callback to its own robot and apply that robot's write policy. | | 23 | AI result properties | Optional result children inherited remotely writable defaults. Preserve their declaration restrictions and keep server-produced results read-only. | | 24 | WSS peer-trust updates | Existing channels did not consistently respond to peer-trust changes. Revalidate complete chains for forward and reverse WSS channels and close only those that no longer validate. | | 25 | GDS revocation checks | Caller-provided certificates could cause issuer retrieval from embedded URLs. Resolve validation from configured local trust, issuers and CRLs without fetching caller-provided locations. | | 26 | GMS observations | Published correction observations retained writable model defaults. Reject remote writes while preserving publisher-side updates. | | 27 | Production-dataset monitoring | Subscription admission and delivery could use the reporter's context instead of each subscriber's permissions. Evaluate the subscriber at admission and on every delivery, including cancellation and disposal paths. | | 28 | WoT MQTT destinations | Hostless MQTT forms could imply a localhost destination before loopback checks. Reject them before credential lookup or connection attempts and validate the effective endpoint. | | 29 | XRegistry opaque reads | Content-ID reads and notifications did not consistently honor encrypted-read requirements or the currently accepted transport. Recheck the bound endpoint and reject transport downgrades before rebinding, including when protected values are already queued. | | 30 | PubSub capture dissection | Key loading did not consistently check the configured capture-root boundary. Check each actual key path immediately before opening it. | | 31 | PubSub publication queues | Imported WriterGroup queues were not fully checked against publish-topic grants. Check the destination actually used by each group. | | 32 | MCP PubSub mutations | A caller-supplied mode could select subscribe grants for an operation that publishes. Derive the required direction and parent context from the actual mutation. | | 33 | Generated write masks | Model import and generation could widen an explicit `UserWriteMask`. Preserve it through import, copy, merge, inheritance and dependency metadata. | | 34 | Generated method flags | An explicit `UserExecutable=false` could be lost. Preserve its specified state and resulting method behavior through the same model paths. | | 35 | Generated role identifiers | Unresolved role identifiers could cause permission entries to be omitted. Preserve namespace-qualified role IDs and report a diagnostic when a policy cannot be represented. | | 36 | Aggregation-server defaults | The `localhost` default did not reliably constrain listener binding. Use numeric loopback for the default endpoint and command-line fallback. | | 37 | Certificate-validation cache | Cached results could skip current leaf or issuer validity checks. Recheck validity windows on cache hits and keep hostname validation independent of cached chain trust. | | 38 | Software-update folders | Revision names and marker contents containing dot segments could select paths outside the software root. Normalize and check every derived path against that root. | | 39 | USDA arrays | Array elements were not encoded for their string or asset context. Escape them as data and preserve valid composition and round-trip behavior. | | 40 | Adapter hot reload | A changed dataset definition with the same name could keep old fields, subscriptions or cached values active. Compare complete immutable definitions and retire old state before replacement. | | 41 | Adapter index ranges | Subscription publication could ignore `IndexRange` and return the complete value. Preserve the requested projection and its status and timestamps; reject unsupported range providers. | | 42 | PubSub metadata and targets | Metadata from one connection could change another connection's target mapping. Scope metadata to its connection and provenance, and resolve targets using actual typed field IDs rather than field names or unintended positional fallback. | | 43 | Scales product locks | Product locks were not applied across every remote mutation path. Apply them consistently to value writes, filling calculations and related recipe/controller operations while retaining permitted updates. | | 44 | Event permission caching | Cached event decisions could outlive identity, role or node-policy changes. Revalidate the current subscriber, event type and source permissions instead of reusing a stale decision. | | 45 | WoT Modbus destinations | Hostless Modbus forms could imply localhost before loopback checks. Reject them before connecting and validate the effective endpoint. | | 46 | MCP local PKI tools | Peer-trust mutations did not consistently require the caller's `CertificateAdministration` grant. Check the capability before trust and untrust operations. | | 47 | Robot-control HTTP bridge | The bridge lacked consistent Host, Origin and port validation and caller-specific session grants. Apply those checks before borrowing the OPC UA session and retain supported local clients. | | 48 | Subscription transfers | Transferred subscriptions could retain cached notifications after the destination's permissions changed. Require authorization continuity between source and destination identities and roles, invalidate stale retransmission data when continuity fails, and preserve retransmission behavior for compatible transfers. | ### Compatibility notes - Replication uses the versioned NSRP v1 policy envelope. Upgrade peers together and re-encode or reseed legacy topology from the authoritative model; missing permission metadata is not inferred from old records. - Integrity-only DTLS peers must be upgraded together for the full sequence/epoch MAC. AEAD behavior is unchanged. - Distinct PubSub key groups require separate connections. - Custom sessions that cannot capture the actual encrypted transport, and subscription providers that cannot honor a requested range, are rejected rather than silently changing the requested behavior. - Subscription transfers retain retransmission data only when the destination authorization remains compatible with the source snapshot; otherwise the old sequence numbers and cached data are invalidated. ## Related Issues No separate tracking issue is linked. The individual corrections are listed above. ## 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. - [ ] 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. |
||
|
|
b72eb44a31 |
Redundancy: bound the Raft log with the engine's snapshots (#4836)
# Description **Draft: builds on #4835 and waits for a RaftCs release** containing marcschier/raft-cs#9, marcschier/raft-cs#10 and marcschier/raft-cs#11, anticipated as 1.1.4. It contains the commit of #4835; review only the second commit. `RaftSharedKeyValueStore` can capture and restore its state (#4794), but `IRaftConsensus` carries no log indices, so the store cannot bound the RaftCs log, which grows without limit. This change uses the engine's snapshots: - `IRaftSnapshotConsensus` (`CommittedItems` with log indices and snapshots, `CompactAsync`) and `RaftCommittedItem`. `DefaultRaftConsensus` implements it over `RaftNode.CommittedItems`, `CreateSnapshotAsync` and `CompactAsync`. - `RaftSharedKeyValueStore` gains a constructor overload with a compaction interval and an optional logger (the existing constructors are unchanged) and `AppliedIndex`. Over an `IRaftSnapshotConsensus` it applies the indexed stream. A snapshot on that stream (a lagging replica installing the leader's snapshot, or the first item after a restart) replaces the state and publishes the differences to watchers. Every compaction interval it snapshots its state into the engine, which discards the log behind it. A compaction that fails is logged and retried at the next interval. - The engine snapshot is a format marker followed by the snapshot encoding of #4794, so it carries the command position: the commands after it keep the positions they have on every replica, and `CreateSnapshotAsync`/`RestoreSnapshotAsync` keep working over it. No compaction runs while a store restored with `FromFirstCommand` still skips the replayed commands its snapshot holds. - A snapshot in another format stops the applier. Pending and later proposals and watches then fail with its error instead of waiting. - `AppliedIndex` is published once the state holds an item, before watchers and the proposer learn of it. - Registration: `RedundancyConsistencyOptions.RaftCompactionInterval` (server), and overloads of `AddRaftClientSharedStore` and `AddRedundantClientSharedStore` that take the interval (client). Zero, the default, never compacts. The store's logger comes from the registered `ITelemetryContext`, else `ILoggerFactory`. - `HighAvailability.md` describes the compaction and warns that a replica must not be rolled back to a release without engine snapshots once the cluster compacted its log. No public member is removed or changed. ## Related Issues - #4794 (store snapshots), #4835 (RaftCs 1.1.4) - marcschier/raft-cs#9, marcschier/raft-cs#10, marcschier/raft-cs#11 ## 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. (`RaftSharedKeyValueStoreCompactionTests`; registration tests in Server and Client.) - [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. (Redundancy test projects on .NET 10 against a RaftCs build of the three pull requests: Server 935, Client 135, Kubernetes 103, PubSub 45, Samples 17, all pass; the Server and Client test projects build for net48.) - [ ] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. (CI cannot restore RaftCs 1.1.4 until it is released.) - [x] I have addressed **all** PR feedback received. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Marc Schier <marcschier@users.noreply.github.com> |
||
|
|
4115ef696a |
Merge origin/master and preserve data-channel lifecycle guards
Keep data-channel close notifications while adopting terminal-error transport draining. Retain recipient-scoped event visibility alongside source-emission admission and shared filter evaluation. Reconcile overlapping NUnit 5 assertion fixes. |
||
|
|
65a67dd2f4 |
WoT runtime materialization, binding execution and native source emission (#4757)
# Description - WoT registry, prepared file store, immutable content, version leases, dependency-graph sharing (advanced graph rewrite), coordinator, materialization, views and atomic refresh. - Explicit caller-context propagation (`IWotRegistryService` / versioned registry / prepared host / publication capture) and the additive `IXRegistryProjectionContextualDelete` capability. - UA/HTTP/MQTT binding execution and compilation, plus the 1.02 FileType bridge and discovery. - WoT-specific native event, source-emission and readiness integration (`MasterNodeManager.SourceEmissions`, `EventManager` identity admission, external-value monitored sources, Fluent attach/detach) and event lease conservation. - All corresponding `WotCon.*` runtime tests, WotCon sample app code, WoT docs, CI feature budgets and Samples.Tests, plus the WotCon Design NodeSet/csv/csproj and ModelSourceParity tests. - Joint model metadata: XRegistry model 0.7.0 / 2026-09-12 (NamespaceVersion 63563, NamespacePublicationDate 63564), RequiredModel date 2025-01-08. - XML-doc work from the frozen WIP, applied to G12-owned declarations only. ## Related Issues - Fixes #4751. - Part of the WoT split tracked in #4511. ## 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. (Tests included, not yet run against current master.) - [ ] I have added all necessary documentation. - [ ] I have verified that my changes do not introduce (new) build or analyzer warnings. (Not run.) - [ ] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. (Not run.) - [ ] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. - [ ] I have addressed **all** PR feedback received. |
||
|
|
ad0190c001 | Merge origin/master into data-channels-quic-experimental | ||
|
|
9bfc780e14 |
Update NuGet dependencies and migrate tests to NUnit 5 (#4840)
# Description Update 55 NuGet dependencies to the latest stable releases available on 2026-10-19, preserving documented compatibility pins and framework-specific version bands. Existing OpenUSD prereleases remain unchanged because no newer releases are available. Migrate existing tests to NUnit 5: await asynchronous assertions, propagate async through helpers, handle nullable predicates, and replace obsolete `Order` attributes with sequencing dependencies. Sequencing dependencies use `AllowFailure = true`; filtering a dependent test also runs prerequisites. Adjust fixture-level parallelism where NUnit dependency scheduling requires it. Keep Roslyn API/runtime pins intact; select newer Immutable/Metadata runtime packages only for test and profiling hosts requiring them. Update the approved SDK pin to 10.0.402 across local, CI, and container builds. Document NUnit 5 conventions. |
||
|
|
a10f9e252d |
Generic runtime continuation, batch lifecycle and readiness (#4759)
# Description This pull request extracts and integrates generic server runtime and continuation-management capabilities for OPC UA NodeManagers. - **NodeManager lifecycle and routing:** add batch registration and reload, staged publication and readiness notifications, captured routing snapshots and reservations, and safe retirement. Wire the lifecycle through host interfaces, async/sync adapters, `MasterNodeManager`, and `StandardServer` startup and shutdown. - **Browse and History continuations:** retain continuation ownership and manager dependencies across requests and reloads; add atomic claims, session caching, and cleanup when requests fail or generations retire. Add historian resume state, tokens, dependency capabilities, and shared continuation-store support. - **MonitoredItem lifecycle:** support detaching and reattaching items across node-manager generations, including event subscriptions shared across root notifiers and correct defaults for detached-item modification. - **Runtime NodeSet references:** track reference additions and deletions through supported API paths and preserve the tracked references during reload. - **Published views and materialization:** add immutable `TypeTable` and `EncodeableFactory` snapshots with publication-owner lifetime management, and provide a live node-materialization query for the existing WoT projection-view consumer. - **CI trace support and documentation:** add scripts and tests for staging NUnit internal traces, and document NodeManager lifecycle, session continuation ownership, and historical-access behavior. ## Related Issues - Extracts the generic runtime portion of #4511. - #4757 depends on this pull request; this pull request does not depend on #4757. ## 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. - [ ] 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. - [ ] 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. - [ ] I have addressed **all** PR feedback received. --------- Co-authored-by: perf <perf@local> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
0f85a9f719 |
AutoID: the OPC UA for AutoID Devices companion SDK (OPC 30010) (#4825)
# Description This completes the AutoID contribution proposed in #4824: the model package for [OPC 30010 OPC UA for AutoID Devices](https://reference.opcfoundation.org/AutoID/v101/docs/) (1.01) plus the server, the client, an MCP profile and a sample pair, in one PR. It follows the newest companion pattern (GMS #4765, MachineTool, CranesHoists, MachineVision). ## Commits 1. `fix(sourcegen)`: a structure property whose DataType a subtype refines is redeclared with its own field (the NodeSet needs it; unchanged from the first slice). 2. `feat(autoid)` model package `Opc.Ua.AutoID` (unchanged from the first slice). Adds only `AutoIdReaderKind`. 3. `feat(autoid)` server package `Opc.Ua.AutoID.Server`. 4. `feat(autoid)` client package `Opc.Ua.AutoID.Client`. 5. `feat(autoid)` MCP profile `Opc.Ua.Mcp.AutoID`. 6. `feat(autoid)` samples `samples/AutoID`. 7. `build(autoid)` solution, package list and package validation tests. 8. `fix(autoid)` x3, `docs(autoid)` and `test(autoid)`: review fixes, net48 compatibility, documentation, the native AOT test. 9. `fix(autoid)` x3 and `feat(autoid-client)`: `GetLocation` location types, the `Client` of access events, the default scan cycle of the simulated readers, and NodeIds and methods in the device snapshot (details below). 10. `feat(autoid)`, `test(autoid)`, `docs(autoid)` (`afdd7858b`, `22bc3b26e`, `ed25fe4ae`, on top of the master merge `1c7948f65`): AutoID composes into an existing Device Integration owner like Pumps and Scales (review feedback; see "Composition with other DI-based companions"). 11. `1458af9e9` (marcschier): the `AutoIdReadOptions` remarks state that `StatusOnly` limits the values read and that each call still browses (Copilot review). 12. `fix(autoid)` `3ef2f1507`: `GetDeviceLocation` answers with the same status codes as `GetLocation`; `feat(autoid-mcp)` `f6405152f`: `autoid_read_device` shows the new snapshot NodeIds and methods (details below). ## Server: `Opc.Ua.AutoID.Server` - Reader devices (RFID, OCR, optical, optical verifier, RTLS) on the DI device model, created by a fluent builder (`AutoIdRuntime.Reader(name, provider)`) and hosted with `AddAutoId` / `ConfigureAutoId` on `IServiceCollection`. - Hardware is behind provider interfaces: `IAutoIdReaderProvider`, `IAutoIdScanProvider` (Scan), `IAutoIdAsyncScanProvider` (ScanStart/ScanStop), `IAutoIdRfidProvider` (ReadTag, WriteTag, WriteTagID, KillTag, LockTag, SetTagPassword, each opt-in by flag), `IAutoIdRtlsProvider`. Providers push scans, presence, log entries and access events through `IAutoIdReaderSink`. In-memory providers (an EPC Gen2 tag model with banks, passwords, locks and kill) are included for tests and samples. - Optional children (methods, `LastScanData`, `DeviceLocation`, `AntennaNames`, location methods) exist only when the provider supports them. Facet profiles and conformance units are advertised only for what is implemented (`IConformanceContributor`); the Full facet is withheld. - `GetLocation` (OPC 30010 6.6.3.2) returns the location in the requested `LocationType`. An unsupported type is `Bad_InvalidArgument`, an unknown object `Bad_NotFound`, and a supported type the object's location is not available in `Bad_InvalidState` (6.6.3.2: "not available or not allowed e.g. due to special configuration"); a provider result of another type never reaches the client. `InMemoryRtlsProvider` takes an optional location converter, for example from the configured local coordinate system (9.3.4) into WGS84. - `GetDeviceLocation` (6.1.3.7) uses the same mapping: an undefined location type is `Bad_InvalidArgument`, a device location that is not available in the requested type `Bad_InvalidState` (it answered `Bad_NotSupported` before). 6.1.3.7 lists only "Standard OPC UA status codes defined for the Call Service in OPC 10000-4"; there `Bad_NotSupported` means "The Method is not supported for the Object instance", which is wrong for a published method, and OPC 10000-4 5.12.2.4 allows reusing an existing status code with the same canonical meaning, so the method follows 6.6.3.2. - Access events (7.10, 7.11): `Client` is "the name of the client application which was the originator of the command", so it is the application name of the session's client description (else its application URI), not the user identity. The results of a `ScanStart` scan name the client that started it; scans the device reports on its own name the device. - The simulated readers default to one scan cycle per second (`CycleInterval`, configurable per provider; the length of a cycle is device specific in OPC 30010), so an unbounded `ScanStart` raises at most one scan event per second. - Behaviour: one scan at a time with a generation counter; ScanStart/ScanStop serialised; a scan started by a session stops when that session closes; the maximum scan duration, tag read length and result count are limited by `AutoIdServerOptions`; device status changes and events are raised outside locks; registration is rolled back on failure; readers, providers and the runtime are disposed in order and in-flight operations are drained. ## Composition with other DI-based companions A server has one Device Integration owner, and AutoID now uses the existing harmonized composition instead of requiring its own node manager: - `AutoIdMachineryModelProvider` adds the AutoID model to the Machinery owner's model-provider pipeline (dependency-ordered, `Order` 200) and keeps the DI nodes the owner loaded, so DI and `DeviceSet` exist once. - `AddAutoIdToMachinery` (after `AddMachinery`) registers it; `ConfigureAutoIdFor<TNodeManager>` creates readers on any `DiNodeManager` owner, successive delegates share one `AutoIdRuntime`; `owner.CreateAutoIdRuntime()` is the path without DI hosting. The owner disposes the runtime on shutdown. - Readers are registered in the owner below its `DeviceSet`, NodeIds in the owner's instance namespace (`AutoIdRuntime.InstanceNamespaceIndex`), not in the `Machines` folder; the AutoID conformance units and facets are published by the owner next to the DI and Machinery ones. - `AddAutoId` remains for the "AutoID is the only DI-based companion" case and is built from the same provider. Next to another DI owner it is still rejected; the message names the composition API. (Bypassing that check gives two `DeviceSet` copies; the server routes `DeviceSet` to the first owner and readers in the second are not browsable, checked locally.) - `Opc.Ua.AutoID.Server` now references `Opc.Ua.Machinery.Server` (a stable package), as Vision does; a stand-alone server still loads only DI and AutoID. ## Client: `Opc.Ua.AutoID.Client` Reader discovery (a failed browse propagates), typed calls for every published method, a device snapshot with documented `AutoIdReadOptions`, and an async event stream (`ObserveScan/Access/Presence/LogEntries`) on `IStreamingSubscription`. Cancellation is honoured throughout. `AddAutoIdClient` registers it for DI. Because most children of a device are optional (6.1, 6.5.3, 6.6.3), the snapshot also carries the NodeIds of `DeviceStatus`, `LastScanData`, `LastScanTimestamp` and `DeviceLocation` and the browse names of the published methods (`Methods`, `HasMethod`), taken from the same browse whatever the read options, so a client can monitor the device and knows what it may call without browsing again. ## MCP profile: `Opc.Ua.Mcp.AutoID` `McpToolProfile.AutoId` with nine tools: list readers, read device, scan, scan start/stop, observe scan events, read tag, get supported location types, get location. `autoid_read_device` also returns `deviceStatusId`, `lastScanDataId`, `lastScanTimestampId` and `deviceLocationId` (NodeId strings, null when the device does not publish the variable) and `methods` (the browse names of the published methods), built explicitly without reflection. Registered in the host builder, the profile switches of the other profiles, `Test-McpGrade.ps1`, `McpREADME.md` and `.mcp/server.json`. ## Samples `samples/AutoID/AutoIdServer` (a dock-door reader with a simulated pallet) and `AutoIdClient` with a README and `Test-AutoIdSamplePair.ps1`. The default port is not a common one; tests use port 0. ## Other changes - `Opc.Ua.Di.Server` gets `InternalsVisibleTo` for `Opc.Ua.AutoID.Server` (`RegisterRootNotifierAsync`). - `UA.slnx`, `.azurepipelines/expected-packages.txt`, `PackageVersionPolicyTests`, `NuGetPackageSetValidationTests`. - Docs: `docs/AutoID.md`, `docs/McpServer.md`, `docs/Profiles.md`, `docs/Samples.md`, `docs/DeveloperGuide.md`, `docs/README.md`, `docs/WhatsNewIn2.0.md`, `README.md`, NugetREADMEs. The "model package only" statements are removed. - All packages follow the root version (no preview override). ## Specification notes and questions for the maintainers - The model is 1.01, published 2020-06-18 (the NodeSet header). OPC 30010 6.1.3.16 prescribes the `AutoIdModelVersion` string `"1.00"`, which is what the NodeSet carries and what the server publishes. - **Question:** the NodeSet's optical verifier `Scan` method returns `WGS84Coordinate`, which looks like a model error. It is not published, and providers that implement `IAutoIdScanProvider` for a verifier are rejected. Is that the behaviour you want? - The stack answers a call to an unpublished optional method with `BadNotImplemented`. ## Design choices reviewers may want changed - `Opc.Ua.Di.Server` gets `InternalsVisibleTo` for `Opc.Ua.AutoID.Server`, so the server can call `RegisterRootNotifierAsync`. - `AutoIdReaderKind` (and `AutoIdReaderKinds.TypeIdOf`) lives in the model package `Opc.Ua.AutoID`, not in the server package, so the client and the MCP tools share it. - The MCP profile is read-only/safe: no tools that write, kill or lock tags. ## Tests `tests/Opc.Ua.AutoID.Tests` has 306 tests (116 of the model slice, 190 new): reader lifecycle, scan semantics, tag access handlers, limits, conformance, in-memory providers, hosting, and over-the-wire client/server tests with result codes, events, concurrency and dispose. `tests/Opc.Ua.Tools.Tests` has 13 AutoID MCP tests plus the updated profile-count tests. New behaviour tests fail without the code (for example a provider's own cancellation, a synchronous scan while a scan stop is in progress; for the last round: `GetLocation` of a supported type the object is not located in answered `Bad_NotFound`, access events named the user "Anonymous" and attributed `ScanStart` results to the device, and an unbounded `ScanStart` on a default simulated reader reported 20 times in 400 ms). Composition: `AutoIdCompositionTests` (stock Machinery owner, providers out of order, DI loaded once, instance ownership, rollback, cancellation, runtime disposal, an application-owned DI owner, a second DI owner rejected) and `AutoIdMachineryCompositionEndToEndTests` (Machinery + Pumps + AutoID in one hosted server, read back by the AutoID, Machinery and Pumps clients, including a scan and the published profiles and conformance units). Mutating the change (no lifetime registration, the DI instance namespace for browse names, the old ownership message) fails three of them. The sample pair script passes. `tests/Opc.Ua.Aot.Tests.Mcp` has an AutoID profile test (`AutoIdMcpAotTests`); the project was published with Native AOT on osx-arm64 and all 64 tests passed. Publishing it also found two `JsonArray.Add<T>` uses in the MCP tools (IL2026/IL3050), now fixed. ## Verification (local, macOS arm64, SDK pinned in `global.json`) The branch contains the master merges `fc556be7e`, `1c7948f65` and `af7feb886`; the three composition commits sit between the last two (the latest merge touches no AutoID, DI or Machinery file). `1458af9e9` (marcschier) and the two commits of the last round follow `af7feb886`. - **Last round (`3ef2f1507`, `f6405152f`).** Builds with `-m:2 -nodeReuse:false -p:UseSharedCompilation=false`: `Opc.Ua.AutoID.Tests` on all target frameworks and with `-p:CustomTestTarget=net48`, `Opc.Ua.Mcp.AutoID` on net8.0/net9.0/net10.0, `Opc.Ua.Tools.Tests` on net10.0: 0 warnings, 0 errors. `Opc.Ua.AutoID.Tests` 306/306 on net10.0; `Opc.Ua.Tools.Tests` AutoID tests 60/60, and the profile, catalogue, composition and package tests that reference AutoID (2241) pass. The changed tests fail without the code: `GetDeviceLocation` answered `Bad_NotSupported` for both cases, and `autoid_read_device` had no `deviceStatusId`. `tests/Opc.Ua.Aot.Tests.Mcp` published with Native AOT on osx-arm64 (no IL2026/IL3050 warning): 64/64 passed. `dotnet format` whitespace, style and analyzers `--verify-no-changes` on the changed files: no finding on a changed line (line endings aside, which the LF checkout on macOS reports for every file). - **Build (composition round).** `Opc.Ua.AutoID.Server` on all target frameworks, `Opc.Ua.AutoID.Tests` on net10.0 and with `-p:CustomTestTarget=net48`, and the AutoID server sample: 0 warnings, 0 errors. MCP tools and JSON unchanged, so the native AOT test was not republished. - **Build (earlier rounds).** The AutoID packages and `Opc.Ua.AutoID.Tests` on net10.0 and, with `-p:CustomTestTarget=net48`, on net48/net8.0/net9.0/net10.0; `Opc.Ua.Tools.Tests` and `Opc.Ua.SourceGeneration.Core.Tests` on net10.0 (`-m:2 -nodeReuse:false -p:UseSharedCompilation=false`): 0 warnings, 0 errors. - **Tests.** `Opc.Ua.AutoID.Tests` 306/306 on net10.0 (composition round; earlier round 288/288); `Opc.Ua.Tools.Tests` (AutoID, host builder, profile and package tests) 2438/2438 on net10.0; `Opc.Ua.SourceGeneration.Core.Tests` 4358 passed, 8 skipped, after the master merge touched `NodeStateGenerator.cs`. net48 was built, not run (no Mono on macOS); the fork CI runs it. The MCP tools and their JSON are unchanged in the last round, so the native AOT test was not republished. - **Coverage.** Composition round: the local coverlet collector recorded zero hits for every module in this environment (also for unchanged code), so no new local number; the new paths are exercised by the 18 composition tests listed above and the fork CI uploads coverage. Earlier round: cobertura from the AutoID tests: `Opc.Ua.AutoID.Server` 92.0 % lines / 86.8 % branches, `Opc.Ua.AutoID.Client` 89.9 % / 80 %; 110 of the 112 coverable changed lines of the last round are covered (the two missed lines skip a browse result without NodeId or name). The generated code of the model package is excluded as in the other model packages. - **Format.** `dotnet format` whitespace, style and analyzers (`--severity info`) `--verify-no-changes` on the changed files: clean. Fork CI (full matrix including macOS): [run 39162077465](https://github.com/biancode/UA-.NETStandard/actions/runs/39162077465) on `f6405152f`: green. (Previous round: run 39130154771 on `ed25fe4ae`, 90/90 jobs green; the upstream checks on the merge `af7feb886` green, 108 passed, 5 skipped.) CodeQL: [run 39162079826](https://github.com/biancode/UA-.NETStandard/actions/runs/39162079826) on `f6405152f`: green, no alert in a file this PR touches. (Previous round: run 39130156598 on `ed25fe4ae`, green.) ## Related Issues - Part of #4824 (tracking issue: AutoID companion SDK). ## 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. - [x] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. (The solution builds on all TFMs; the AutoID test projects ran on net10.0 locally, net48 runs in the fork CI.) - [x] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. - [x] I have addressed **all** PR feedback received. --------- Co-authored-by: Marc Schier <marcschier@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
99dca35158 | Keep native validation packages out of standard publish | ||
|
|
60ad14f786 | Merge master into data-channels-quic-experimental | ||
|
|
526f8765b9 | Implement OPC UA Data Channels | ||
|
|
fc0687a2dc |
Source generator: a NodeSet2 Variable without a Value has a null Value (#4798)
# Description A Variable or VariableType in a NodeSet2 that has no Value element has a null Value (OPC 10000-6 F.5); the runtime NodeSet2 importer loads it that way. The source generator initialized such a node with the default value of its DataType instead ((int)0, (double)0, ...), so a server built from the generated code reported values the model never declared. The NodeSet2 importer now records that the node declares no Value (NodeSetValueIsNull), the flag follows the node into the merged instance hierarchy, and the node state generator emits Variant.Null for it when no value was merged in from elsewhere. ModelDesign inputs, where a missing DefaultValue implies the DataType default, are unchanged. The tests generate a DI-style NodeSet2 together with the DI NodeSet2 and check the generated values of a type's InstanceDeclaration, of an instance's children with and without a Value, and of a VariableType. ## Related Issues None. ## 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. - [x] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. - [x] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. - [x] I have addressed **all** PR feedback received. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Marc Schier <marcschier@users.noreply.github.com> |
||
|
|
8441351902 |
Client: let ManagedSession validate a rediscovered endpoint (#4791)
# Description When a reconnect is rejected with a status that requires the endpoint to be refreshed, ManagedSession reads the endpoint again from the server's discovery endpoint and ConfiguredEndpoint.Update replaces the endpoint in use with whatever discovery returned. The application had no say: a discovery answer that, for example, drops the security mode or presents a different server certificate was taken over as it was. ManagedSession.ValidateRediscoveredEndpoint (a RediscoveredEndpointValidator) receives the endpoint in use and the rediscovered one before the session is recreated against it. A Bad result keeps the endpoint in use and fails the reconnect attempt with that result, so the reconnect policy decides what happens next. Without a validator the behaviour is unchanged. ## Related Issues None. ## 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. - [x] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. - [x] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. - [x] I have addressed **all** PR feedback received. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Marc Schier <marcschier@users.noreply.github.com> |
||
|
|
55bd944609 |
WoT: Types-side TD/TM-NodeSet conversion with consolidated corpus and minimal consumer compat (#4758)
# Description Adds the pure Types-side WoT Thing Description / Thing Model to NodeSet conversion: resolution, context, declarations, types, values, localization, URI/format handling, bounded hashing, plus supporting native XML codec, NodeSet alias/value helpers, RelativePathFormatter and a JsonElement equality polyfill. - Carrier-witness fix: a missing native event is accepted only when the owning preserved type declares it through a `GeneratesEvent` reference (directly or via its type/supertype chain) AND a bounded, validated selected EventType carrier identity equals the normalized authored native target. Native-present events get a full compare; an ambiguous native key and a missing map stay strict; a readable document is never archive authority. - Support-only dependencies: additive per-instance, non-serialized `WotDocument.IsSupportOnly(kind, name)` (ArgumentNullException guard). The resolver marks support-only after closure; explicit selection wins; the View builder excludes only true support-only members, so unit/actsOn dependencies stay in the JSON. Nested controls cover inner value-only projection under an outer value-only selection (unit kept, support-only) versus an outer explicit SelectAll (explicit, not support-only). - Documentation/XML comments and member-order only in commit 5c368d3a; no logic change. - Timeout repair on current head |
||
|
|
768b39e1be |
Server: surface failures to restore durable subscriptions (#4795)
# Description SubscriptionManager.RestoreSubscriptionsAsync logged and swallowed every restore failure: a subscription store that threw, a store that reported an unsuccessful restore, and a stored subscription that could not be recreated. The server then started without those subscriptions, the store was told the restore had completed (so it cleaned up what it held), and nothing but a log line showed that subscriptions had been lost. A failed restore now fails the restore, and with it the startup: - a store that throws fails with BadInternalError and the store's exception as the inner exception; - a store that reports Success = false fails with BadInternalError; - stored subscriptions that cannot be recreated are collected and reported as an AggregateException, before the store is told that the restore completed, so the store keeps what it holds. SubscriptionManager.ContinueOnRestoreFailure (default false) restores the previous behaviour for applications that prefer to start without the subscriptions. Cancellation is no longer swallowed unless that option is set. The Quickstarts durable subscription store reported Success = false when it had nothing stored; it now reports a successful restore of no subscriptions, and keeps reporting a failure when its file cannot be read. ## Related Issues None. ## 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. - [x] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. - [x] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. - [x] I have addressed **all** PR feedback received. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Marc Schier <marcschier@users.noreply.github.com> |
||
|
|
7fb986bd0d |
Add recycling operator console and sample container images (#4810)
# Description Add a local browser-based operator console for the synthetic recycling line, using direct, encrypted OPC UA connections and the existing typed coordination interface. - Extract a sample-local, non-packable `Recycling.Client` library shared by the server and console. Preserve the model XML bytes, namespace/type identifiers, resource identity, wire format and journal schema; the server retains its .NET 8/9/10 targets. - Add a .NET 10 ASP.NET Core workstation host with bounded SSE state/trend delivery, native companion inspection, paged material/genealogy reads, and role-limited coordinator commands. Exact command envelopes are retained for explicit retries; uncertain outcomes are not automatically reissued. - Add the operator interface using local HTML/SVG, CSS and JavaScript modules: ten process groups, substep inspection, faults/constraints, persistent quality holds, trends, material tracing and simulator administration. Show raw quality/provenance and handle stale data, run changes, browser expiry and large identifiers explicitly. No browser-generated plant telemetry or frontend framework/npm build is introduced. - Keep application/user credentials in external certificate stores. Use strict X.509 trust, signed/encrypted UA sessions, local browser bootstrap sessions, same-origin/CSRF checks, and a conservative configured control ceiling. This is a local synthetic sample, not a physical emergency-stop or shared remote-user system. - Register `recyclingserver` and `recyclingclient` in the existing Images CI matrix, using source/prebuilt Dockerfile stages and the existing portable-IL/multiarchitecture packaging flow. Add an optional loopback-published Compose console service. No new schedules or NuGet publication entries are added. - Add managed, real-UA, Chromium, NativeAOT and image-contract qualification, with Microsoft.Playwright as a test-only centrally managed dependency, and update usage/deployment documentation. ## 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. - [ ] 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. - [ ] I have addressed **all** PR feedback received. --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
7e25e1a3d7 |
Extract remaining shared companion, rendering and packaging changes (#4806)
# Description The shared AI deployment contracts and instance registration; DI software-upload outcomes, digest handling and simulator; bounded Positioning/Pumps reads; PubSub raw-data/matrix and security-provider handling; Vision read-quality contracts; xRegistry resource writes and instance projection; OpenUSD authoring, embedded viewport and retained-frame capture APIs; standalone sample CLI controls; and the generic Debug/RID package-policy and fork/Dependabot coverage-comment fixes. The Linux native prerequisite step is retained for **Vision tests only**. ## Related Issues - Related tracking issue: [#4461](https://github.com/OPCFoundation/UA-.NETStandard/issues/4461), without automatic closure. - Source work: [#4462](https://github.com/OPCFoundation/UA-.NETStandard/pull/4462), which remains intact for the coordinator's later integration. - Immediate stacked predecessor: [#4804](https://github.com/OPCFoundation/UA-.NETStandard/pull/4804). ## 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. - [ ] 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. - [ ] 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. - [ ] I have addressed **all** PR feedback received. |
||
|
|
4e77ba3f84 |
Redundancy: snapshot and restore for RaftSharedKeyValueStore (#4794)
# Description RaftSharedKeyValueStore applies every committed command but offers no way to capture its state. An application that wants to bound the Raft log cannot snapshot the store before compacting its consensus log, and after a restart it cannot restore the state without replaying the whole log. Add CreateSnapshotAsync, which commits a barrier through the consensus and encodes the state in a versioned format, and RestoreSnapshot, which replaces the state before the store starts applying the log. A malformed snapshot raises FormatException; restoring after start raises InvalidOperationException. Tests: RaftSharedKeyValueStoreSnapshotTests (round trip, log applied after restore, restore after start rejected, corrupt snapshot rejected). ## Related Issues None. ## 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. - [x] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. - [x] 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 <223556219+Copilot@users.noreply.github.com> Co-authored-by: Marc Schier <marcschier@users.noreply.github.com> |
||
|
|
b1c0cd20fd |
Fix classic browse-path subscription restoration (#4814)
# Description Persisted classic subscriptions retain their server/client handles but previously lost relative-path resolution metadata. Transfer could succeed, yet a later `ApplyChangesAsync` attempted to resolve an already-created item and failed. The SDK now owns this restoration lifecycle without exposing a public resolution-state setter. - Preserve the originally monitored target in internal snapshot state and restore both `ResolvedNodeId` and `Status.NodeId`. Transfer/reactivation use handles without re-browsing; fresh creation resolves paths again. - Version classic subscription/configuration files and read existing unversioned 2.0 files using frozen legacy wire layouts. Legacy missing targets remain unknown rather than being guessed. - Exclude existing items from creation-time resolution, reserve creation before asynchronous translation, and reject stale translation results. Failed deletion retains identity and pending retry state. - Support forward-only snapshot input using position tracking and bounded, per-ExtensionObject buffering in the binary decoder, including short reads. No whole-file buffering or reflection-based serialization is introduced. - Add unit/integration regressions and document the supported lifecycle and migration implications. **Compatibility:** Newly written files require the updated SDK. Existing 2.0 files remain readable. Assigning a different `StartNodeId` or `RelativePath` to a created/in-flight item now immediately throws `BadInvalidState`; unchanged assignments remain allowed. No V2 subscription-engine changes or 1.5 XML import are included. ## 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. - [ ] 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. - [ ] I have addressed **all** PR feedback received. |