Commit Graph

23 Commits

Author SHA1 Message Date
romanett 537b10c8b3 Fix History User Access Levels and add CTT coverage nodes (#4026)
# Description

Improve Reference Server coverage for OPC UA Compliance Test Tool
scenarios and document the latest CTT findings.

## Reference Server coverage

- Historize the configured AccessRights nodes and seed them with the
same deterministic mixed-quality history as the other test nodes while
preserving their access controls.
- Add a historian node that intentionally does not support
ServerTimestamp.
- Add a SelectionListType String instance whose Selections property is
String[] as required by Part 5, with localized descriptions and
RestrictToList enforcement.
- Add an EUR CurrencyUnit property and verify its structured value and
binary encoding.
- Add a node with explicit read/write base access bits plus
NonatomicRead and NonatomicWrite AccessLevelEx flags.
- Ensure the first ByteString array elements meet the CTT minimum-length
prerequisite.

## Latest CTT findings

- Confirm run-23 A&C residuals are CTT re-entrant Read and
branch-unaware Retain checks rather than server regressions.
- Confirm NumberOfTransitions includes Uncertain values as required by
Part 13.
- Document the CTT CurrencyUnit decoder dropping a correctly encoded
Exponent.

## Related Issues

- Follow-up to #3960 and #4002.

## Checklist

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

Focused affected tests pass on .NET 10 and .NET Framework 4.8.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Marc Schier <marcschier@hotmail.com>
2026-07-21 13:52:12 +02:00
Marc Schier 5206cc4e13 Self-fence Kubernetes Lease leadership using monotonic time (#4042)
## Failure

Lease takeover trusted another replica's wall-clock `RenewTime`, so
clock skew could steal a live Lease. A hung renewal could also leave
local leadership asserted, and a stale completion or notification could
resurrect leadership after fencing or disposal.

## Fix

- Measure unchanged foreign `resourceVersion` age with local monotonic
time.
- Serialize and bound API attempts, and self-fence with a lease-duration
watchdog.
- Reject stale completions and suppress out-of-order leadership
notifications.

## Standard

This is a Kubernetes coordination extension to OPC 10000-4 §6.6
redundancy. Kubernetes Lease ownership is now evaluated from stable
resource observations rather than untrusted peer wall clocks.

## Tests

- `dotnet test
tests\Opc.Ua.Redundancy.Kubernetes.Tests\Opc.Ua.Redundancy.Kubernetes.Tests.csproj
-c Release --nologo -m:1` — 97 passed on net8.0 and 97 passed on net10.0

- Review follow-up: `KubernetesLeaseLeaderElectionTests` — 22 passed on
net8.0.

## Split

Split from #4021.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-21 11:28:44 +02:00
Marc Schier 3c4661d2bf Protect certificate and identity secrets in MCP and durable samples (OPC 10000-4 security) (#4043)
## Failure

MCP auto-accept mutated the shared application certificate manager and
accepted
every certificate error, so one permissive connection could affect
concurrent or
later strict sessions. The durable-subscription sample also serialized
complete
user identity tokens, including user-name passwords and issued bearer
tokens.

## Fix

- Isolate auto-accept certificate managers/configurations per connection
and keep
  their lifetime with the managed session.
- Auto-accept only `BadCertificateUntrusted`; retain strict validation
for all
  other certificate errors and shared sessions.
- Version the durable store and reject the previous unsafe format.
- Remove user-name passwords before persistence and reject issued-token
  subscriptions rather than storing bearer credentials.
- Add focused tools tests plus solution, project, and
durable-subscription docs.

## Reference

- OPC 10000-4 Session identity-token and application-certificate
security.
- Split from #4021.

## Tests

- `Opc.Ua.Tools.Tests` Release: 7 passed on net10.0; 4 passed on net48.
- `Quickstarts.Servers` Release builds passed on net10.0 and net48 with
zero warnings/errors.
- `Opc.Ua.Mcp` Release net10.0 build passed with zero warnings/errors.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-21 11:26:25 +02:00
Marc Schier 1d2a7c8d00 Complete partial vectored UA-TCP sends (OPC 10000-6) (#4028)
# Description

Stream socket semantics permit a successful vectored send to complete
after fewer bytes than requested. The previous one-shot send treated
that legal partial completion as a closed connection and left the
remaining bytes unsent, risking truncation of an OPC 10000-6 UA-TCP
message chunk.

The transport now continues sending while advancing across copied buffer
segments, without mutating the caller's `BufferCollection`, and rejects
zero-byte or impossible over-reported sends.

## Validation

- `TcpByteTransportTests`: net10.0 (10 passed), net48 (10 passed).
- `git diff --check` passed.

## Related Issues

- Split from #4021.

## Checklist

_Only verified claims are checked._

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

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-21 07:21:35 +02:00
MrAlaskan 973cc81483 fix(di): accept FunctionalGroupType subtypes in BrowseFunctionalGroupsAsync (#4032)
### Summary

This PR makes `DiDeviceClient.BrowseFunctionalGroupsAsync()` accept
`FunctionalGroupType` subtypes instead of requiring an exact
type-definition match and adds a regression test that verifies derived
functional groups are included in the returned results.

### Problem

In OPC UA DI, functional groups may be modeled as instances of
`FunctionalGroupType` or of any subtype derived from it. A client helper
that enumerates functional groups therefore needs to follow the OPC UA
type hierarchy instead of requiring the object's `TypeDefinition` to be
exactly `FunctionalGroupType`.

Previously, `BrowseFunctionalGroupsAsync()` filtered browse results
through a helper named `IsSubtypeOf()`, but that helper only compared
`typeDefinition == expectedType`. As a result, any device that exposed
functional groups through a legal subtype of `FunctionalGroupType` would
have those groups silently filtered out even though they were valid DI
functional-group objects.

### Changes

- Replace the exact type-definition comparison in
`BrowseFunctionalGroupsAsync()` with a real subtype check based on
`Session.NodeCache.IsTypeOfAsync(...)`.
- Preserve the existing filtering behaviour for non-functional-group
objects.
- Add a regression test that verifies a derived functional-group type is
returned while an unrelated object is still excluded.
2026-07-20 08:27:13 +02:00
MrAlaskan 8602d40cff fix(alias): compare PortableNodeId identifiers by NodeId semantics (#4037)
### Summary

This PR fixes `AliasNamePubSubRefreshStrategy` category matching for
string NodeIds whose identifier contains semicolons and adds a
regression test covering that scenario.

### Problem

`AliasNamePubSubRefreshStrategy` is supposed to match the publisher's
`PortableNodeId.Identifier` against the local alias category `NodeId`
while ignoring namespace index differences. However, the previous
implementation did that by converting both NodeIds to text and stripping
everything before the first `;`.

That happened to work for simple identifiers such as
`ns=1;s=MyCategory`, but it broke for valid string NodeIds whose
identifier itself contains semicolons. For example, when the local
category is `ns=1;s=A;B`, the previous logic transformed the local value
into `s=A;B` but transformed the already namespace-free portable form
`s=A;B` into just `B`. As a result, both sides represented the same
identifier semantically, yet the refresh strategy could never match them
and would silently ignore legitimate alias update messages for that
category.

### Changes

- Replace the string-prefix stripping comparison in
`AliasNamePubSubRefreshStrategy` with namespace-insensitive `NodeId`
equality, preserving the original identifier type and value.
- Add a regression test that verifies a PubSub alias update for a
category such as `s=A;B` still invalidates the local cache.
2026-07-20 08:26:32 +02:00
MrAlaskan 8f4087605a Fix unordered server endpoint validation (#4029)
### Summary

This PR makes `Session.ValidateServerEndpoints()` compare
`ServerEndpoints` and `UserIdentityTokens` as unordered sets and adds
regression tests that verify `OpenAsync()` no longer rejects legal
servers that return the same endpoint data in a different order.

### Problem

According to OPC UA Part 4, the client validates the endpoint set
returned by `CreateSessionResponse.ServerEndpoints` against the endpoint
set observed during discovery. The specification describes these values
as a set of endpoint descriptions filtered by the relevant transport
profile, not as an ordered array that must preserve the exact
discovery-time enumeration order.

Previously, `Session.ValidateServerEndpoints()` first checked the
endpoint count and then compared `m_discoveryServerEndpoints[ii]`
against `serverEndpoints[ii]` by index. It also compared
`UserIdentityTokens[jj]` by index within each endpoint. As a result, a
server that returned the same legal endpoints in a different order, or
returned the same token policies in a different order within an
endpoint, could be rejected with `BadSecurityChecksFailed` even though
the endpoint data itself had not changed.

### Changes

- Replace index-based `ServerEndpoints` validation with unordered
matching based on the same endpoint fields already validated by the
client.
- Compare `UserIdentityTokens` as an unordered multiset instead of
requiring the server to preserve the original token enumeration order.
- Extend the client session test scaffolding so discovery endpoints can
be supplied explicitly.
- Add regression tests that verify `OpenAsync()` accepts reordered
`ServerEndpoints` and reordered `UserIdentityTokens`.
2026-07-20 08:17:34 +02:00
MrAlaskan 399747f6a0 fix: don't return continuation points on BadNoContinuationPoints (#4022)
### Summary

This PR fixes `Browse` and `BrowseNext` so they do not return a usable
continuation point when the server reports `BadNoContinuationPoints`,
and adds regression tests that cover both code paths.

### Problem

According to OPC UA Part 4, if the server cannot allocate another
continuation point for a `Browse` or `BrowseNext` operation, the result
should be `Bad_NoContinuationPoints`. In that case, the server must not
also return a continuation point that the client can continue to use.

Previously, `MasterNodeManager.FetchReferencesAsync()` could return
`BadNoContinuationPoints` while still passing a live continuation point
object back to its callers. `BrowseAsync()` then copied that
continuation point into the result, producing a contradictory response
with `StatusCode = BadNoContinuationPoints` and a non-empty
`ContinuationPoint`. `BrowseNextAsync()` went further and overwrote the
error with `Good` whenever a continuation point object was still
present. That behavior hides continuation-point quota exhaustion and
breaks the expected paging semantics.

### Changes

- Dispose and clear the continuation point when `FetchReferencesAsync()`
hits the no-continuation-points path.
- Only copy a continuation point into `Browse` and `BrowseNext` results
when the operation completed successfully.
- Add deterministic regression tests that verify both `Browse` and
`BrowseNext` return `BadNoContinuationPoints` without a continuation
point when the continuation-point quota is exhausted.
2026-07-20 08:02:45 +02:00
MrAlaskan f6ae61c6bc fix(di): accept DeviceType subtypes during device discovery (#4033)
### Summary

This PR makes `DiDiscoveryClient.EnumerateDevicesAsync()` accept
`DeviceType` subtypes instead of requiring an exact type-definition
match and adds regression tests that verify derived device types are
discovered while non-device objects remain filtered out.

### Problem

In OPC UA DI, device instances may be modeled directly as `DeviceType`
or as instances of any subtype derived from `DeviceType`. A discovery
helper that claims to enumerate `DeviceType` devices therefore needs to
follow the OPC UA type hierarchy instead of requiring an exact
`TypeDefinition` match.

Previously, `DiDiscoveryClient.BrowseForDevicesAsync()` identified
devices with a direct comparison against `DeviceType`. As a result, any
server that exposed devices through a legal subtype of `DeviceType`
would have those devices treated as non-device objects, causing them to
be skipped or only used as recursion points instead of being returned as
discovered devices.

### Changes

- Replace the exact `DeviceType` comparison in `BrowseForDevicesAsync()`
with a real subtype check based on
`session.NodeCache.IsTypeOfAsync(...)`.
- Preserve the existing recursive browse behaviour for non-device
objects.
- Add regression coverage that verifies exact `DeviceType` instances
still work, derived device types are now discovered, and non-device
objects are still excluded.
2026-07-20 07:47:48 +02:00
MrAlaskan 565fc02535 fix(subscription): fail transfer synchronization when deleting stale monitored items (#4031)
### Summary

This PR makes `MonitoredItemManager.TrySynchronizeHandlesAsync()` treat
`DeleteMonitoredItems` failures as transfer-synchronization failures and
adds a regression test that verifies the client no longer reports
success when the server rejects deletion of stale monitored items.

### Problem

OPC UA Part 4 requires clients to evaluate service results at two
levels: `ResponseHeader.ServiceResult` reports the outcome of the
service call as a whole. `Results[]` reports the outcome of each
individual operation in that service. Even when the service-level result
is `Good`, the service may still have only partially succeeded. The
client therefore still needs to inspect the response payload, especially
the per-operation `StatusCode` values, before concluding that the
requested work actually completed.

In `TrySynchronizeHandlesAsync()`, the transfer/recovery path deletes
monitored items that still exist on the server but no longer exist
locally. Previously, this delete path treated
`DeleteMonitoredItemsAsync()` as successful as long as the call itself
did not throw, and it did not validate `ResponseHeader.ServiceResult`,
`Results[]`, or the associated diagnostic information. As a result, the
client could report transfer synchronization success even when the
server explicitly returned failures for one or more stale monitored
items.

### Changes

- Validate the `DeleteMonitoredItemsAsync()` response inside
`TrySynchronizeHandlesAsync()` instead of treating any non-throwing call
as success.
- Return `false` when the delete operation reports a bad service-level
result or any bad per-item result, so synchronization only succeeds
after stale server-side items are actually removed.
- Add a regression test that verifies `TryCompleteTransferAsync()`
returns `false` when deletion of an extraneous server-side monitored
item is rejected.
2026-07-20 06:17:21 +02:00
MrAlaskan b105eaae02 fix(subscription): do not acknowledge keep-alive future sequence numbers (#4030)
### Summary

This PR stops the V2 subscription message processor from acknowledging
keep-alive sequence numbers and adds a regression test that verifies
acknowledgements are only queued for notification messages that actually
contain data.

### Problem

According to OPC UA Part 4 publish semantics, a keep-alive
`NotificationMessage` does not acknowledge delivery of a real
notification. Its `SequenceNumber` identifies the next notification
message that will be sent, not a notification payload that the client
has already received.

Previously, `MessageProcessor.ProcessMessageAsync()` correctly treated
`NotificationData.Count == 0` as a keep-alive and intentionally avoided
advancing the data-message deduplication gate. However,
`OnNotificationReceivedAsync()` still unconditionally queued a
`SubscriptionAcknowledgement` for every message, including empty
keep-alives. As a result, the V2 client could acknowledge a future
sequence number before the corresponding data/event notification had
actually been received.

This could produce spurious acknowledgement noise against strict servers
and made the V2 path inconsistent with the existing classic subscription
engine, which only acknowledges messages that contain notification data.

### Changes

- Stop queueing acknowledgements for keep-alive messages in
`MessageProcessor.OnNotificationReceivedAsync()`.
- Preserve the existing keep-alive dispatch path and continue
acknowledging notification messages that carry real data.
- Extend the message processor tests to verify that keep-alives do not
enqueue acknowledgements and that the subsequent real notification still
does.
2026-07-20 02:47:24 +02:00
MrAlaskan 12f770b3b6 Fix managed session failover target selection (#4025)
### Summary

This PR makes `ManagedSession` select failover targets relative to the
currently active endpoint after a successful failover and adds a
regression test that verifies subsequent failover decisions no longer
use the original connection target as the current server.

### Problem

According to OPC UA Part 4 redundancy semantics, once a client has
failed over to a different redundant server, subsequent target selection
must be based on the server that is currently hosting the active
session. The current server is not permanently tied to the endpoint that
was used for the initial connection.

Previously, `ManagedSession` stored the constructor-supplied
`ConfiguredEndpoint` in a read-only property and always passed that
outer value into `IServerRedundancyHandler.SelectFailoverTarget()`.
However, the inner `Session` updates its own `ConfiguredEndpoint` during
`RecreateInPlaceAsync()` when failover succeeds. After the first
failover, the outer `ManagedSession` and inner `Session` could therefore
disagree about which redundant member was currently active, causing
later failover decisions to be evaluated against the original endpoint
instead of the active one.

### Changes

- Make `ManagedSession.HandleFailoverAsync()` prefer the inner session's
current `ConfiguredEndpoint` when selecting a failover target, while
keeping the outer endpoint as a fallback before the session is
available.
- Preserve the existing failover flow and scope the fix to target
selection only.
- Add a regression test that verifies `HandleFailoverAsync()` passes the
inner session's current endpoint to
`IServerRedundancyHandler.SelectFailoverTarget()`.
2026-07-19 07:43:19 +02:00
MrAlaskan 70c74e7cb8 fix: scope Cancel request matching to the current session (#4023)
### Summary

This PR scopes `Cancel` request matching to the caller's session and
adds regression tests that verify a request handle can no longer cancel
work that belongs to another session.

### Problem

According to OPC UA Part 4, `Cancel` applies to a request handle from
the same session that issued the original request. The server should not
use a client-supplied `requestHandle` to match outstanding requests
across all sessions.

Previously, `StandardServer.CancelAsync()` validated the caller's
session but then forwarded only the raw `requestHandle` to
`RequestManager.CancelRequests()`. `RequestManager` iterated the global
outstanding-request table and canceled every request whose
`ClientHandle` matched, without checking the owning session. Since
request handles are only session-scoped and different sessions can
legitimately reuse the same value, one session could cancel another
session's in-flight request.

### Changes

- Pass the validated caller session id from
`StandardServer.CancelAsync()` into `RequestManager.CancelRequests()`.
- Restrict `RequestManager.CancelRequests()` to requests whose
`SessionId` and `ClientHandle` both match the `Cancel` request.
- Update existing request-manager and publish-queue tests to pass the
session id and add a regression test that proves matching request
handles in different sessions no longer interfere with each other.
2026-07-19 06:23:52 +02:00
MrAlaskan 2d55126cb8 fix: return BadInvalidState for unlatched alarm reset (#4020)
### Summary

This PR fixes the reset status code returned for unlatched alarms and
adds a regression test that verifies `Reset` and `Reset2` both report
`BadInvalidState` when the alarm is not latched.

### Problem

According to the alarm reset semantics described in OPC UA Part 9,
`Reset` is only valid for a latched alarm that is no longer active and
has already been acknowledged and confirmed when those states are
present. For any other invalid state, the server should return
`Bad_InvalidState`.

Previously, `AlarmConditionState.HandleResetCore()` returned
`BadConditionNotShelved` when `LatchedState.Id == false`. That status
code belongs to the shelving state machine, not the latched alarm reset
flow. As a result, clients calling `AlarmClient.ResetAsync()` could
receive a shelving-related error for a reset precondition failure, which
misrepresents the actual alarm state and breaks the expected Part 9
semantics.

### Changes

- Change the unlatched branch in `AlarmConditionState.HandleResetCore()`
to return `BadInvalidState` instead of `BadConditionNotShelved`.
- Add a regression test in `AlarmConditionStateMethodTests` that covers
both `Reset` and `Reset2` when `LatchedState` exists but the alarm is
not latched.
2026-07-18 21:45:32 +02:00
Marc Schier 20d9d65a64 Fix generated configuration namespace resolution (#4019)
# Description

Fix source-generated configuration types whose explicit namespace
references constants emitted by the same generator pass. Roslyn cannot
resolve those constants while `[DataType]` attributes are analyzed, so
the generated XML codec silently fell back to `urn:memorybuffer` /
`urn:testdata` while the shipped configuration uses the model namespace
URIs.

This change:

- adds `MODELGEN021` to fail when an explicitly supplied
`[DataType.Namespace]` expression cannot be resolved;
- uses compile-time-available model namespace URIs for the MemoryBuffer
and TestData configuration types;
- restores production-shaped XML fixtures and exercises the
`UpdateExtension<T>()` / `ParseExtension<T>()` path;
- documents the source-generator namespace requirement and migration
guidance.

Targeted validation passed:

- `TypeGeneratorTests`: 17 tests on `net10.0` and `net48`;
- `ConfigurationRoundTripTests`: 4 tests on `net10.0` and `net48`.

## Related Issues

- Fixes #4014

## Checklist

- [ ] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [x] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- [x] I have added all necessary documentation.
- [x] I have verified that my changes do not introduce (new) build or
analyzer warnings.
- [ ] I ran **all** tests locally using the **UA.slnx** solution against
at least .net **framework** and .net **10**, and all passed.
- [ ] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [x] I have addressed **all** PR feedback received.
2026-07-18 20:15:49 +02:00
Marc Schier 84fa8e8906 CTT follow-up: monitored items, alarms, codecs, history, and model validation (#4002)
# Description

Follow-up to #3978 based on the latest OPC UA CTT aggregate, matrix,
Historical Access, Attribute, and Node Management runs.

## Aggregate run 16

- Ignore synthetic `BadBoundNotFound` raw-bound markers when feeding
aggregate calculators.
- Add direct and live historian regression coverage.

## Multi-dimensional Variant matrices

- Fix `Variant.Copy()` so rank-2 Variant matrices preserve their payload
and dimensions.
- Enforce valid Variant matrix dimensions consistently across Binary,
JSON, XML stream, and XML parser codecs.
- Encoders return `BadEncodingError`; decoders return `BadDecodingError`
for malformed matrix dimensions.
- Add cross-codec valid/null/empty/malformed tests and a batched
ReferenceServer Read regression.
- Correct the previous CTT issue attribution: the invalid
`Scalar_Static_Arrays2D_Variant` server encoding poisoned the batched
CTT Read.

## Node Management AddNodes

- Return `BadNodeIdRejected` for unsupported requested-NodeId
namespaces.
- Validate hierarchical ReferenceType source/target NodeClass
constraints.
- Document run-18 confirmation that all remaining 15 AddNodes errors are
CTT project/script configuration issues.

## Historical Access and Attribute Services run 18

- Reject whitespace in `NumericRange` syntax with
`BadIndexRangeInvalid`.
- Add HistoryRead tests for operation-level invalid syntax and
per-DataValue `BadIndexRangeNoData`.
- Document CTT defects:
- `012.js` expects `BadIndexRangeNoData` at the operation level instead
of on returned DataValues;
  - `Err-012.js` uses a non-historizing node for an access-denied test;
- generic Attribute array helpers omit NodeId[] and StatusCode[]
conversion paths.

## Base Information model validation run 19

- Run the CTT main session as `sysadmin` over Basic256Sha256
SignAndEncrypt so SecurityAdmin RolePermissions and EncryptionRequired
restrictions are both satisfied without weakening the server model.
- Preserve predefined well-known NodeIds while making generated dynamic
type and optional-child factories allocate compatible per-instance
NodeIds, remap internal references, and update references from the
owning subtree.
- Materialize dynamic RoleType properties through generated helpers and
keep role-child string NodeIds disjoint from numeric RoleManager root
IDs.
- Add net48/net10 regressions for generator output, NodeId
allocation/reference remapping, DI mandatory children, dynamic roles,
and low-privilege versus SecurityAdmin Browse visibility.
- Document CTT defects for permission-unaware mandatory-child
validation, the UA 1.04 reference model used against a UA 1.05 server,
stale ServerDiagnostics cache, and the `ConformanceUnits` scalar/array
mismatch.
## Related Issues

- Follow-up to #3960 and #3978.

## Checklist

- [x] I have signed the CLA and read the CONTRIBUTING document.
- [x] Tests prove the fixes and improve coverage.
- [x] Required CTT documentation is updated.
- [x] Focused builds/tests introduce no warnings.
- [x] Affected tests pass on .NET Framework and .NET 10.
- [ ] CI and CodeQL are green.
- [ ] All PR feedback is addressed.



## Latest CTT full run and A&C run 20

- Fix Monitor Value Change V2 `036.js`: an ambiguous
`DataValue(StatusCode)` call encoded `BadIndexRangeNoData` as a Good
value.
- Prove all 19 configured matrix monitored items notify when the
selected value actually changes; document the independent `042.js`
script defects.
- Rebase dynamic `CertificateExpired` / `TrustListOutOfDate` alarm roots
and descendants, restore standard type declarations and ModellingRules,
and expose the cross-node-manager references.
- Deliver method-triggered events to every authorized Session, fixing
the two-Session Confirm scenario.
- Preserve locale-only `LocalizedText` values consistently across
Binary, JSON, and XML and align Acknowledge/Confirm comment behavior and
tests.
- Document the remaining proven CTT defects for TransactionDiagnostics,
SemanticChange, History `013`, PolicyId scope, A&C, durable
subscriptions, and Subscription Minimum events.

## Base synchronization

Merged `master` through `007cfaf90` (`#4010`, repository layout
consolidation) and retained the matrix regression tests at their new
`tests/` locations.

## Latest validation

- Focused merged-tree tests pass on net10 and net48.
- `Opc.Ua.Server` and `Quickstarts.Servers` Release builds complete with
0 warnings and 0 errors on net10 and net48.
- The existing
`DisableEnableViaTypeAndInstanceMethodNoEventsDuringDisabledAsync`
timing test took its documented inconclusive timeout path after the
layout merge; no test failure was reported.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: romanett <romanett98@gmail.com>
2026-07-18 05:01:03 +02:00
Copilot b76022d13d Add MinimalClient - lightweight OPC UA console client with fluent API (#4006)
# Description

Adds `MinimalClient`, a compact console OPC UA application demonstrating
the unified dependency-injection surface, secure endpoint discovery,
managed sessions, fluent V2 subscriptions, and Alarms & Conditions
support.

## Changes

- Adds `ConfigureApplication(options => ...)` to build and validate one
shared `ApplicationConfiguration` for combined client/server hosts.
- Extends `OpcUaClientOptions` so the existing `AddClient(options =>
...)` callback accepts application identity, PKI, and
certificate-validation settings like `AddServer(...)`.
- Composes root `ConfigureApplication(...)` values with client defaults
in either registration order; shared values take precedence and client
values fill gaps.
- Keeps server samples on their established `AddServer(...)`
configuration pattern.
- Adds `MinimalClient` as a .NET 10 Native AOT console application using
top-level statements and `HostApplicationBuilder`.
- Discovers a `SignAndEncrypt` / `Basic256Sha256` endpoint by default;
`--insecure` and `--auto-accept` are explicit opt-ins with warnings.
- Demonstrates fluent `AddSubscription(...)`,
`TryAddMonitoredItem(...)`, and `AlarmClientFactory` usage.
- Adds targeted configuration, client, and combined client/server tests.

## Example

```csharp
builder.Services
    .AddOpcUa()
    .AddClient(options =>
    {
        options.ApplicationName = "MinimalClient";
        options.ApplicationUri = "urn:localhost:OPCFoundation:MinimalClient";
        options.ProductUri = "uri:opcfoundation.org:MinimalClient";
        options.Session = new ManagedSessionOptions
        {
            SessionName = "MinimalClient",
            SessionTimeout = TimeSpan.FromSeconds(60)
        };
    })
    .AddDiscoveryAndConnect(options =>
    {
        options.DiscoveryUrl = discoveryUrl;
        options.SecurityMode = MessageSecurityMode.SignAndEncrypt;
        options.SecurityPolicyUri = SecurityPolicies.Basic256Sha256;
    })
    .AddSubscriptions()
    .AddAlarms();
```

## Related Issues

_If there is no issue yet, open one and link it here._

## Checklist

- [ ] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [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.
- [ ] I fixed all failing and flaky tests in the CI pipelines and all
CodeQL warnings.
- [x] I have addressed all PR feedback received.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: romanett <romanett98@gmail.com>
Co-authored-by: Marc Schier <marcschier@hotmail.com>
2026-07-17 19:36:53 +02:00
Marc Schier 41a648c9db Fail fast on reverse connect listener startup (#4009)
# Description

Fail reverse-connect listener startup immediately instead of logging
bind failures and surfacing a later connection timeout.

- Validate and stage configured and programmatic listener endpoints
before replacing active configuration.
- Open all listeners atomically and report endpoint-specific failures
with `BadTcpEndpointUrlInvalid`, the transport-specific status, or
`BadNoCommunication`.
- Roll back listeners opened by a failed attempt without reacquiring the
manager lock, including forced listener disposal when normal close
fails.
- Preserve meaningful `ServiceResultException` status codes through both
`StartService` overloads.
- Dispose replaced configuration watchers and reject callbacks from
stale watcher generations.
- Keep the intended shared-listener behavior: one long-lived
`ReverseConnectManager` can accept connections from multiple Servers on
the same Client URL.
- Expand `Docs/ReverseConnect.md` with listener lifetime, shared-manager
usage, diagnostics, and specification references.

## Validation

- `ReverseConnectManagerTests`: net10.0 and net48.
- `ReverseConnectHostTests`: net10.0 and net48.
- Ordered reverse-connect listener reuse integration tests: net10.0.

## Related Issues

- Relates to #3985.

## Checklist

- [ ] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [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-07-17 13:20:24 +02:00
Marc Schier 12ebfba603 Add ISA95 JobControl and ManagedSession regression coverage (#3998)
## Summary

- Adds the official ISA95 JobControl 2.0.0 NodeSet2 as a complete
source-generator regression fixture.
- Extends the lightweight generated-stack test stub with
`NamespaceMetadataState`, required by the complete model.
- Adds a deterministic ManagedSession cancellation regression proving
that a started connect attempt is stopped.

The ManagedSession cleanup implementation and Quickstarts utility
coverage now come from `master`; this PR retains the additional
deterministic cleanup regression.

## ISA95 fixture provenance and integrity

The fixture is byte-for-byte the official file:

- Source:
`OPCFoundation/UA-Nodeset@e0b5d80cfff698f0276393e28df138a7fbc4b541`
- Path: `ISA95-JOBCONTROL/opc.ua.isa95-jobcontrol.nodeset2.xml`
- Git blob: `82f008cd126ade54e5f2c737bcc116621a8c3087`
- SHA-256:
`52327e736beb604c7253a5a5393d1ad525b9ecf35b71fbacb4f429cacb334064`

The fixture test retains the OPC Foundation MIT header and verifies the
2.0.0 model/date, 258 nodes (including 134 variables), representative
late variables, and that every `ns=1` reference target resolves locally.

## Validation

- ManagedSession tests: 17 passing on net10.0; 15 passing and 2
framework-gated skips on net48.
- Quickstarts server utility tests: 3 passing on net10.0 and net48.
- ISA95 generator and fixture integrity regression: 5 passing on
net10.0.
2026-07-17 13:19:00 +02:00
Marc Schier 437a0dadc7 Merge origin/master into marcschier/3907-repository-layout 2026-07-16 07:11:36 +02:00
Marc Schier 8538f531e7 Update paths for the consolidated repository layout 2026-07-16 06:26:05 +02:00
Marc Schier a21b690d08 Move the remaining duplicate binary assets 2026-07-16 06:26:05 +02:00
Marc Schier 7cec2d614d Move repository files to the consolidated layout 2026-07-16 06:25:54 +02:00