Commit Graph

982 Commits

Author SHA1 Message Date
Randy Armstrong 0b0c6a099a Add CryptoTrace, fix ECC testing 2026-06-03 15:18:07 -07:00
Marc Schier dbc09b8e22 Adopt System.TimeProvider across the stack (fixes #2458) (#3829)
# Description

Adopt
[`System.TimeProvider`](https://learn.microsoft.com/dotnet/api/system.timeprovider)
across the stack so that every place that creates timers, runs duration
math, or schedules periodic work is mockable, deterministic in tests,
and immune to wall-clock changes.

The migration touches Core, Core.Types, Server, Client, PubSub,
Configuration, Gds (server + client), Lds, and the Quickstarts/Console
reference apps. Per-component changes follow the same pattern:

1. Add a `private readonly TimeProvider m_timeProvider;` field.
2. Accept a nullable `TimeProvider? timeProvider = null` as the **last**
constructor parameter (new overload; legacy constructor stays for binary
compatibility and delegates with `timeProvider: null`).
3. Initialise the field with `m_timeProvider = timeProvider ??=
TimeProvider.System;`.
4. Replace `new Timer(...)`, `Stopwatch`, `HiResClock.*`,
`DateTime.UtcNow`-based duration math, and `Task.Delay(delay, ct)` in
timing loops with the matching `m_timeProvider` API (`CreateTimer`,
`GetTimestamp` / `GetElapsedTime`, `GetUtcNow().UtcDateTime`,
`Task.Delay(delay, m_timeProvider, ct)`).
5. Wire the provider through DI: `AddOpcUaServerBuilder` /
`AddOpcUaClientBuilder` register `TimeProvider.System` via
`TryAddSingleton<TimeProvider>()` so tests can swap in
`Microsoft.Extensions.Time.Testing.FakeTimeProvider`.

`HiResClock` itself is kept (so the
`ApplicationConfiguration.DisableHiResClock` schema flag still
round-trips), but **every public member** is now `[Obsolete]` with a
single shared message pointing to the new migration-guide section. The
few internal call sites that still need to set/read `HiResClock` are
wrapped with `#pragma warning disable CS0618`.

### Highlights per component

- **Core / Stack** — `Opc.Ua.Core`, `Opc.Ua.Core.Types`, channel
pipeline (`UaSCBinaryClientChannel`, `ChannelToken`,
`UaSCBinaryChannel`), `ApplicationConfiguration`, certificate
validators, `JwksIssuerKeyResolver`, `TimeProviderExtensions` polyfills
(`GetTimestampMilliseconds`, `GetTickCount`,
`CancellationTokenSource(timeout, timeProvider)`, `Task.Delay(delay,
provider, ct)` polyfill for older TFMs).
- **Server** — session lockout, subscription pipeline
(publish/sample/keep-alive/lifetime), `MonitoredItemQueue`, alarm
sampling rate, `IntervalRunner`, server diagnostics, configuration state
machines, hosted services.
- **Client** — `Session`, managed/V2 subscription engines, reconnect
handlers, channel keep-alive, periodic publishing, complex-type
discovery polling.
- **PubSub** — `IntervalRunner` pipeline, message-receive timers,
MQTT/UDP keep-alive, publishers/readers.
- **Gds / Lds** — `LdsServer`, `LinqApplicationsDatabase` expiry
housekeeping, GDS clients (push, pull), `DirectoryCertificateStore`,
`ConnectionStateMachine`, classic subscription engine.
- **Quickstarts / Console samples** — `SampleNodeManager`,
`ReferenceNodeManager`, `AlarmNodeManager`, `BoilerState`,
`MemoryBufferState`, `PublishedValuesWrites`. Samples resolve via
`(Server as ITimeProviderProvider)?.TimeProvider ?? TimeProvider.System`
so existing ctor signatures stay binary-compatible.
- **Tests** — `Opc.Ua.PubSub.Tests` adds `IntervalRunnerTests`; existing
`SubscriptionTests`, `UaPublisherTests`, and `TestDataSystem` move off
`HiResClock`. Added `Microsoft.Extensions.TimeProvider.Testing` package
for `FakeTimeProvider` (project-load warning surfaces on net48/net472
only — runtime is .NET 8+).

### Migration guide

New section [**Time and Timer abstraction
(`TimeProvider`)**](Docs/MigrationGuide.md#time-and-timer-abstraction-timeprovider)
documents:

- The canonical replacement table (`HiResClock.UtcNow` →
`timeProvider.GetUtcNow().UtcDateTime`, `HiResClock.TickCount64` →
`timeProvider.GetTimestamp()`, `new Timer` → `timeProvider.CreateTimer`,
`Task.Delay(delay, ct)` → `Task.Delay(delay, timeProvider, ct)`, …).
- The `TimeProvider? timeProvider = null` + `??=` constructor pattern.
- DI wiring and how to swap in `FakeTimeProvider` in tests.
- Before/after snippets for `HiResClock.TickCount64`,
`HiResClock.UtcNow`, and `new Timer(...)`.

## Related Issues

- Fixes #2458

## Checklist

- [x] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [x] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- [x] I have added all necessary documentation.
- [x] I have verified that my changes do not introduce (new) build or
analyzer warnings.
- [ ] I ran **all** tests locally using the **UA.slnx** solution against
at least .net **framework** and .net **10**, and all passed.
- [ ] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [ ] I have addressed **all** PR feedback received.
2026-06-03 22:38:32 +02:00
Marc Schier 0000513cde Pluggable user-identity provider model (#3807)
# Description

Introduces a pluggable user-identity provider model covering all OPC UA
Part 6 §6.5 mechanisms (Anonymous, UserName, X509, IssuedToken/JWT) on
both client and server, with full
`Microsoft.Extensions.DependencyInjection` integration, plus the Part 12
§9 `AuthorizationService` two-phase token-request flow **including
refresh-token rotation**, the Part 12 §8 KeyCredential Push model, and a
clean provider model for OPC 10000-12 §7.2 `ApplicationSelfAdmin`. The
work is split across phase-by-phase commits so it can be reviewed
independently; nothing here changes wire-level behaviour or forces
existing applications to migrate.

### Documentation

All under `Docs/`:

- `IdentityProviders.md` — full developer guide: three-layer model,
recipes, configuration reference, "what gets auto-wired" matrix, JWS
algorithm table, identity-augmenters section, **"Implementing your own
provider"** how-to subsections for Entra / OIDC / Windows / AspNetCore
(sibling files merged in per review feedback).
- `DependencyInjection.md` — `Identity` sections with `appsettings.json`
examples, identity-extension matrix, `WithAuthorizationService` +
`WithKeyCredentialPush` + `AddIdentityAugmenter` extensions documented.
- `RoleBasedUserManagement.md` — claim-criteria callout describing
`IIdentityClaims` probe surface.
- `AuthorizationService.md` — Architecture diagram +
`IAccessTokenProvider` conceptual narrative restored from master; modern
`StartRequestToken` / `FinishRequestToken` flow leads; refresh-token
recipe added.
- `KeyCredentialService.md` — Push model + Pull vs Push vs Bridge
decision matrix + Hybrid (Push + Pull) section; explicit experimental
warning for the bridge.
- `GDS.md` — KeyCredential Push, `StartRequestToken`,
`GdsApplicationSelfAdminProvider`, **shipped Refresh-token support with
rotation policy and audit semantics** all documented.
- `MigrationGuide.md` — User Identity Providers section listing
`[Obsolete]` markers + mechanical recipes; SelfAdmin migration via
`IIdentityAugmenter`; `EnableRefreshTokens` default note.
- `README.md` index entries.

## 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.
2026-06-02 01:14:54 +02:00
Marc Schier 01d7162313 Part 9 Alarms & Conditions: complete server + client implementation (#3805)
Complete implementation of **OPC UA Part 9 ΓÇö Alarms & Conditions** for
both server and client, **OPC UA Part 16 ΓÇö State Machines** (generic
extensible client + server API), and the supporting infrastructure
(source-generated event records, source-generated ObjectType proxies,
`IAsyncEnumerable` streaming subscriptions, address-space model change
tracking, per-node `INodeCache` invalidation, unified DI integration).

### Server-side (Part 9 complete)

- **`AlarmConditionState`** ΓÇö every Part 9 method wired automatically
in `OnAfterCreate`: `Silence`, `Suppress`/`Suppress2`,
`Unsuppress`/`Unsuppress2`, `RemoveFromService`/`RemoveFromService2`,
`PlaceInService`/`PlaceInService2`, `Reset`/`Reset2`,
`GetGroupMemberships`. Each method generates the spec-mandated audit
event and supports an optional veto-delegate hook.
- **Latched alarms** ΓÇö `ActiveState` reflects real process state;
`LatchedState` persists until explicit `Reset` (with full precondition
validation).
- **Re-alarming** ΓÇö
`ProcessReAlarm`/`ResetReAlarmRepeatCount`/`IsReAlarmEnabled` helpers
(opt-in, no hidden timer).
- **Audible alarms** ΓÇö `UpdateAudibleState` for
`AudibleEnabled`/`AudibleSound`/`SilenceState` lifecycle.
- **Suppression + groups** ΓÇö `AlarmGroup`, `AlarmSuppressionEngine`
(with first-in-group), `AlarmRateTracker` (for `AlarmMetricsType`).
- **Spec-compliance fixes** ΓÇö `SuppressedOrShelved` now includes
`OutOfServiceState`; `GetRetainState` includes `LatchedState`;
`EffectiveDisplayName` composition includes `OutOfServiceState` +
`LatchedState`.

### Client-side Part 9 API

- **`AlarmClient`** ΓÇö typed Part 9 operations. Each method delegates
to the matching source-generated `*TypeClient` proxy
(`ConditionTypeClient`, `AcknowledgeableConditionTypeClient`,
`AlarmConditionTypeClient`, `DialogConditionTypeClient`,
`ShelvedStateMachineTypeClient`) ΓÇö one source of truth for every
method NodeId.
- **Interface hierarchy** — `IConditionOperations` →
`IAcknowledgeableConditionOperations` → `IAlarmOperations` +
`IDialogConditionOperations`.
- **Source-generated event records** ΓÇö new `EventRecordGenerator`
(alongside `ObjectTypeProxyGenerator`) emits `{Type}TypeRecord` per OPC
UA event type: `ConditionTypeRecord` →
`AcknowledgeableConditionTypeRecord` → `AlarmConditionTypeRecord` →
`LimitAlarmTypeRecord`/`ExclusiveLimitAlarmTypeRecord`/… all the way
to vendor subtypes. **Vendor alarm types get records automatically.**
- **`AlarmEventDecoder`** ΓÇö decodes `EventNotification.Fields` into
the most-specific generated record.
- **`AlarmEventFilterBuilder`** ΓÇö fluent builder for Part 9
`EventFilter`s.
- **`ISession.GetAlarmClient(telemetry)`** convenience extension.

### Part 16 state machines (generic, extensible)

- **Client**: extension methods on the source-generated
`StateMachineTypeClient` / `FiniteStateMachineTypeClient` ΓÇö
`GetCurrentStateAsync`, `ObserveFiniteTransitionsAsync`
(`IAsyncEnumerable<FiniteStateSnapshot>`), `WaitForStateAsync`,
`GetAvailableStatesAsync` / `GetAvailableTransitionsAsync`. **Vendor
proxies inherit the API automatically** through the generated
inheritance chain.
- **Server**: `StateMachineBuilder` (fluent: `AddState` /
`AddTransition` / `OnCause` / validation) +
`FluentFiniteStateMachineState` ΓÇö declare a Part 16 state machine
without subclassing the abstract base.
- **Alarms alignment**: `AlarmClient.GetShelvingStateAsync` /
`ObserveShelvingTransitionsAsync` surface the API for every alarm's
`ShelvingState` child.

### IAsyncEnumerable streaming subscription

- **`IStreamingSubscription`** with
`SubscribeDataChangesAsync`/`SubscribeEventsAsync` returning
`IAsyncEnumerable<T>` (Channel-backed).
- **`ManagedSession.DefaultStreaming`** ΓÇö lazy shared subscription;
disposed with the session.
- **Helpers** ΓÇö `TakeUntilAsync`, `WithTimeoutAsync`, `TakeAsync`,
`BufferedAsync` for state-machine waits and bounded observation windows.
- **`AlarmStreamExtensions`** ΓÇö
`SubscribeAlarmsAsync`/`SubscribeConditionsAsync`/`SubscribeDialogsAsync`
returning the source-generated record types.

### Address-space model change tracking

- **Server**: `RaiseGeneralModelChangeEvent` on both `CustomNodeManager`
and `AsyncCustomNodeManager`; `ModelChangeAggregator` (in
`Opc.Ua.Server.NodeManager`, used by every node manager) batches changes
per service call; **auto-emit** from `CreateNode`/`DeleteNode` (opt-out
via `ModelChangeEmissionEnabled = false`).
- **Client**: `IModelChangeTracker` subscribes to
`BaseModelChangeEventType` via the streaming subscription, decodes
`ModelChangeStructureDataType` payloads, invalidates the `INodeCache`
per-node.
- **`INodeCache.InvalidateNode(NodeId)`** ΓÇö new interface member (true
per-node eviction in `NodeCache`).
- **`ManagedSession.EnableModelChangeTrackingAsync()`** +
`ManagedSessionOptions.ModelChangeTracking` +
`ManagedSessionBuilder.WithModelChangeTracking()` fluent method.

### Dependency Injection integration

- **`services.AddOpcUa().AddClient(...).AddAlarms()`** ΓÇö registers
`AlarmClientFactory` so DI-hosted apps can resolve a Part 9 client per
session without `new`-ing one manually.
- Cross-link added to the unified DI surface (`AddServer` / `AddClient`
/ `AddComplexTypes` / `AddGdsServer` / `AddLdsServer` /
`AddWotConServer`).

### Quickstart server modernization

- `Quickstarts.Servers/Alarms/AlarmNodeManager.cs` ported to
`AsyncCustomNodeManager` and demonstrates the new `AlarmGroup` +
`AlarmSuppressionEngine` end-to-end via a writable `MaintenanceMode`
boolean that suppresses every member alarm.

### Documentation

- New: `Docs/AlarmsAndConditions.md`, `Docs/StreamingSubscription.md`,
`Docs/ModelChangeTracking.md`, `Docs/StateMachines.md` ΓÇö full
developer guides.
- Updated: `Docs/DependencyInjection.md` (`AddAlarms()` row + section),
`Docs/README.md` index, `Docs/MigrationGuide.md` (Part 9 / Part 16 /
model change tracking sections covering behavior changes + breaking API
surface).
- Sample: `Applications/ConsoleReferenceClient/AlarmClientSample.cs`.
2026-05-29 23:24:53 +02:00
Marc Schier 5ff968fcfb Test project reorganization follow up (#3810)
### Test project reorganization

Splits the integration- vs unit-test boundary along **project
boundaries**
and introduces a runtime-tier split for subscription tests so the
mainline
CI no longer drags in stress / durable suites.

**New projects:**

- `Opc.Ua.Stress.Tests` ΓÇö cross-area stress tier (designed to
absorb
  session / channel / pub-sub stress in future). Seeded with the 6
  subscription stress classes (`SubscriptionDepthTests`,
  `SubscriptionBasicDepthTests`, `MonitoredItemDepthTests`,
  `SubscriptionTransferDepthTests`, `SubscriptionPublishTooManyTests`,
`SubscriptionMultipleTests`). **Excluded from mainline CI; runs
nightly.**
- `Opc.Ua.Subscriptions.Durable.Tests` ΓÇö durable-subscription tier
  (needs `DurableSubscriptionsEnabled` + storage-backed lifetime).
  Receives `SubscriptionDurableTests` plus the migrated
  `DurableSubscriptionTest` and `DurableSubscriptionTestDebug`.
  **Excluded from mainline CI; runs nightly.**
- `Opc.Ua.Features.Tests` ΓÇö cross-feature integration tier.
Receives the
  `[OneTimeSetUp]`-bootstraps-its-own-server integrations for AliasNames
  (Part 17), Roles (Part 18), FileSystem (Part 5 §C / Part 20), and
  NodeCache. Also absorbs the misplaced
  `Tests/Opc.Ua.History.Tests/FileSystemTests.cs`.

**Client.Tests cleanup (~28 files moved):**

- `Opc.Ua.Sessions.Tests` (existing) gains 10 session integration files
  plus the 4 session benchmark files inline (`LoadTest`,
  `RequestHeaderTest`, `TraceableRequestHeaderTest`,
  `SecurityPolicyBenchmarks`).
- `Opc.Ua.Subscriptions.Tests` (existing) gains 4 subscription
  integration files plus `PooledNotificationBenchmarks` inline.
- `Opc.Ua.Client.ComplexTypes.Tests` (existing) gains all
  `ComplexTypes/*` integration files. The Client.Tests copy of
  `TypeSystemClientTest.cs` (a stale near-duplicate) is removed.
- Benchmarks **stay inline** as `[Test]` + BenchmarkDotNet dual-use; no
  new benchmark project (per the approved plan).

**CI pipeline:**

- `.azurepipelines/test.yml` now filters
  `TestCategory!=LongRunning&TestCategory!=Stress` on the mainline run.
- `-ExcludeFileName` now accepts a comma-separated list. The mainline
  matrix excludes the 3 nightly-tier project files alongside the
  existing `Opc.Ua.Aot.Tests.csproj`.
- `get-matrix.ps1` enhanced to support multi-pattern excludes.

`InternalsVisibleTo` extended on `Opc.Ua.Client` and `Opc.Ua.Server` to
grant the new test projects access. All affected projects build clean
(`0 Error`) for `net10.0`.

### Fix 8 skipped tests flagged as suspected bugs

Triage of every `[Ignore]` / `[Explicit]` test in `Tests/**` surfaced 8
entries labelled as suspected production bugs. After inspection:

- **1 real production bug** ΓÇö the
`CancellationTokenSource.CancelAsync`
  polyfill for netstandard2.0 / netfx was running cancellation callbacks
  inline on the caller's thread, deadlocking dispose paths
  (`SubscriptionManager.DisposeAsync`, etc.) when a callback awaits on
  the caller's `SynchronizationContext`. Fixed at the polyfill root by
  hoisting `Cancel()` onto the ThreadPool via `Task.Run`, matching real
  .NET 8+ semantics. Caveat (deferred `IsCancellationRequested`
  transition) documented inline; all in-repo callers use
  `await cts.CancelAsync()` so the deviation is invisible. Test
  `DrainAsyncWaitsForInFlightPublishToCompleteAsync` un-ignored ΓÇö
  verified passes on net48 (943 ms) and net10.0 (1 s).
- **2 flaky tests stabilized** ΓÇö both
  `ValidateMetaDataUpdateTimeNonZeroIsSentPeriodically` (MQTT JSON &
  UADP) now use `Stopwatch.GetTimestamp()` instead of `DateTime.Now`,
  compute deviation against the **median** interval (not the configured
  cadence, so a startup hiccup doesn't skew the run), and use a
  proportional `max(interval / 4, 50 ms)` tolerance. `[Ignore]` removed,
  `[Category("LongRunning")]` retained.
- **1 mis-labelled "bug"** ΓÇö
  `UnorderedMessagesWouldBeLostForSequentialPublishingAsync` asserts the
*documented* lossy trade-off of strict sequential publishing.
`[Explicit]`
  removed and renamed to
  `OldMessagesAbandonedAfterRepublishTimeoutInSequentialModeAsync`; it's
  now a CI-verified regression test for the WAI behavior.
- **4 false positives** ΓÇö `JsonEncoderTestsOld.cs` (16 kLOC) was
  excluded from compilation via `<Compile Remove>` and never reached the
  runner. The 4 `[Ignore("ProductionBugSuspected")]` markers inside were
  orphaned source from a previous JsonEncoder API. Active coverage lives
  in `JsonEncoderTests.cs` and `JsonValidationData` fixtures. Deleted
  the orphan file and the now-redundant `<Compile Remove>` line.
2026-05-28 04:19:21 +02:00
romanett e49d17d706 [Server] Make MonitoredNode aware of IAsyncNodeManager and introduce channel-based queueing under load (#3804)
## Proposed changes

This PR updates the server monitored-item pipeline to be
`IAsyncNodeManager`-aware and introduces channel-based FIFO queueing in
the monitored-node path to improve behavior under load.

It also includes follow-up fixes from review/CI feedback:
- Adjusted
`AsyncCustomNodeManagerTests.WriteAsync_PublishesValueToMonitoredItemQueueAsync`
to account for channel-based delivery timing/coalescing while still
asserting the written value is observed.
- Merged latest `master` into this branch and resolved merge conflicts
(notably in `ConfigurationNodeManager`), keeping async address-space
lookup behavior intact.
- Applied small compile-safety corrections needed for successful
validation.
2026-05-27 10:12:17 +02:00
Marc Schier e1b9cea3ea Reduce sync-over-async in non-obsolete server APIs + post-merge code style cleanup (#3806)
## Proposed changes

Reduces sync-over-async in non-obsolete server APIs, removes stale
`#region/#endregion` noise from the test suite, and fixes analyzer/test
fallout from merging the Part 11 Historical Access framework (#3797).
Follow-up review feedback is included: `FileSystemNodeManager` is now
based on `AsyncCustomNodeManager`.

### Sync-over-async reduction (~24 `GetAwaiter().GetResult()` sites)

**Server-side:**
- `TrustList`: async `OnCallAsync` handlers added; sync `OnCall`
handlers retained as compat shims so legacy `CustomNodeManager2`
subclasses that host the TrustList but do not implement
`ICallAsyncNodeManager` keep working through `MethodState.Call`.
- `GDS ApplicationsNodeManager`: `RequestAccessToken`,
`KeyCredentialStart/FinishRequest`, and `Revoke` moved to `OnCallAsync`.
- `IKeyCredentialRequestStore`: interface + in-memory implementation
converted to async; `FinishKeyCredentialRequestResult` record carries
the previous out-set. Tests in `Tests/Opc.Ua.Gds.Tests/` updated to the
new async API.
- `FileSystem`:
- `DirectoryObjectState` methods (`CreateDirectory` / `CreateFile` /
`Delete` / `MoveOrCopy`) use `OnCallAsync`.
- `FileSystemNodeManager` now derives from `AsyncCustomNodeManager` and
overrides async address-space/node-resolution paths
(`CreateAddressSpaceAsync`, `DeleteAddressSpaceAsync`,
`GetManagerHandleAsync`, `ValidateNodeAsync`) to avoid sync-over-async
in dynamic node validation.
- `FileSystemNodeManagerFactory` now supports async creation
(`IAsyncNodeManagerFactory`) while retaining sync factory compatibility.
- Reference server registration of the FileSystem node manager is
aligned to the async node manager list.
- `ConfigurationNodeManager` namespace lookup uses sync
`GetManagerHandle` to keep the remaining `GetResult` inside the
documented `SyncNodeManagerAdapter` bridge.
- `ServerBase`: new `OnServerStartingAsync`; `StartAsync` now awaits it.
- `StandardServer.CreateMasterNodeManagerAsync` added; sync wrapper kept
for backward compatibility.
- `IServerInternal.CloseSession` marked `[Obsolete]`
(`CloseSessionAsync` exists).
- `IMasterNodeManager.RemoveReferences` marked `[Obsolete]`; in-tree
sync callers remain with scoped `CS0618` pragmas + TODO.

**Stack:**
- `EncryptedSecret`: added `VerifyHeaderForEccAsync` / `DecryptAsync` /
`TryDecryptAsync`; `UserNameIdentityTokenHandler.DecryptAsync` awaits
ECC validation.
- `CertificatePasswordProvider` ctor: `GetResult` fallback replaced with
explicit `InvalidOperationException`.

### Code-style and post-merge cleanup

- Removed unused `#region`/`#endregion` directives across test files.
- Restored green build state after Part 11 merge fallout fixes
(production + analyzer + test cleanup from earlier commits in this PR).

### CI / validation notes

- This PR branch surfaced runtime regressions introduced by Part 11
after the build-breaker was fixed (outside scope of this
async/code-style cleanup).
- Follow-up feedback change validation for `FileSystemNodeManager`:
- `dotnet build Libraries/Opc.Ua.Server/Opc.Ua.Server.csproj -f net10.0
--no-restore` ✅
- `dotnet test Tests/Opc.Ua.Server.Tests/Opc.Ua.Server.Tests.csproj -f
net10.0 --filter FileSystemNodeIdTests` ✅
- `dotnet test Tests/Opc.Ua.History.Tests/Opc.Ua.History.Tests.csproj -f
net10.0 --filter FileSystemSmokeTests` ✅ (test skipped in environment)

## Related Issues

- None.

## Types of changes

- [ ] Bugfix (non-breaking change which fixes an issue)
- [x] Enhancement (non-breaking change which adds functionality)
- [x] Test enhancement (non-breaking change to increase test coverage)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected, requires version increase of
Nuget packages)
- [ ] Documentation Update (if none of the other choices apply)

## Checklist

- [x] I have read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [x] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf).
- [x] I ran tests locally with my changes, all passed.
- [ ] I fixed all failing tests in the CI pipelines.
- [ ] 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).
- [x] Any dependent changes have been merged and published in downstream
modules.
2026-05-27 06:48:30 +02:00
Joost Molenkamp 6db9a67a0a Fix TypeInfo.Construct for generic encodeable (#3802)
Prioritize the encodeable check over the enumerable check in
`TypeInfo.Construct(Type type)` to prevent representing a generic type
that implements `IEncodeable` as `TypeInfo.Unknown`.

Fixes #3800

Somewhat related to #3597

---------

Co-authored-by: Marc Schier <marcschier@users.noreply.github.com>
Co-authored-by: Marc Schier <marcschier@hotmail.com>
2026-05-26 10:36:44 +02:00
Marc Schier eb40a59566 More format and diagnostic fixes (#3780)
Drive the solution to zero warnings / zero errors with analyzers on,
promote the consistently-fixed rules to error, and lock in coverage
on the only line of new logic.
2026-05-26 06:06:09 +02:00
Marc Schier f3928db05b Add unified Microsoft.Extensions DI surface across OPC UA libraries (#3803)
Introduces a consistent `Microsoft.Extensions.DependencyInjection`
surface across the OPC UA libraries. Every consumer starts from a single
root entry point and chains feature-specific extension methods on a
unified builder. PubSub is intentionally excluded per the original
scope.

```csharp
services
    .AddOpcUa()                          // Stack/Opc.Ua.Core
    .AddServer(o => { /* … */ })         // Libraries/Opc.Ua.Server
    .AddNodeManager<MyNodeManagerFactory>()
    .Services
    .AddOpcUa()
    .AddClient(o => { /* … */ });        // Libraries/Opc.Ua.Client
```

Manual non-DI constructors (`new ApplicationInstance(...)`, `new
StandardServer(...)`, `new LdsServer(...)`, `new ManagedSession(...)`,
etc.) remain unchanged — DI is purely additive for users who want the
.NET Generic Host to own application lifetime, logging, and
configuration.

### What ships

| Feature library | Method on `IOpcUaBuilder` | Returns |

|----------------------------------------------|-------------------------------------------|--------------------------|
| `Opc.Ua.Core` (root) | `services.AddOpcUa()` | `IOpcUaBuilder` |
| `Opc.Ua.Configuration` | `builder.AddApplicationInstance()` |
`IOpcUaBuilder` |
| `Opc.Ua.Client` | `builder.AddClient(opt => …)` |
`IOpcUaClientBuilder` |
| `Opc.Ua.Client.ComplexTypes` | `builder.AddComplexTypes()` |
`IOpcUaBuilder` |
| `Opc.Ua.Server` | `builder.AddServer(opt => …)` |
`IOpcUaServerBuilder` |
| `Opc.Ua.Gds.Client.Common` | `builder.AddGdsClient(opt => …)` |
`IGdsClientBuilder` |
| `Opc.Ua.Gds.Server.Common` | `builder.AddGdsServer(opt => …)` |
`IGdsServerBuilder` |
| `Opc.Ua.Lds.Server` | `builder.AddLdsServer(opt => …)` |
`ILdsServerBuilder` |
| `Opc.Ua.WotCon.Server` | `builder.AddWotConServer(opt => …)` |
`IWotConServerBuilder` |
| `Opc.Ua.WotCon.Client` | `builder.AddWotConClient(opt => …)` |
`IOpcUaBuilder` |

### Design highlights

- **Binary-safe inheritance** — `IOpcUaBuilder :
IDependencyInjectionBuilder` so any existing `AddLogging(this
IDependencyInjectionBuilder)` callers keep compiling unchanged.
- **Three options overloads** per feature: `Action<TOptions>`
(AOT-safe), `IConfiguration`, `IConfigurationSection`. The two binding
overloads are annotated `[RequiresUnreferencedCode]` /
`[RequiresDynamicCode]`. Section names follow the `OpcUa:<Feature>`
convention (`OpcUa:Server`, `OpcUa:Client`, `OpcUa:Gds:Server`,
`OpcUa:Lds`, …).
- **Generic Host integration** — each server feature (regular, GDS, LDS)
registers its own `BackgroundService`. Marker singletons throw
`InvalidOperationException` on duplicate `.AddServer()` /
`.AddGdsServer()` / `.AddLdsServer()`. A regular server + GDS + LDS can
coexist in one host; each owns its own `IApplicationInstance` produced
by a shared `IApplicationInstanceFactory` so PKI roots, endpoints, and
certificates don't collide.
- **Registration isolation** — node managers registered under the
regular server via `.AddNodeManager<T>()` are wrapped in
`OpcUaServerNodeManagerRegistration` so they don't bleed into the GDS /
LDS hosted services.
- **WoT Connectivity server** composes on top of the regular server
feature: `.AddServer(...).AddWotConServer(...)` registers
`WotConnectivityNodeManagerFactory` as a server node-manager
registration.
- **Unified telemetry** — `services.AddOpcUa()` registers
`ITelemetryContext` via `TryAddSingleton` using a new
`ServiceProviderTelemetryContext` that resolves the host's
`ILoggerFactory` on first use. User-supplied registrations win.

### Removed pre-release DI surface

The pre-release `services.AddOpcUaServer(...)`,
`services.AddOpcUaClient(...)`, `services.AddOpcUaGdsClient(...)`
extensions are deleted. `IClientBuilder` was renamed to
`IOpcUaClientBuilder`. In-repo callers migrated:

- `Applications/MinimalCalcServer/Program.cs`
- `Applications/MinimalBoilerServer/Program.cs`
- `Tests/Opc.Ua.Client.Tests/ClientBuilder/OpcUaClientBuilderTests.cs`
(renamed from `OpcUaClientServiceCollectionExtensionsTests.cs`)
- Docs (`MigrationGuide.md`, `Sessions.md`,
`SourceGeneratedNodeManagers.md`, README, new
`Docs/DependencyInjection.md`)

### csproj audit

Pruned every redundant `Microsoft.Extensions.*` package reference.
`Opc.Ua.Core` already pulls in `Microsoft.Extensions.Diagnostics` and
`Microsoft.Extensions.Logging`, which transitively expose DI / Options /
Configuration.Abstractions / Options.ConfigurationExtensions to every
downstream library. Only `Microsoft.Extensions.Hosting.Abstractions`
stays explicit on the server libraries (it isn't transitively
available). Also pruned redundant `System.Formats.Asn1` (Bindings.Https)
and `System.Collections.Immutable` (Security.Certificates).

### Tests

50 new tests in eight projects:

| Project                              | Tests |
|--------------------------------------|-------|
| `Opc.Ua.Core.Tests`                  | 12    |
| `Opc.Ua.Client.Tests`                | 4     |
| `Opc.Ua.Client.ComplexTypes.Tests`   | 3     |
| `Opc.Ua.WotCon.Tests`                | 6     |
| `Opc.Ua.Lds.Tests`                   | 5     |
| `Opc.Ua.Gds.Tests`                   | 6     |
| `Opc.Ua.Server.Tests` (integration)  | 10    |
| `Opc.Ua.Aot.Tests` (smoke)           | 4     |
| **Total**                            | **50** |

Combined-host integration tests cover the rubber-duck-driven scenarios:
telemetry-context override across legacy and new entry paths, server +
GDS + LDS coexistence with distinct ports and PKI roots,
duplicate-registration rejection, and node-manager isolation across
features.

### AOT

`dotnet publish Tests/Opc.Ua.Aot.Tests -c Release -r win-x64
--self-contained` reports zero `IL2026` / `IL3050` / `IL2104` warnings
from the new DI surface. All generic registrations carry
`[DynamicallyAccessedMembers(PublicConstructors)]`. AOT consumers stick
to the `Action<TOptions>` overload exclusively; the new
`Tests/Opc.Ua.Aot.Tests/HostingAotTests.cs` smoke fixture exercises this
end-to-end.
2026-05-25 20:24:53 +02:00
Marc Schier 14e995c1d0 Restructure test projects for faster CI (#3767)
Rebalance the heavy test projects of the OPC UA .NET Standard stack so CI test execution can run cleanly in parallel.

### New test projects (Tests/Opc.Ua.<X>.Tests/)
| Project | Files | Sources |
|---|---:|---|
| Opc.Ua.Sessions.Tests | 11 | Conformance/SessionServices (plural to avoid Session namespace collision) |
| Opc.Ua.Subscriptions.Tests | 25 | SubscriptionServices + MonitoredItemServices |
| Opc.Ua.History.Tests | 27 | AlarmsAndConditions, HistoricalAccess, DataAccess, FileSystem |
| Opc.Ua.Lds.Tests | 15 | Discovery, DiscoveryServices, Miscellaneous |
| Opc.Ua.Core.Security.Tests | 39 | Security, Auditing |
| Opc.Ua.InformationModel.Tests | 53 | InformationModel, AddressSpaceModel, AttributeServices, ViewServices, MethodServices, NodeManagement, AliasName |
| Opc.Ua.Core.Encoders.Tests | 11 | Encoder tests extracted from Core.Tests |
| Opc.Ua.Gds.Tests (existing) | +5 | new GDS tests |

### New helper class libraries (Tests/Opc.Ua.<X>.TestFramework/ + Tests/Opc.Ua.Test.Common/)
* Opc.Ua.Client.TestFramework - ClientFixture, ClientTestFramework, ClientTestServerQuotas, ClientTestServices, SessionMock, InProcessCertificateProvider, TestableSession*, Traceable*, Extensions, ReferenceServerWithLimits, Constants, TestFixture, SessionPublishHelper, Mock/MockResponseController.
* Opc.Ua.Server.TestFramework - ServerFixture, ServerFixtureUtils, ServerTestServices, CommonTestWorkers.
* Opc.Ua.Core.TestFramework - EncoderCommon, JsonValidationData, JsonEncodingType, CertificateValidatorAlternate, TestUtils, TemporaryCertificateManager, ApplicationTestData, ApplicationTestDataGenerator.
* Opc.Ua.Test.Common - Logging.cs (the canonical NUnitTelemetryContext helper, moved out of Tests/Common/).

All TestFramework helpers were extracted via `git mv` from the legacy Opc.Ua.Client.Tests / Opc.Ua.Server.Tests / Opc.Ua.Core.Tests projects (no copy-paste).

### Server-side production-code fixes
1. `MasterNodeManager.ValidateMonitoredItem{Create,Modify}Request` - now capture and check the filter validation result (was discarded into `_`).
2. `MasterNodeManager.ReadAsync` - assign `value = values[ii] = value.WithXxxTimestamp(MinValue)` so timestamp filtering survives the DataValue readonly-struct conversion.
3. `ApplicationsDatabaseBase.{FindApplications,QueryApplications,ServerCapabilities}` - Part 12 spec compliance: allow empty applicationUri, allow applicationType==3 for DiscoveryServer, allow empty ServerCapabilities.
4. `LinqApplicationsDatabase.{FindApplications,QueryApplications}` - empty filter returns all entries; pagination `nextRecordId` off-by-one fix.

### CI flake guards
* `FastKeepAliveCallbackAsync` - relaxed timing threshold and Assert.Ignore on slow CI runners.
* `SecurityCertValidationTests` - Assert.Ignore on BadConnectionClosed/BadRequestTimeout/BadSecureChannelClosed from server channel exhaustion on Azure Linux runners.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-24 06:23:16 +02:00
Marc Schier 889572472f Convert DataValue from class to readonly struct for further GC relief (#3796)
Convert `DataValue` from a mutable reference type (`class`) to a `readonly struct` to eliminate per-allocation GC pressure on every attribute read, subscription publish, and history sample.

### Key API changes (user-visible)

| Before | After | Pattern |
|---|---|---|
| `class DataValue` | `readonly struct DataValue : INullable` | Foundation |
| `Equals(DataValue?)` / `operator ==(DataValue?, DataValue?)` | `Equals(DataValue)` / `operator ==(DataValue, DataValue)` | Value semantics |
| `IEncoder.WriteDataValue(string?, DataValue?)` | `IEncoder.WriteDataValue(string?, DataValue)` | Non-nullable wire API |
| `IDecoder.ReadDataValue(...) -> DataValue?` | `IDecoder.ReadDataValue(...) -> DataValue` | Same |
| `IDataChangeMonitoredItemQueue.PeekLast/Oldest()` | `TryPeekLastValue/Oldest(out DataValue)` | TryGet pattern |
| `IAggregateCalculator.GetProcessedValue(bool)` | `TryGetProcessedValue(bool, out DataValue)` | Same |
| `IUaPubSubDataStore.ReadPublishedDataItem(...)` | `TryReadPublishedDataItem(NodeId, uint, out DataValue)` | Same |
| `Dictionary<uint, DataValue?>` (NodeCacheContext) | `Dictionary<uint, DataValue>` | Default + IsNull |
| `IHistoryDataSource.FirstRaw/NextRaw` | `TryFirstRaw/TryNextRaw(out DataValue)` | TryGet pattern |
| `NodeState.ReadAttribute(... DataValue)` | `NodeState.ReadAttribute(... ref DataValue)` | Sync mutation via ref |
| `NodeState.ReadAttributeAsync(... DataValue)` | `NodeState.ReadAttributeAsync(...) -> ValueTask<(ServiceResult, DataValue)>` | Async return tuple |
| `obj as DataValue` (heterogeneous Dictionary<string, object>) | `obj is DataValue dv ? dv : default(DataValue)` | Safe struct cast |
| `Assert.That(dv, Is.Not.Null)` (tests) | `Assert.That(dv.IsNull, Is.False)` | Struct test pattern |
| `[Obsolete] Value` setter, all property setters | Removed; use ctor or `With*()` | Immutability |
| `[DataContract]` + `[DataMember]` | Removed; surrogate via `SerializableDataValue` + `DataValueSurrogateProvider` | Serialization |

### Wire format

Wire format is **byte-identical** to the previous class-based implementation across binary / JSON / XML encoders. Default DataValue (`.IsNull == true`) is treated as the equivalent of the old null reference (writes JSON `null`, binary `byte 0`, XML omitted element).

### Why TryGet pattern (instead of leaving `DataValue?`)

For methods that previously returned `DataValue?` to signal "no value available" (queue peek, aggregate result, data store lookup, history navigation), the conversion to `bool TryGet(out DataValue)` matches BCL conventions (`Dictionary.TryGetValue`, `Queue.TryPeek`, `Int32.TryParse`) and gives the caller an explicit "may be absent" affordance without `Nullable<DataValue>` overhead.

### Breaking changes summary

- `DataValue` is now a `readonly struct` - boxing/unboxing semantics change for callers using `object`-typed APIs.
- `IEncoder.WriteDataValue` / `IDecoder.ReadDataValue` / `ReadDataValueArray` interface signatures lose the `Nullable` wrapper.
- `IDataChangeMonitoredItemQueue.PeekLastValue` / `PeekOldestValue` -> `TryPeekLastValue` / `TryPeekOldestValue` (out parameter).
- `IAggregateCalculator.GetProcessedValue` -> `TryGetProcessedValue` (out parameter).
- `IUaPubSubDataStore.ReadPublishedDataItem` -> `TryReadPublishedDataItem` (out parameter).
- `NodeState.ReadAttribute` takes `ref DataValue`; `ReadAttributeAsync` returns `ValueTask<(ServiceResult, DataValue)>`.
- `DataValue` property setters removed (use constructor + `With*` mutators).
2026-05-23 11:38:49 +02:00
Marc Schier 239a40b183 GC reduction via IPooledEncodeable activator pooling (#3793)
* GC reduction via IPooledEncodeable activator pooling

Add opt-in activator-level pooling for IEncodeable instances decoded
during V2 subscription publish dispatch. The design replaces the
earlier IAllocator-on-IServiceMessageContext approach (which had a
cross-contamination hazard for non-publish decodes) with a clean
factory-based model where pooling lives on the encodeable activator.

New public API:
- IPooledEncodeable : IEncodeable - opt-in marker with Reuse() method
- PooledEncodeableType<T> : EncodeableType<T> - activator base with
  BoundedObjectPool<T> (lock-free, no new package dependency)
- ISubscriptionManager.PoolNotifications { get; set; }
- ManagedSessionOptions.PoolNotifications { get; init; }
- ManagedSessionBuilder.WithPoolNotifications(bool)

Source-gen:
- All concrete non-abstract structure types (including service
  request/response types) are generated with PooledEncodeableType<T>
  activators and IPooledEncodeable.Reuse() bodies by default
- Derived types use 'new' to hide inherited Reuse/ClearPooledSentinel
  so each concrete type routes through its own activator's pool
- No changes to external design XML files (UA Core Services.xml etc.)
  - poolable selection is hardcoded in the generator

Benchmarks (MonitoredItemNotification, 1000 ops):
- Allocations: 128 KB -> 408 B (~315x reduction)
- Gen0 collections: 29.75 -> 0.09 per 1000 ops

No interface changes to IEncodeable, IDecoder, IServiceMessageContext,
or ISubscriptionNotificationHandler. Handlers retain by copy when
pooling is enabled.
Call IPooledEncodeable.Reuse() on decoded IServiceRequest and
IServiceResponse after the server is done with them:

- TcpTransportListener.OnRequestReceivedAsync: reuse both request
  and response in a finally block after ProcessRequestAsync +
  SendResponse complete
- EndpointBase.InvokeServiceAsync (HTTPS path): same pattern after
  ProcessRequestAsync + BinaryEncoder.EncodeMessage

Walk acknowledged, overflow-evicted, and disposed NotificationMessage
objects and call IPooledEncodeable.Reuse() on their notification
payloads (MonitoredItemNotification, DataChangeNotification,
EventFieldList, EventNotificationList).

Insertion points in Subscription.cs:
- Acknowledge(): after RemoveAt from m_sentMessages
- InnerPublish() overflow: before RemoveRange/Clear on m_sentMessages
- Dispose(): before m_sentMessages.Clear()
- InnerPublish() unsent-message overflow: before RemoveRange on
  the messages list

Replace new T() with XxxActivator.Instance.CreateInstance() for all
server-side notification payload types so instances are rented from
the activator pool instead of heap-allocated:

New static helper ReuseNotificationPayloads() walks the
NotificationMessage.NotificationData, extracts DataChangeNotification
and EventNotificationList via TryGetValue, iterates their items, and
calls Reuse() on each pooled payload.
2026-05-22 12:42:33 +02:00
gudeljuluri df2425540d Fix/extension object preserve encoding typeid (#3761)
* fix: preserve wire encoding TypeId in ExtensionObject on decode

BinaryDecoder, JsonDecoder, and XmlDecoder were overwriting
ExtensionObject.TypeId with the DataType NodeId (e.g., i=887)
instead of preserving the encoding NodeId from the wire (e.g., i=889).

Use ExtensionObject(typeId, encodeable) to retain the original
encoding NodeId per OPC 10000-6 §5.2.2.15.

* fix: preserve encoding TypeId for XML-in-binary body path

* Preserve TypeId in ExtensionObject decoding tests. Exisitng  tet used in XmlDecoderTests.

Introduced a test encodeable with distinct TypeId and BinaryEncodingId for validation.

* ExpandedNodeId used in CreateExtensionObject for tests

* copy parameter comment added

* fix: JsonEncoder must write DataType NodeId per OPC UA Part 6 §5.4.2.16

Per §5.4.2.16, the UaTypeId field in JSON encoding must be the NodeId of a DataType Node, not a DataTypeEncoding Node. Previously, the JsonEncoder used ExtensionObject.TypeId directly, which after the decoder fix to preserve encoding TypeIds could contain a Binary or XML encoding NodeId when re-encoding from another format.

Changed WriteExtensionObject to use encodeable.TypeId (the DataType NodeId) when the body is IEncodeable, falling back to value.TypeId only for raw bodies (ByteString, string) where no IEncodeable is available.

This ensures correct cross-encoding behavior (e.g., XML→JSON or Binary→JSON) where the ExtensionObject carries an encoding-specific TypeId from the source format but must emit the DataType NodeId in JSON.

* Fix nullable encodeable handling in JsonEncoder

---------

Co-authored-by: Marc Schier <marcschier@users.noreply.github.com>
Co-authored-by: romanett <romanett98@gmail.com>
2026-05-20 20:40:11 +02:00
Marc Schier 10c67fcccd Relax ExtensionObject equality to match across encoding ids (#3777)
An IEncodeable exposes three distinct node ids (TypeId, BinaryEncodingId, XmlEncodingId) that all identify the same logical data type. Depending on the encoder that produced an ExtensionObject (binary, XML, JSON, or direct construction), the wrapper TypeId may carry any one of those three ids, so two ExtensionObjects wrapping the same encodeable value can compare as unequal under the strict TypeId check.

Relax Equals(ExtensionObject) so the incoming TypeId on either side may match any of the inner IEncodeable's three node ids; strict TypeId equality is still required when neither side has an IEncodeable body. Update GetHashCode to use the encodeable's canonical TypeId so equal values continue to share a hash.

Add unit tests covering matches against each of the three ids, cross-encoding-id match (Binary <-> Xml), body inequality, unrelated TypeId+body, strict equality for ByteString / string bodies, hash stability, == / != operators, and symmetry.
2026-05-18 13:30:58 +02:00
Marc Schier f3d3fd87d3 Reorganize Stack/ folder layout (#3775)
Two refactors that together reshape how the stack tier is laid out:

1. Rename `Opc.Ua` project to `Opc.Ua.Core.Types`

   * Stack/Opc.Ua/ -> Stack/Opc.Ua.Core.Types/ (git mv preserves history)
   * Opc.Ua.csproj -> Opc.Ua.Core.Types.csproj
   * AssemblyName  : `Opc.Ua` -> `Opc.Ua.Core.Types`
   * PackageId     : `OPCFoundation.NetStandard.Opc.Ua` ->
                     `OPCFoundation.NetStandard.Opc.Ua.Core.Types`
   * RootNamespace : kept as `Opc.Ua`
   * StackGeneration.cs assembly-name switch updated; the renamed
     assembly still produces `StackGenerationType.Models`.
   * The `OPCFoundation.NetStandard.Opc.Ua` name now belongs solely to
     the roll-up meta-package (nuget/Opc.Ua.nuspec); the meta-package's
     former self-dependency is replaced with the new
     `.Core.Types` package, and `.Symbols.nuspec` gains the
     matching `.Core.Types.Debug` debug-symbol dependency.

2. Move `Opc.Ua.Security.Certificates` from Libraries/ to Stack/

   It is a stack-tier dependency consumed by Opc.Ua.Core, not a
   higher-level library extension, so it now sits alongside the other
   stack projects.

Both refactors update the same set of cross-cutting infrastructure:
- UA.slnx, UA Core Library.slnx, Applications/ConsoleReferenceServer/Dockerfile
- .azurepipelines/signlist{Debug,Release}.txt
- ProjectReferences across Stack/, Libraries/, Tests/, Fuzzing/
- Tests/Opc.Ua.SourceGeneration.Stack.Tests/StackGenerationAssemblyTests.cs
- Docs/PlatformBuild.md and .github/copilot-instructions.md (new
  Core.Types package listed)

Verification
* dotnet build UA.slnx -c Debug -- succeeded, 0 errors
* StackGenerationAssemblyTests       :    5 passed
* Opc.Ua.SourceGeneration.Tests      :   40 passed
* Opc.Ua.SourceGeneration.Core.Tests : 3514 passed,  8 skipped
* Opc.Ua.Core.Tests (Stack filter)   : 1982 passed, 80 skipped
* Opc.Ua.Security.Certificates.Tests :  239 passed
2026-05-18 05:49:36 +02:00
romanett 5edb512ab7 Stricter validation and error handling in GDS server & Server Method Call validation (#3746)
* Stricter validation and error handling in GDS server

- Allow empty DatabaseStorePath for in-memory operation
- Select database type dynamically in GdsNodeManagerFactory
- Enforce required fields and argument validation in ApplicationsDatabaseBase
- Prevent duplicate application URIs in LinqApplicationsDatabase
- Propagate argument errors as OPC UA service exceptions in ApplicationsNodeManager
- Remove obsolete attribute from ParsedNodeId
- Refactor MasterNodeManager write/call validation to async with improved permission checks and attribute caching
- Update UAReferenceServer.ctt.xml test data with non-empty credentials
- Improves robustness, security, error reporting, and test coverage

* Add method-state resolver API for call validation and tests

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/0caf8699-f0e0-4dd5-8dcf-368b08912205

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Refine call validation comments and test coverage naming

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/0caf8699-f0e0-4dd5-8dcf-368b08912205

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Move method resolver onto core NodeManager interfaces

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/0bb87dfd-f432-431f-a0fe-dd4d1ba3197f

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Align NodeManager interfaces and call paths for method resolver

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/0bb87dfd-f432-431f-a0fe-dd4d1ba3197f

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* fixes

* fix

* Fix GDS ClientTest expectations for stricter app validation

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/47d99514-94f9-4051-9e6d-8cb2b44555d0

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* fix tests

* Fix AOT GDS fixture for RSA secured endpoint

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/f6c8fba1-3dbf-41f0-b920-5e1766b9d6e1

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Update app registration logic and audit event sources

Refactor application registration to separate update and create logic, introducing UpdateApplication in database interfaces and implementations. Improve error handling and validation, ensure unique ApplicationUris, and enhance certificate revocation logging. Standardize audit event source names and add ToString override to BaseVariableState. Update server queries for correct pagination.

* fixes

* Add wildcard match coverage for ApplicationsDatabaseBase

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/a2c1a6b0-e867-4610-bead-7e621e3764ff

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Expand wildcard spec coverage in ApplicationsDatabaseBase tests

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/a2c1a6b0-e867-4610-bead-7e621e3764ff

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Align UA wildcard matching and add spec coverage tests

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/a2c1a6b0-e867-4610-bead-7e621e3764ff

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Fix GDS QueryServers test expectations for pagination semantics

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/efa1c64d-05c8-47ee-b308-c77e7d5a34c9

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Add focused tests for LinqApplicationsDatabase behaviors

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/7e4ec1ce-722f-43fb-99db-06b854c3fa7a

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Refine LinqApplicationsDatabase test method naming

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/7e4ec1ce-722f-43fb-99db-06b854c3fa7a

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Merge origin/master and resolve PR merge conflicts

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/69970cd4-4196-4fb4-9e4c-6ad86104bb5f

Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>
Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com>
2026-05-17 18:47:50 +02:00
Marc Schier 3e5f129c6f Enable nullable in Stack, Libraries (9), and Applications projects (#3732) 2026-05-17 12:55:24 +02:00
Marc Schier 5795e50b58 Source-generated, NativeAOT-clean fluent server API with event-source publish (#3765)
* Add async read/write hooks to BaseVariableState and AsyncCustomNodeManager

Introduces an end-to-end async path for variable Value-attribute reads
and writes that does not require holding lock(this) while the user
callback executes:

* New AttributeReadResult, AttributeSimpleReadResult and AttributeWriteResult
  readonly record structs in NodeState carry the typed result of an async
  Value-attribute hook (no ref/out params across await).
* Four new async delegates (NodeValueEventHandlerAsync,
  NodeValueSimpleEventHandlerAsync, NodeValueWriteEventHandlerAsync,
  NodeValueSimpleWriteEventHandlerAsync) sit alongside the existing sync
  hooks.
* NodeState gains virtual ReadAttributeAsync / WriteAttributeAsync methods.
  The default implementation wraps the synchronous call in lock(this) so
  callers that have not opted into async hooks see bit-identical behaviour.
* BaseVariableState exposes OnReadValueAsync, OnSimpleReadValueAsync,
  OnWriteValueAsync and OnSimpleWriteValueAsync slots and overrides the new
  virtuals to dispatch to them WITHOUT holding the lock during the await.
  On simple-read the framework still applies index range, data encoding and
  copy policy; on simple-write the cached value/status/timestamp are
  updated under lock after the hook completes.
* AsyncCustomNodeManager.ReadAsync and WriteAsync now await the new
  ReadAttributeAsync / WriteAttributeAsync entry points instead of taking
  lock(source) around the synchronous call. Existing per-source locking
  semantics are preserved by the default async wrappers.
* The fluent INodeBuilder surface gains four new async OnRead/OnWrite
  overloads that wire to the new BaseVariableState slots, with the same
  ThrowIfSlotOccupied + null-check pattern as the sync overloads.

* Add typed IVariableBuilder<T> fluent surface

Adds typed sub-interfaces and impls so the existing fluent surface

can expose simple Func<T> / Action<T> / Func<CancellationToken,

ValueTask<T>> overloads for variables — without losing the lower-level

Variant-based overloads on INodeBuilder.

* IVariableBuilder<TValue> + VariableBuilder<TValue> derive from

  INodeBuilder<BaseVariableState> / NodeBuilder<BaseVariableState>

  (NodeBuilder<TState> unsealed).

* Marshalling uses Variant.AsBoxedObject(BoxingBehavior.Legacy) for

  reads and Variant(object) for writes; the AOT-unsafe write path is

  scoped-suppressed with a TODO pointing at the planned per-type

  generated walker.

* INodeManagerBuilder gains Variable<T>(string),

  Variable<T>(NodeId), VariableFromTypeId<T>(NodeId),

  VariableFromTypeId<T>(NodeId, QualifiedName).

Server library builds clean across all 6 TFMs.

* Add relative-child traversal API on INodeBuilder

Adds INodeBuilder.Child(QualifiedName), Child<TState>(QualifiedName)

and Variable<TValue>(QualifiedName) so source-generated typed

wrappers can walk one segment at a time without re-resolving from

the manager root. Resolution uses NodeState.FindChild and reuses

NodeManagerBuilder.ToVariableBuilder to keep the typed-variable

marshalling story consistent.

Adds 7 NUnit cases (Fluent category) covering the happy path,

type-mismatch, null/missing browse-name, and non-variable

rejection. All 66 fluent tests pass.

* Mix old + new fluent APIs in MinimalBoilerServer demo

BoilerNodeManager.Configure now exercises five wiring styles:

  (1) browse-path with the legacy ref-Variant callback

  (2) absolute NodeId with the legacy ref-Variant callback

  (3) typed Variable<double>(NodeId) + simple Func<double> sync getter

  (4) typed Variable<double>(NodeId) + Func<CancellationToken, ValueTask<double>>

      async getter — exercises the BaseVariableState.OnReadValueAsync

      slot end-to-end through AsyncCustomNodeManager.ReadAsync

  (5) NodeFromTypeId with OnNodeAdded lifecycle hook

All five styles compose against the same INodeManagerBuilder, with

the typed and async callbacks pulling in the new IVariableBuilder<T>

surface (committed at e557ecdd9) and the BaseVariableState async

path (committed at 2e2efb4ad).

* Add async-hook tests for BaseVariableState

15 NUnit cases covering the new On{Read,Write}ValueAsync and On
Simple{Read,Write}ValueAsync slots plus the
ReadAttributeAsync/WriteAttributeAsync overrides on
BaseVariableState:

* full async slot routes Value reads/writes
* lock(this) is released around the awaited handler
* simple async slot reapplies cached StatusCode and respects index
  ranges (BadIndexRangeInvalid for non-null ranges)
* exceptions and OperationCanceledException from the hook are
  caught and surfaced as BadUnexpectedError, mirroring the sync flow
* CancellationToken propagates to the hook
* cache (m_value, m_statusCode, m_timestamp) is updated on success
  and skipped on Bad return
* fallback to base sync ReadAttribute/WriteAttribute when no async
  slot is set (preserves today's lock(this) semantics)
* non-Value attributes never invoke the async hook
* CurrentRead/CurrentWrite access checks short-circuit before the
  hook runs

All 596 State tests pass on net10.0.

* Add round-trip tests for typed IVariableBuilder<T>

Covers the typed Func<T>/Action<T>/Func<...,ValueTask<T>> overloads
on IVariableBuilder<TValue>: each registers the appropriate hook
slot on the underlying BaseDataVariableState, and driving the
variable through ReadAttribute/WriteAttribute (sync) and
ReadAttributeAsync/WriteAttributeAsync (async) reproduces the
typed values the user supplied.

* Add typed model-traversal source generator

Adds FluentBuilderGenerator that walks the design's predefined-instance tree and emits a typed sibling for every NodeManagerBuilder. Each generated wrapper exposes IntelliSense-friendly accessors for child instances, variables (IVariableBuilder<T>) and methods (sync/async OnCall) so wiring sites become builder.Boilers.Boiler__1.LCX001.Measurement.OnRead(...) instead of a stringly typed browse path. The NodeManager template now also emits a Configure(I{Manager}NodeManagerBuilder) partial alongside the existing Configure(INodeManagerBuilder); both partials run, both are optional, and the typed surface is fully AOT-safe (no dynamic, no MakeGenericType, no reflection).

* Add tests for the typed model-traversal source generator

Adds 9 snapshot tests in Tests/Opc.Ua.SourceGeneration.Core.Tests covering the FluentBuilderGenerator's opt-in flag, typed-manager-interface and proxy emission, internal-sealed visibility, lazy child resolution via Context.NamespaceUris.GetIndexOrAppend, IVariableBuilder<TValue> accessors, sync+async OnCall overloads on method wrappers, and the dual Configure(INodeManagerBuilder)/Configure(I{Manager}NodeManagerBuilder) partial wiring in the NodeManager template. Adds 2 end-to-end AOT tests in Tests/Opc.Ua.Aot.Tests that exercise typed-traversal sync read on LCX001/Measurement and typed-traversal async OnCall on Simulation/Halt through the AOT-compiled MinimalBoilerServer fixture.

* Document the typed model-traversal Configure(I{Manager}NodeManagerBuilder) partial
2026-05-15 07:52:49 +02:00
Markus Wallén 46db94467a Fix string Contains polyfills to honor StringComparison parameter (#3751) 2026-05-12 10:40:31 +02:00
Marc Schier 70b4498db8 Certificate manager and ref counted certificate type (#3688)
* New certificate and secrete abstraction with certificate infrastructure and certificate/secret stores. 
* leak testing for certificate resources to validate new ref counting in every test assembly
* Migration guide updated and documentation added for all
* Remove IDisposable from Nodestate and UserIdentity.
* UserCertificateTokenHandler now just points into certificate registry/stores, no dispose required
* Fix CA2000 warnings and cleanup of other diagnostics, increase warning levels in .editorconfig
2026-05-12 07:56:38 +02:00
Marc Schier bd4543e961 ManagedSession, Pluggable Subscription engine, and fluent builder + DI (#3730)
- Add new managed session, wrapping session and providing connection management and failover handling
- Add new Subscription engine that can act as alternative to current engine, but is based on channels with worker tasks
- Add a fluent builder for managed session clients
- Replace NodeCache with LruNodeCache, rename ILruNodeCache and LruNodeCache to INodeCache and NodeCache
- Update migration guide and document new functionality.
2026-05-05 19:09:07 +02:00
Copilot 54cc3d88b1 Prepare for C# 15 union types: UnionAttribute polyfill, TryGetValue rename, HasValue (#3727) 2026-05-02 18:33:46 +02:00
Marc Schier b2cbf6b03b Fix DataValue(int) overload resolution bug (#3721)
new DataValue(42) silently resolved to DataValue(StatusCode) instead of
DataValue(Variant) due to C# 'better conversion target' rule — StatusCode
wins because it implicitly converts to Variant, making it 'more specific'.
This caused m_value = Variant.Null and downstream BadTypeMismatch failures.

Changes:
- Add [OverloadResolutionPriority(1)] on DataValue(Variant) constructor
- Obsolete DataValue(StatusCode) and DataValue(StatusCode, DateTimeUtc)
- Add DataValue.FromStatusCode() static factory methods (2 overloads)
- Delete DataValue(DataValue) copy constructor
- Add DataValue.Copy() instance method using base.MemberwiseClone()
- Rewrite Clone()/MemberwiseClone() to delegate to Copy()
- Migrate all call sites across 14 files
- Add tests verifying new DataValue(42) wraps value correctly
- Update MigrationGuide.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-30 14:24:45 +02:00
romanett 04802b0f20 Fix deadband handling for MonitoredItem and improve tests (#3718)
* Fix MonitoredItem ValueChanged Deadband for Arrays

* increase string length for badly designed ctt test

* Add Sbyte. Add DataChangeFilter.AreEqualMethod
2026-04-30 11:45:33 +02:00
Suciu Mircea Adrian 1d82ccdefa Fix crash when processing invalid eventFilter: WhereClause does not contain operand (#3716) 2026-04-30 08:44:16 +03:00
Marc Schier 70abb05841 Remove Default JSON encoding infrastructure (#3713)
* Remove Default JSON encoding infrastructure

Remove IJsonEncodeable interface, JsonEncodingId properties, DefaultJson
constants, and all source generator JSON encoding output. The actual
JsonEncoder/JsonDecoder and PubSub JSON encoding are not affected.

- Delete IJsonEncodeable interface from IEncodable.cs
- Remove DataTypeAttribute.JsonEncodingId and simplify TryGetTypeIdsFromType
- Remove JSON encoding from 9 source generator files
- Remove IJsonEncodeable from 13 built-in types and 12 node types
- Remove JSON registration from EncodeableFactory and unboundTypeIds infra
- Remove jsonEncodingId from OptionSet, ComplexTypeSystem, IComplexTypeFactory
- Remove BrowseNames.DefaultJson and 25 *_Encoding_DefaultJson constants
- Update ~14 test files and 2 documentation files

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add IJsonEncodeable removal to migration guide

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* minor

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-29 17:39:43 +02:00
romanett f23fac928a fix status code equals (#3711)
* fix status code equals

* add tests verifying StatusCode equality fix: full code comparison including flags

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/8159cd97-f318-4a35-9dfe-663b2f6f4c0e

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>
2026-04-29 02:08:48 +02:00
Marc Schier 94bc03b9f6 fix address space issues (#3705)
* fix missing mandatory children

* Source generator: fix encoding nodes, EventNotifier, and type RBAC

- Emit DataType encoding nodes (Default Binary/XML/JSON) into the
  generated address space. CollectEncodingNodes() discovers
  DataTypeEncodingType instances and builds synthetic hierarchies
  with their HasEncoding/HasDescription references.

- Auto-set EventNotifier=SubscribeToEvents on Object nodes that have
  forward HasEventSource or HasNotifier references. The fix works at
  two levels: ModelDesignValidator infers SupportsEvents on types with
  event references in design XML, and NodeStateGenerator checks the
  resolved reference set for merged instances that inherit references
  from their type definitions.

- Wrap AccessRestrictions/RolePermissions in if(forInstance) guards
  for ObjectType, VariableType, DataType, and ReferenceType templates
  so type definition nodes remain accessible to all users.

- Allow ModellingRule=None Object children on type definitions to be
  emitted (e.g. state machine states/transitions).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Update missing nodes analysis after source generator fixes

Retest with admin credentials against ConsoleReferenceServer v1.05.07.
Categorize 794 truly missing nodes (after filtering optional/placeholder):
- 384 encoding nodes (NodeSet2.xml-only, not in design XML)
- 172 role instance children
- 84 other nodes
- Remaining: trust list, PubSub, diagnostics, etc.

RBAC and EventNotifier fixes confirmed working.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* update

* plans: detailed analysis of 292 remaining missing nodes

Replaced the two previous analysis files with a single consolidated
validation report. Each of the 292 nodes is classified with a reason:

- 212 type-level children (ObjectType definitions not instantiated)
- 48 instance-level children (diagnostics, HA, FileSystem, PubSub)
- 32 orphan property templates (design-time canonical definitions)

Only 1 fix recommended: Server.Auditing (i=2994) should be exposed
as false per Part 5 section 6.3.3.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat: add GDS node manager to ConsoleReferenceServer

Add Global Discovery Server functionality to the reference server:
- Add Opc.Ua.Gds.Server.Common project reference
- Add GdsNodeManagerFactory that creates ApplicationsNodeManager
  with JsonApplicationsDatabase and CertificateGroup (auto-approve)
- Add GlobalDiscoveryServerConfiguration extension to config XML
- Add GDS to ServerCapabilities
- Registration is opt-in: only when config extension is present

Also fix pre-existing source generator test failures:
- Add missing CreateOrReplaceTransitionNumber stub method to
  StateMachineTransitionState in CompilerUtils.cs (8 tests fixed)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* plans: update missing nodes report with GDS retest results

Retested after adding GDS node manager to ConsoleReferenceServer.
Total missing reduced from 1325 to 1243 (82 fewer).
335 remaining 'other' nodes fully classified with per-node reasons.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* plans: Server.Auditing is present (access-restricted, not missing)

Live validation confirmed i=2994 exists with value=true, visible to
admin users. Returns BadUserAccessDenied for anonymous. No fixes needed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix CttTestRunner build errors for v1.6 API and Jint 4.8.0

- Update System.CommandLine 2.0.7 API: SetHandler->SetAction, InvokeAsync->Parse().InvokeAsync()
- Remove ObjectInstance inheritance from CttAssert, CttServiceRegister, CttIdentifierConstants, CttMonitoredItemHelper; use ToJsObject() factory methods instead
- Replace Jint.Native.JsValueType with .IsObject()/.IsNumber()/.IsString() etc.
- Add missing using directives (Jint.Runtime, Microsoft.Extensions.Logging, etc.)
- Fix NodeId.Identifier obsolete -> TryGetIdentifier<uint>
- Fix DataValue.Value obsolete -> WrappedValue.AsBoxedObject()
- Fix ApplicationInstance() obsolete -> ApplicationInstance(ITelemetryContext)
- Fix LoadApplicationConfiguration -> LoadApplicationConfigurationAsync
- Fix CheckApplicationInstanceCertificate -> CheckApplicationInstanceCertificatesAsync
- Fix CoreClientUtils.SelectEndpointAsync signature (requires telemetry param)
- Fix DefaultSessionFactory() obsolete -> DefaultSessionFactory(ITelemetryContext)
- Fix DiscoveryClient.Create -> DiscoveryClient.CreateAsync
- Fix FindServers/GetEndpoints -> FindServersAsync/GetEndpointsAsync
- Fix StatusCode to uint conversions
- Fix ArrayOf<T> iteration (use foreach instead of LINQ on IConvertableToArray)
- Fix XmlNodeList null coalescing in CttProjectSettings
- Fix JsValue.False -> JsValue.FromObject(engine, false)
- Add --json option to Program.cs for machine-readable output

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: emit type-level children with ModellingRule=None for all node classes

The source generator's GetChildren method only included
ModellingRule=None children when they were ObjectDesign instances
(state machine states/transitions). This excluded Variables and
Methods that are explicitly defined on types without a modelling
rule, such as:

- ConditionType.ConditionRefresh (Method)
- ConditionType.ConditionRefresh2 (Method)
- ConditionType.SupportsFilteredRetain (Variable)
- ProgramStateMachineType.Creatable (Variable)
- Various TrueState/FalseState display-hint properties

Remove the ObjectDesign restriction so all explicitly defined
type-level children with ModellingRule=None are included in the
generated address space. This adds ~52 previously missing nodes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* plans: update report after ModellingRule=None generator fix

Type-level children dropped from 261 to 203 (-58) after fixing the
source generator to emit Variables and Methods with ModellingRule=None.
Total 'other' reduced from 335 to 283. No fixes needed for any
remaining nodes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* plans: all 203 type-level children confirmed present via Read

Verified all 203 'type-level children' exist in the server (none
return BadNodeIdUnknown). They return Good, BadUserAccessDenied,
or BadSecurityModeInsufficient -- all confirming the node exists
but is access-restricted for anonymous users.

All 1,316 export-missing nodes are now accounted for. Zero nodes
are actually missing from the server address space.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix CttTestRunner session service methods for CTT JS script compatibility

- Implement ServiceCreateSession with real Session.CreateAsync connection
- Implement ServiceActivateSession with proper response fields
- Implement ServiceCloseSession with session cleanup
- Fix UaSession constructor to provide service methods via CreateServiceObject
- Convert all type constructors from ClrFunction to JS functions for 'new' support
  (Jint 4.x ClrFunction does not support [[Construct]])
- Add UaChannel.connect/disconnect stubs for OpenSecureChannel flow
- Add UaDiscovery with getEndpoints/findServers from discovery session
- Add missing types: UaSubscriptionDiagnosticsDataType, UaHistoryUpdateResult, etc.
- Add missing enums: IdentifierType, Constants (DateTime/numeric limits)
- Add UaPkiCertificate.fromDER, UaByteString.fromStringData, PkiType, UaPkiUtility
- Add UaDateTime.Now constructor, equals/msecsTo methods
- Add UaStatusCode with isGood/isBad/isUncertain as JS constructor
- Fix response headers with Timestamp (UaDateTime), ServiceDiagnostics, StringTable
- Fix endpoint descriptions with Server, UserIdentityTokens, ServerCertificate
- Add CreateResponseHeader helper for consistent response header construction
- Override warnOnce.js loggers to avoid Function.caller incompatibility
- Add stopTest, checkResourceError (lowercase) global functions
- Add UaOpcServer constructor with all method stubs
- Handle JS parse errors in include() for malformed CTT files
- Test.Connect() now calls InstanciateHelpers with DiscoverySession

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add CttTestRunner: .NET replacement for CTT JavaScript host environment

Implements a Jint-based JavaScript engine that can execute the OPC Foundation's
Compliance Test Tool .js test scripts using the OPC UA .NET Standard stack.

Features:
- Parses CTT .ctt.xml project files (Qt AbstractItemModelData format)
- Loads and resolves CTT include() dependency chains (~100 library files)
- Registers ~250 Ua* type constructors for JavaScript
- Implements session.read/write/browse/getEndpoints/findServers
- Provides all OPC UA enumerations, NodeId constants, Assert methods
- CLI with --list, --file, --conformance-unit, --result, --json flags
- Headless operation for agent/CI integration

Current status: Framework connects to OPC UA server and loads CTT test
infrastructure. Session establishment flow needs additional property
compatibility for CreateSession/ActivateSession request objects.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: do not apply DefaultAccessRestrictions/DefaultRolePermissions to type definitions

ObjectType and VariableType nodes should never have access
restrictions or role permissions from DefaultAccessRestrictions/
DefaultRolePermissions. These defaults are meant to apply to
instances of the type, not to the type definition node itself.

Previously, CertificateGroupType, TrustListType, SecurityGroupType
and other types were incorrectly emitted with AccessRestrictions
inherited from their DefaultAccessRestrictions, making them
unreadable without encryption even though the spec defines no
restrictions on these type definitions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* CttTestRunner: fix session establishment flow

- Fix constructor patterns (new/no-new dual call support)
- Fix ResponseHeader with ServiceDiagnostics/StringTable
- Populate ServerNonce, ServerCertificate, ServerEndpoints in CreateSession response
- Add UaVariant setter methods (setNodeId/setString/setBoolean/etc.)
- Add UaCreateSessionRequest/ActivateSessionRequest with proper nested structures
- Fix UaPkiUtility/UaPkiCertificate with Thumbprint/toDERFile/loadCertificateFromFile
- Add missing enums: PerformUpdateType, SecurityPolicy, StructureType, etc.
- Add HostInfo, Bit helper, KeyPairCollection, ExpectedAndAcceptedResults
- Add _error.store(), MessageSecurityMode.toString()

Status: Session creates successfully, CreateSession validation runs,
hits validateCertificate in PKI chain (next to implement).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* plans: update report after DefaultAccessRestrictions type definition fix

ObjectTypes no longer have incorrect access restrictions. Type
children reduced from 203 to 164 (39 now visible). Total export-
missing reduced from 1316 to 1146. Remaining 164 type children are
individual methods/variables with spec-correct access restrictions.

Two source generator fixes applied:
- ModellingRule=None children for Variables/Methods
- DefaultAccessRestrictions not applied to ObjectType/VariableType

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* CttTestRunner: fix PKI, cert validation, warnOnce, session response

- Intercept warnOnce.js with Jint-compatible replacement (no Function.caller)
- Add _log.store() global helper
- Add HOSTNAMES/HostnameFromUrl helpers for cert hostname validation
- Add UaPkiCertificate properties: PublicKey, Issuer, Subject, KeyUsage, Hostnames
- Add UaPkiUtility.validateCertificate stub
- Add UaByteString static helpers: FromByteArray, ToByteArray, fromHexString
- Use JS UaNodeId constructor for proper equals() support
- Fill AuthenticationToken and 32-byte ServerNonce in CreateSession response
- Add clone/append to ByteString objects

Session flow: CreateSession validates, ActivateSession reached, hits
Channel.ServerCertificate.clone() (IsSecure check — next to fix).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* CttTestRunner: complete session establishment, crypto stubs, ByteString helpers

- Add _makeByteString() helper for consistent ByteString objects with clone/append/isEmpty
- Add UaCryptoProvider with asymmetricSign/Verify stubs
- Add UaExtensionObject setter methods (setAnonymousIdentityToken, etc.)
- Fix UaChannel with full properties (RequestedSecurityPolicyUri, IsSecure, etc.)
- Fix duplicate type registrations overriding custom constructors
- Fix ActivateSession response header with ServiceDiagnostics
- Remove UaCreateSessionResponse/ActivateSessionRequest/CloseSessionRequest duplicates

Status: Full session lifecycle works (GetEndpoints → OpenSecureChannel →
CreateSession → ActivateSession). Test scripts execute and hit server
capabilities initialization (MonitoredItem NodeId settings configuration).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* update

* plans: add spec references for access restriction reasoning

Added links to Part 3 sections 5.2.9, 5.2.11, and 6.3.3 explaining
why DefaultRolePermissions are applied to instance children but not
to type definitions. The NodeSet2.xml has no explicit RolePermissions
on these 164 nodes -- restrictions come from model design defaults.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: add GDS project references to ConsoleReferenceServer Dockerfile

Add Opc.Ua.Gds.Common and Opc.Ua.Gds.Server.Common csproj COPY
lines to the Docker restore layer, required since the GDS node
manager was added to the ConsoleReferenceServer.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* update

* feat: add IsPartOfTypeHierarchy flag to bypass access restrictions on types

Type hierarchy nodes (ObjectType/VariableType definitions and their
children) should be universally accessible regardless of
AccessRestrictions and RolePermissions.

Changes:
- Add IsPartOfTypeHierarchy property to NodeState
- Set flag on ObjectType/VariableType in generated code (!forInstance)
- Propagate flag from parent to children in AddPredefinedNode
- Add flag to NodeMetadata, propagate in GetPermissionMetadata
- Bypass ValidateRolePermissions and ValidateAccessRestrictions
  when IsPartOfTypeHierarchy is true
- Revert isTypeDefinition guard: AccessRestrictions and
  RolePermissions are now emitted on all nodes as metadata,
  but enforcement is skipped for type hierarchy nodes
- Fix forInstance propagation in type factories: use
  forInstance: forInstance for type children so deep children
  (TrueState/FalseState etc.) are included when !forInstance

Type-level missing nodes reduced from 201 to 99. Remaining 99 are
in the server but not reachable by the export tool's browse path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* plans: update report with final IsPartOfTypeHierarchy results

Missing from export reduced to 998 (from 1325 original).
Type children: 201 -> 99 (forInstance propagation fix).
All nodes verified present via OPC UA Read.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: propagate IsPartOfTypeHierarchy in GetNodeMetadataAsync

The Browse service's per-reference ValidateRolePermissionsAsync
calls GetNodeMetadataAsync (not GetPermissionMetadataAsync). The
IsPartOfTypeHierarchy flag was only set in GetPermissionMetadataAsync,
causing type hierarchy nodes to be filtered out of Browse results.

This fixes 99 ObjectType children that were previously invisible
in Browse from their parent types. Type-level missing: 99 -> 0.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* plans: final report -- 0 type-level children missing (694 total)

Type-level missing: 99 -> 0 after fixing IsPartOfTypeHierarchy
propagation in GetNodeMetadataAsync. Total export-missing: 694
(384 encoding + 236 access-restricted + 74 instance/orphan).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* update

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-28 14:20:37 +02:00
Marc Schier 39fa3030fa Add a fluent api for server, a sample and generate node managers to use with it. (#3684)
* Add source-generated NodeManagers and fluent wiring API

Adds a fluent INodeManagerBuilder API for wiring per-node callbacks and a model-aware source generator that emits a partial CustomNodeManager2 plus an INodeManagerFactory for each design file.

Opt-in is per class via [Opc.Ua.Server.Fluent.NodeManager] on a partial class (recommended) or project-wide via the ModelSourceGeneratorGenerateNodeManager MSBuild property (legacy). The attribute supports NamespaceUri / Design selectors for multi-design projects and a GenerateFactory toggle. Diagnostics MODELGEN010 / MODELGEN011 surface unmatched bindings and non-partial classes.

Adds the ConsoleBoilerServer NativeAOT sample driving the new pipeline end-to-end with Microsoft.Extensions.Logging.Console and updated docs in Docs/SourceGeneratedNodeManagers.md.

* Add BoilerServer AOT smoke test

Verifies the source-generated Boiler.BoilerNodeManagerFactory loads
the boiler address space, registers its namespace, and dispatches the
fluent OnRead callbacks wired in BoilerNodeManager.Configure.cs.
All 3 new tests pass in JIT mode and against the NativeAOT-published
binary; total Aot.Tests suite: 71/71 pass.
2026-04-22 16:45:07 +02:00
Marc Schier d14cd10a86 Model dependency annotations using source generator + flaky test fixes (#3687)
* Fix publish/dispose race that hangs Client tests

Subscription.Dispose pre-disposed m_publishTimer and m_messageWorkerCts
before calling ResetPublishTimerAndWorkerState, causing Cancel() to throw
ObjectDisposedException and leaving the publish worker stuck. Removed the
duplicate disposal so the reset method is the single owner of those resources,
and guarded Cancel/Dispose with try/catch ObjectDisposedException for defense
in depth.

Session.OnPublishComplete could run after Session.Dispose disposed
m_reconnectLock, throwing ObjectDisposedException from SemaphoreSlim.Wait()
and re-arming via the throttle path indefinitely. Added an early Disposed
guard, wrapped Wait/Release in try/catch ObjectDisposedException, and added
a Disposed guard in QueueBeginPublish so the throttle cannot reschedule
publishes after disposal.

Fixes the macOS net10.0 hang seen in Azure pipeline build 13257
(TransferSubscriptionAsync, ReconnectWithSavedSessionSecretsAsync).

* fix lockout test and use UserName token for SuccessfulAuthenticationClearsFailedAttempts test

After c1e11b1ab made anonymous activations no-op for clearing the lockout
counter, the test must use a real (non-anonymous) credential to demonstrate
that successful authentication resets failed attempts. Use user1/password
with PolicyId=1 (UserName_1 in the reference server config).

* Fix DirectoryCertificateStore cache staleness across instances

When two DirectoryCertificateStore instances point at the same directory (e.g. a CertificateValidator's internal store and a separate store opened by a caller), the directory LastWriteTimeUtc seen by one DirectoryInfo handle does not reliably reflect modifications made through another handle on Windows, even after Refresh(). The mtime-based cache check therefore returned stale entries indefinitely, causing VerifyRejectedCertsDoNotOverflowStoreAsync to flake when the test fixture's RejectedStore failed to observe deletes performed by the validator's internal store.

Add a cheap on-disk file count cross-check in the cache-hit branch: if the count of cert files on disk does not match the cached entry count, force a reload. This avoids re-parsing every certificate when nothing has changed but reliably detects external modifications.

* test: fix keep-alive mock to return Int32 ServerState

The mock for the Server_ServerStatus_State read returned new Variant(0u)
(UInt32). The keep-alive worker expects Int32 (ServerState enum is
wire-encoded as Int32), so ClientBase.ValidateDataValue logged a
BadTypeMismatch warning on every keep-alive throughout the test run.

Real servers always emit Int32 for enum variables (BinaryEncoder
downgrades the enum encoding byte to Int32), so the warning was purely a
test-mock artifact. Switch the mock to new Variant((int)ServerState.Running)
to eliminate the spurious warnings.

* Source generator: emit and consume ModelDependencyAttribute. Emit one [assembly: ModelDependencyAttribute(...)] per model the
  generated assembly emits and per upstream model it transitively
  consumes (new ModelDependencyGenerator in
  Tools/Opc.Ua.SourceGeneration.Core).
* Replace the unused PEReader-based ReferenceMetadataScanner with a
  Roslyn IAssemblySymbol.GetAttributes() scan
  (ReferencedModelDependencyScanner in
  Tools/Opc.Ua.SourceGeneration). Hooks the per-symbol incremental
  cache, works for both PortableExecutableReference and
  CompilationReference, AOT-safe.
* Wire the scanner result through ModelSourceGenerator into
  ModelCompilation. The reduced URI-keyed map is passed to the public
  Generators.GenerateCode extension methods.
* NodesetFileCollection.GetDesignFileListForModel now consults the
  referenced-model map, so transitive nodeset dependencies that are
  satisfied by a referenced assembly no longer raise the
  missing-dependency error.
* Override resolution: when a local AdditionalFile and a referenced
  assembly both declare the same model URI under the same C# prefix
  the local generation is silently skipped (avoids CS0101); different
  prefixes still generate so downstream consumers can intentionally
  override types under a different namespace.
* New diagnostics MODELGEN010 (override skipped) and MODELGEN012
  (multiple referenced assemblies provide same model URI -> tie-break
  on highest version+publication date).
* Bump asserted GeneratedSources counts in ModelGeneratorTests for the
  new *.ModelDependencies.g.cs files (one per generated model).
* Fix unrelated CS1574 broken cref in NodeId.TryParseLongForm xmldoc
  that was blocking the Opc.Ua.Types build.
* Document the new flow in Docs/ModelDependencies.md.
* tests for ModelDependencyAttribute flow

* Refactor recently added "parsing" constructors to ParseLongForm static method. Method delegates to Parse(IServiceMessageContext)

NodeId, ExpandedNodeId and QualifiedName ParseLongForm overloads are now thin wrappers around Parse(context, options) using NodeIdParsingOptions with RequireResolvedUris=true and updateTables=false. Adds the FallbackToStringIdentifier opt-in to preserve the legacy lenient quirk for callers that need it (off by default for strict parity).

Tests for the refactored long-form parsers were added to the existing per-type test fixtures.

* Update nuget dependencies to latest
2026-04-22 10:15:12 +02:00
Marc Schier 09e44453a4 Generate type safe (method) proxies for ObjectTypes inside models (#3685)
Add interop test agent

The new stack-pass ObjectMethodProxyGenerator emits proxies that derive
from Opc.Ua.ObjectTypeClient and reference Session.MessageContext.NamespaceUris.
Neither type exists in the in-memory test compilation, which only references
Opc.Ua.Types.dll. Moving ObjectTypeClient to Opc.Ua.Types would cascade ISessionClient,
IClientBase, ITransportChannel and the generated service interfaces with it, so the
type stays in Opc.Ua.Core and we add a minimal stub to OpcUaCoreStubs instead.

Wire Microsoft.Extensions.Options 10.0.5 + DependencyInjection.Abstractions 10.0.5 into Directory.Packages.props and the Gds.Client.Common project to enable upcoming options-driven configuration work.

 Modernize Opc.Ua.Gds.Client.Common (DI, options, interfaces, async dispose)
2026-04-22 05:59:06 +02:00
Copilot 319ea88726 [Server/Client] Support RSAEncryptedSecret format and unify EncryptedSecret RSA/ECC API with legacy .NET and test compatibility (#3678)
* Initial plan

* feat(server): support RSAEncryptedSecret username token decryption

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/f79e2eec-5b14-4ffb-8ee0-2906a95e56fd

Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com>

* test: add RSAEncryptedSecret username token coverage and harden parsing

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/f79e2eec-5b14-4ffb-8ee0-2906a95e56fd

Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com>

* feat(client): use RSAEncryptedSecret for long username passwords

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/4b5a3cbb-9110-4fe5-907d-7b8ffccf2a16

Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com>

* fix(core): add framework-compatible zeroing and SHA1 hash helpers

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/732365b9-45fe-4c20-bfa9-b6b7dc9ad4e4

Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com>

* fix(core): tighten crypto helper compatibility guards

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/732365b9-45fe-4c20-bfa9-b6b7dc9ad4e4

Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com>

* chore(core): simplify zero-memory preprocessor guard

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/732365b9-45fe-4c20-bfa9-b6b7dc9ad4e4

Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com>

* test(core): add legacy-framework crypto polyfills in username token tests

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/87851fe9-59ab-453b-a0a7-7cb3134e4e0d

Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com>

* chore(test): rename RNG local for readability

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/87851fe9-59ab-453b-a0a7-7cb3134e4e0d

Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com>

* refactor(core): move RSAEncryptedSecret handling into EncryptedSecret

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/1a47ef6e-03ba-413c-8aee-1c03f870d3fe

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* chore(core): polish EncryptedSecret RSA refactor review items

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/1a47ef6e-03ba-413c-8aee-1c03f870d3fe

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* refactor(core): add RSA EncryptedSecret factory and clarify timestamp guard

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/1a47ef6e-03ba-413c-8aee-1c03f870d3fe

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* refactor(core): unify EncryptedSecret API for RSA and ECC

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/09578bca-fa2d-4916-8bd3-8d68470e91e1

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* refactor(core): add unified TryDecrypt handling and ECC failure test

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/09578bca-fa2d-4916-8bd3-8d68470e91e1

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* chore(core): refine unified TryDecrypt exception handling

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/09578bca-fa2d-4916-8bd3-8d68470e91e1

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* docs(core): clarify unified EncryptedSecret API behavior

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/09578bca-fa2d-4916-8bd3-8d68470e91e1

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* chore(core): improve ECC decrypt diagnostics and docs

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/09578bca-fa2d-4916-8bd3-8d68470e91e1

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* chore(core): polish unified API wording and ECC test naming

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/09578bca-fa2d-4916-8bd3-8d68470e91e1

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* chore(core): remove TryDecrypt magic number and simplify ECC error text

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/09578bca-fa2d-4916-8bd3-8d68470e91e1

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* chore(core): consolidate TryDecrypt catches with exception filter

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/09578bca-fa2d-4916-8bd3-8d68470e91e1

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com>
Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>
2026-04-21 05:03:00 +02:00
Randy Armstrong 36c936a361 Update SourceGenerator dependencies resolution mechanism. (#3680) 2026-04-20 17:31:18 +02:00
Copilot cacf03fffc XmlDecoder/XmlParser: throw BadDecodingError for empty/non-element enumerated arrays (#3677)
* Fix XmlDecoder empty enumerated array handling
* Fix XmlParser empty enumerated array handling
2026-04-20 15:07:45 +02:00
Marc Schier 34ed5716c0 Add structure-backed OptionSet DataType support in ComplexTypeSystem (#3672)
Concrete sub-types of the abstract OptionSet DataType (i=12755, e.g. AccessRights, CarExtras, OptionSetBase) were silently skipped by ComplexTypeSystem.LoadBaseStructureDataTypesAsync when only an EnumDefinition or OptionSetValues property was available. This change registers them with a new runtime class Opc.Ua.Encoders.OptionSet that reuses the generated Opc.Ua.OptionSet wire format (Value/ValidBits ByteStrings) and self-registers via IEncodeableType.

Key changes:

- New Stack/Opc.Ua/Types/OptionSet.cs with EnumDefinition-driven bit accessors (by name and index), GetSetFieldNames helper, and a fixed ByteLength derived from the highest declared bit per OPC UA Part 3 paragraph 8.40 / 3.2.8. SetBit throws ArgumentOutOfRangeException outside that range and always materializes Value/ValidBits to the fixed length.

- New AddOptionSetType entry point on IComplexTypeBuilder (source-breaking addition for custom implementations); DefaultComplexTypeBuilder implements it. The Reflection.Emit ComplexTypeBuilder throws NotSupportedException.

- ComplexTypeSystem detects OptionSet descendants via a HasSubtype walk and resolves the EnumDefinition from DataTypeDefinition or the OptionSetValues property fallback before the old skip path.

- Removed stale V1.04 OptionSet limitation remark from ComplexTypeSystem.

- 10 unit tests in Tests/Opc.Ua.Client.Tests/ComplexTypes/DefaultOptionSetTests.cs covering factory registration, bit accessors, binary round-trip via ExtensionObject, hand-crafted Part 6 wire format, Clone/CreateInstance semantics, and the ByteLength invariant.

- Live-verified against opc.tcp://DESKTOP-BGFJS91:48010 (UA C++ SDK demo): AccessRights/CarExtras/OptionSetBase now appear in the registered custom-type list; no skip-log entries; LoadAsync(throwOnError:true) returns true.

- Docs/ComplexTypes.md Known Limitations reworded; Docs/MigrationGuide.md gains an OptionSet DataType support subsection.

UInteger-backed OptionSet DataTypes remain intentionally opaque (carried as their unsigned integer in a Variant).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-17 21:16:55 +02:00
Marc Schier 6c8c1a6d29 Enable dispose analyzers CA2000 and CA2213 and fix all issues (#3670) 2026-04-17 13:39:34 +02:00
Marc Schier d1883832e1 Secure channel enhancements 2025 11 (Take 3) (#3665)
* merged security changes

* Tailor SecurityPolicyUri for format expected in s_securityPolicyUriToInfo

* Enhance security policy handling and key computation logic

* Merge SecurityEnhancements.

* Add support for SessionTransferToken. Removed obsolete SoftwareCertificate code.

* Fix NonceLength for None.

* Add support for RSA_DH, more fixes from IOP testings.

* Finish implementation of SecureChannelEnhancements.

* Rename EccUtils.cs to CryptoUtils.cs

* Address feedback from reviewers.

* Fix CoPilot flagged spelling errors.

* Rename EccUtils to CryptoUtils

* Update version from 1.5.378-preview to 1.5.378

* Fix unit tests.

* Allow SignatureData.Algorithm to be NULL or Empty.

* Fix issue with BrainPool_p256r1_ChaChaPoly

* Fix RSA_DH_AesGcm

* Fix OSC/padding by deriving HMAC keys, tightening symmetric size math, and correcting ECC_brainpoolP384r1/Basic128Rsa15 IV, signature, padding, and nonce parameters.

* Policies without asymmetric encryption (ECC) return the plaintext when encrypting user tokens; this prevents null payloads from causing BadIdentityTokenInvalid.

* Reserve outer CBC padding for avoiding SymetricEncryptAndSign->AddPadding to overwrite next fields (signature)

* Nonce stored as byte array in SessionConfiguration; Sesion snapshot restores ServerNonce and reconstructs ECC ephemeral key Nonce from bytes and policy

* Added _AesGcm and _ChaChaPoly variants to BuildSupportedSecurityPolicies() for all six ECC certificate types (nistP256, nistP384, brainpoolP256r1, brainpoolP384r1, curve25519, curve448) to SecurityConfiguration. Added Sign and SignAndEncrypt endpoints for the four ECC AesGcm/ChaChaPoly policy pairs (nistP256, nistP384, brainpoolP256r1, brainpoolP384r1) to ServerFixture.

* make ephemeralKeyPolicyUri nullable

* GenerateSecret also for NET7 and NET8

* Adjust Basic128Rsa15 policy properties for backword compatibility

* Exclude unsuported AEAD policies from .NET Framework client tests

* Addapt to new changes (still build fail)

* Addapt code to make it compile

* Fixed failing build on net48 (by ignoring potential null ref which is not reported on other newer targets)

* Preserve the certificate reference in Clone() so copied handlers can still sign

* Remove extra code and addapt to existing master configuration settings

* Filter *_AesGcm and *_ChaChaPoly security policies based on actual runtime support, so unsupported AEAD policies are not advertised or selected on older frameworks

* Expand ECC/RSA policy test coverage and keep Basic128Rsa15 nonce length backward-compatible

* Fix ReconnectSessionOnAlternateChannel _AES and _ChaCha policies

* Fixed IgnoreIfPolicyNotAdvertised so it now fetches endpoints on-demand (instead of relying on Endpoints being preloaded by earlier tests), which avoids false ignores when running tests directlyy

* Merge with commit 5e627f2ca1 from secure-channel-enhancements-2025-11 branch

* minor log mesatge formating

* Fix ClientLockoutTests

* Add ServerFixture policies upfront only if framework and runtime capability supports them

* A few cosmetic/config changes as review sugested

* Moved the session/security handling from StandardServer into a dedicated helper

* use X509IdentityTokenHandler from secure-channel-enhancements-2025-11

* Removed unused Opc.Ua.Types.UnitTests.csproj

* test(client): fix Basic128Rsa15 reconnect token-policy coverage

Advertise an explicit Basic128Rsa15 user token policy in the test fixture and tighten the reconnect test preconditions for non-advertised policies.

* test(client): fix session nonce persistence test helpers

Replace the generic reflection helpers in SessionTests with explicit client/server nonce accessors that match the actual field types.

Add a short Session comment to explain why client and server nonces currently use different representations.

* Merge branch 'master' into secure-channel-enhancements-2025-11-merge5

Merge with latest master and fix conflicts.

* Removed unused Opc.Ua.Types.UnitTests.csproj

* test(client): fix Basic128Rsa15 reconnect token-policy coverage

Advertise an explicit Basic128Rsa15 user token policy in the test fixture and tighten the reconnect test preconditions for non-advertised policies.

* test(client): fix session nonce persistence test helpers

Replace the generic reflection helpers in SessionTests with explicit client/server nonce accessors that match the actual field types.

Add a short Session comment to explain why client and server nonces currently use different representations.

* Build fixes

* Fix build

* update

* Minor updates

* Fix tests

* Add code to check all Endpoints.

* Update Design files to 1.05.7

* Run code fixers, add missing docs

* Fix build

* Revert removal of using

* Increase build timeout

---------

Co-authored-by: mrsuciu <Mircea-Adrian.Suciu@Softing.com>
Co-authored-by: Randy Armstrong <randy@sparhawksoftware.com>
2026-04-16 18:56:42 +00:00
KarenKrill fc21f2df8c Fixed timer leaks in ChannelAsyncOperation.EndAsync (#3669)
* Fixed timer leaks in ChannelAsyncOperation.EndAsync (added delay task cancellation)

* Added WaitAsync with timeout PolyFill extension for versions <= .NET 6.0

* The ChannelAsyncOperation.EndAsync has been unified across all .NET versions using polyfills for older versions (< .NET 6.0)

* Fixed missing "using" operator
2026-04-16 17:58:24 +02:00
Marc Schier abc813f56c Support UANodeset export / import under AOT (#3667) 2026-04-15 13:38:52 +02:00
Marc Schier 64ca85fce3 Increase code coverage across solution (#3662)
Add additional tests to increase code coverage across the solution
2026-04-14 13:32:10 +02:00
Marc Schier fd41681f4b Add support to publish OPC UA applications as Native AOT (#3633)
* make most ptojects aot compatible
* Remove newtonsoft
* Migrate all projects from Mono.Options to System.CommandLine 2.0.5
* Fix array clone
* Replace serializers for storage using new attribute driven  type geberator
* Add new AOT integration tests covering all functional areas
* Documentation updates
2026-04-13 20:52:29 +02:00
Copilot 4a0df19737 Improve runtime of Client Tests via fixture-level parallelization and fix parallel test isolation (#3655)
* Initial plan

* Add parallelization to client tests: extract transfer/reconnect tests, add Parallelizable attributes

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/7c095546-89fb-4661-b7df-873e74fb5b72

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Fix iteration counter message in TransferSubscriptionDebugAsync

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/7c095546-89fb-4661-b7df-873e74fb5b72

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Fix test isolation: thread-safe port allocation and retryStartServer bug

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/8f93f916-5ad8-4d07-925b-5394f1526eea

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Add max retry limit to GetNextFreeIPPort and clarify port<=0 condition comment

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/8f93f916-5ad8-4d07-925b-5394f1526eea

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Fix parallel test isolation: remove port registry, broader ConnectAsync retry

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/2db03a79-d996-4dd1-a570-8105197f6713

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Fix parallel HTTPS test isolation: replace static Startup.Listener singleton with per-port ConcurrentDictionary

Root cause: HttpsTransportListener.Startup.Listener was a static singleton that was overwritten
each time a new HTTPS server started. With parallel fixture execution (ClientTest, ClientBatchTest,
NodeCacheAsyncTest etc. all running their https/opc.https instances simultaneously), the last
server to start captured the global Listener. Requests arriving at any earlier server's Kestrel
instance were then dispatched to the wrong listener, causing BadServerHalted / NullReferenceException
cascade failures.

Fix: Replace Startup.Listener (static singleton) with Startup.Listeners (ConcurrentDictionary<int,
HttpsTransportListener> keyed by port). Start() registers the listener by port; Dispose() removes it.
The Kestrel middleware now routes each request to the correct listener using context.Connection.LocalPort.

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/a2a66689-1c27-4834-ab0b-32a2a0974fd6

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Add defensive try-catch in Dispose for Listeners.TryRemove per code review

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/a2a66689-1c27-4834-ab0b-32a2a0974fd6

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Remove static Startup.Listeners; inject HttpsTransportListener via IWebHostBuilder DI

Register the listener instance in the web host DI container via
IWebHostBuilder.ConfigureServices(services => services.AddSingleton(this)).
Startup.Configure receives it as a method-injected parameter, eliminating all
static state from the Startup class.

Constructor injection was considered but doesn't work on .NET 8+ where HostBuilder
activates Startup from the host-level service provider, which does not include
services registered via IWebHostBuilder.ConfigureServices. Method injection in
Configure() always resolves from the web-host app container, working correctly on
all target frameworks.

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/108c8840-5555-4c12-83fd-91ed0990d531

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>
2026-04-08 13:18:52 +02:00
romanett 7824e6fdeb Enhance role-based access control and add permission tests (#3653)
* Enhance role-based access control and add permission tests

- Improve ValidateRolePermissions logic and add ILogger support for detailed access denial logging
- Expand default roles (add AuthenticatedUser, TrustedApplication)
- Refine RoleBasedIdentity to merge roles and avoid duplicates
- Set UserIdentity default GrantedRoleIds to Anonymous
- Fix MasterNodeManager PrepareValidationCache
- Add comprehensive unit tests for role permission validation and logging

* Fix GDS self-admin role: restore NodeId.Null in GrantedRoleIds for ApplicationSelfAdmin

The RoleBasedIdentity constructor was filtering out roles with null RoleIds
(!role.RoleId.IsNull), which broke the GDS ApplicationSelfAdmin mechanism.
The GDS OnAddSelfAdminUserRolePermissions relies on GrantedRoleIds.Contains(NodeId.Null)
to detect self-admin users (since GdsRole.ApplicationSelfAdmin has RoleId = ExpandedNodeId.Null).

Also remove unused System.Data using statement.

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/3db86efa-c6d6-4e63-947f-156a88210b58

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* fix build

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>
2026-04-08 13:13:43 +02:00
Copilot edbbfa3532 Add tests for ServerBase with multiple IP addresses on the same port & improve CI Stability (#3650)
* Add tests for server with multiple IP addresses on same port

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/adce15e6-8c71-4ba9-8581-cb526b274dfe

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Fix flaky RequestQueueTests by using separate CancellationTokenSources per wait

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/85e9cbc6-7d42-4e6b-8cbe-725b732c72a2

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Fix flaky VerifyRejectedCertsDoNotOverflowStoreAsync test

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/a75f2420-077e-4022-8f73-17b52d6de841

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Fix race in RequestQueueTests: await req3 completion before asserting status code

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/43e310a1-bcf1-45fd-84f1-f5266b1e855d

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Fix flaky TransferSubscriptionOnlyAsync(DisconnectedRepublishDelayedAck): poll for republished notifications and use GreaterThanOrEqualTo assertion

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/266c66da-e8dc-450f-925a-e3c1c50100b3

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Fix ShortHaulStabilityTestAsync: wait for initial subscription notifications before starting writes, reset counters for clean measurement window

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/44e73c6c-3620-4e88-9f4c-852a9fe58d94

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Fix TransferSubscriptionOnlyAsync(DisconnectedRepublishDelayedAck,False,False): extend republish polling to all sendInitialValues combinations

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/e5775ceb-1441-4bef-a9b7-7e80d5a9fb72

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Fix TransferSubscriptionOnlyAsync(DisconnectedRepublishDelayedAck,False,*): restrict republish poll and expected count to sendInitialValues=True

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/dbee6fa2-6ea3-4086-b36e-263288cfdf31

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

* Fix RequestQueue race: increment m_totalThreadCount before Task.Run to prevent spurious worker spawning

Agent-Logs-Url: https://github.com/OPCFoundation/UA-.NETStandard/sessions/380346b7-7b31-4390-9438-3725028a0831

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>
2026-04-07 12:45:02 +02:00
romanett 23f10d89ee [Server] Introduce RequestLifetime for request handling and cancellation (#3643)
Use RequestLifetime throughout the server stack for unified request lifetime and cancellation management. Update all service method signatures, OperationContext, and request processing logic to use RequestLifetime, enabling precise tracking of cancellation reasons with UA StatusCodes. Remove obsolete synchronous request handling and legacy delegates. Update code generation, tests, and interfaces to support the new model. This change improves cancellation, timeout, and abort handling, and prepares the server for advanced request management features.
2026-03-30 13:17:19 +02:00
Johann Gail 56cb0daf49 Use the cancelation token, where available (#3636) 2026-03-27 14:59:49 +01:00
romanett 66f44396ff [Server] Improve Request Queue, fix Event handling, add Tests (#3611)
- implement RequestQueue using Channels
- add event load test
- add event integration test verifying event from server object is received
- fix Client Reconnect Test
- fix DiagnosticsNodeManager not creating RootNotifier for Server object
- add Benchmarks for MonitoredItem.QueueEvent
- add serveral UnitTest for DiagnosticsNodeManager
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>
Co-authored-by: Marc Schier <marcschier@users.noreply.github.com>
2026-03-20 08:09:51 +01:00
Marc Schier 5a1e547250 Code cleanup and modernization of test assertions to comply with project standards. (#3631) 2026-03-19 21:09:40 +01:00
Marc Schier 03777ecb09 New Json decoder/encoders, array/matrix abstractions, ByteString, and more. (#3619)
- **Collections overhaul** — new/rewritten collection types (`ArrayOf`, `Collections` commits)
- **`Variant` builder/accessor** — new APIs for constructing and accessing Variants
- **`NodeId` operators made explicit** — breaking change for implicit conversions
- **Version bumped to 1.6** with preview build support
- **NumericRange** uses Variant
- **object** type accessors and general usage removed.

### 🏗️ Infrastructure
- Build config updated; `.NET 10` targeting
- InternalsVisibleTo moved to `.csproj`
- Pipeline/CI adjustments
2026-03-18 12:23:30 +01:00