Commit Graph
21 Commits
Author SHA1 Message Date
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 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
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
Marc Schier 2960388560 Update headers and license files (#3388)
Update license headers to copy right year 2025, replace RCL/GPL headers with MIT, replace the nuget license with MIT (via license expression).
2025-12-04 19:44:40 +01:00
Marc Schier 712e770fbe Restructure core and built in types into base assembly and nuget (#3326)
This change adds a **new Opc.Ua.Types** nuget that contains core types that do not rely on generated code.  Why the name?  Because almost all code that was in the Types subfolder is part of it, and because we do not want to change the name of the Opc.Ua.Core nuget/assembly because code relies on it.  Any other name like base or bootstrap felt out of place due to the current structure already defining the name "types".  Key design is to depend on only a small set of libraries with limited transitive dependencies, that includes hashcode, logging.abstraction, and for non standard20 builds System.Text.Json (needed for localizedText mul parsing).  Also, key is to support .netstd 2.0 to support source generators and to allow removal of EEC_SUPPORT defines by removing std2.0 from core and all libraries.

The changes are mostly restructuring, and thus small but contain some key changes:

- **Status code** struct now comes with a Symbol field, which is used to carry symbols in case the code is not generated.  Currently (until generated) the status code struct must be manually created with a symbol or the ToSymbolicId extension must be called in the core project.  Future will generate Status code constants just like NodeId that contain the symbol which also removes the reflection-based lookup.  Several tests needed to be adjusted as they checked the status code symbol strings instead of the code.
- **Core identifiers** are copied as they were needed for code to compile, that includes attributes, a subset of status codes and node identifiers.  These will never change so no need to generate them.
- The types folder now contains **encoders**, except for json which is still in core.  Once the json encoder is ported to system.text.json it will also be included and Newtonsoft.Json dropped as dependency (-> #3329).  
- Since types relies on **IOperationContext** and **ISystemContext**, and these export IUserIdentity which relies on crypto, these were split and extended in core with ISessionOperationContext and ISessionSystemContext, which mean changes to ServerSystemContext and introduction of SessionOperationContext and SessionSystemContext classes that extend OperationContext and SystemContext.  Some runtime checks in the server are therefore needed to get at the session and auth info, however, those are anyway optional fields and are not on the critical paths, and thus a type check is in order (and was done in the past already, but against OperationContext class).
- **Manifest resources** are now split between Core (nodeset zip, some generated resoures) and Types (built in bsd/xsds).  This needs to be revisited later if the bsd/xsd need to be regenerated during type generation.  Note that the embedded resource names changed as a result. Since these are only used internally and do not constitute public API this feels reasonable vs duplication of resources in 2 assemblies.
- Reflection however now needs to load not just the built in **encodeables** from Opc.Ua.Types assembly, but also the generated encodeables from Opc.Ua.Core assembly.  For this a new helper Utils method was added to get the Core assembly when linked against Opc.Ua.Core otherwise null.
- Whatever utils methods needed to be split out into the new Opc.Ua.Types assembly, they are now in **CoreUtils** static class in Opc.Ua.Types, and Utils.xxxx forwards to them to ensure minimal changes.  Later we would want to obsolete them in favor of CoreUtils, although it might make sense to split them again into domain specific utilities, such as networking, files, etc
2025-11-10 19:33:24 +01:00
Marc Schier 1c178402ad Refactor client state persistence and add more tests (#3303)
* State handling and better serialization
* Nullable for almost everything  inclient library
* Additional testing of session
2025-11-01 17:18:48 +01:00
fab6f2ac1a [Client] Obsolete APM calls (#3282)
* Refactor for robustness and async improvements

Refactored code to enhance robustness and maintainability by enabling nullable reference types and using `async`/`await` for asynchronous operations. Updated exception handling to use `BadNotConnected()` for more accurate error states. Removed unused code and improved test files to align with these changes. Introduced `OpenInternalAsync` for better channel management and handled on-demand connection creation in `SendRequestAsync`.

* Obsolete APM
* Remove WCF remainders, channel clean up and initial cut at Channel factory
* Clean up copyright notice formatting
* Make connect async in Socket and abstract bindings for easier testing
* Fix unit tests

These changes enhance robustness, maintainability, and correctness of channel and task management.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Roman Ettlinger <romanett98@gmail.com>
2025-10-24 13:37:59 +02:00
d619d82a91 Fix client Close method to properly wait for outstanding publish requests (#3254)
* Add PublishRequestCancelWaitTimeout property and implement cancel logic in CloseAsync

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

* Add unit tests for PublishRequestCancelWaitTimeout feature

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

* Handle OperationCanceledException in wait loop for publish requests

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

* Dont create new Publish Requests while closing

---------

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: Roman Ettlinger <romanett98@gmail.com>
2025-10-23 08:52:20 +02:00
Marc Schier 4c651d05d2 Improve test stability of ValidateTransfer test, ensure thread safety in MonitoredItem (#3243)
* Improve thread safety and logging in MonitoredItem

Replaced Queue with ConcurrentQueue in MonitoredItemDataCache
for thread-safe operations. Updated Publish method to use
List<DataValue> for better handling of dequeued values. Enhanced
logging to include SourceTime and added logs for dropped values.

In DurableSubscriptionTest, added try-finally block to ensure
proper session closure and disposal, preventing memory leaks.
Introduced DurableSubscriptionTestLooper for stress testing
under debug mode. Refactored test code for improved readability.

* Improve logging and session management

Updated logging in `Session.cs`, `ServerBase.cs`, and `UaSCBinaryClientChannel.cs` to pass exceptions directly to the logger, enhancing error handling. Added connection state checks in `Session.cs` to prevent operations when the session is closed or never connected. Updated `Subscription.cs` to reflect session disconnection in `PublishingStopped`. Enhanced test outputs and renamed `DurableSubscriptionTestLooper` to `DurableSubscriptionTestDebug` in `DurableSubscriptionTest.cs`, reducing loop count for faster debug testing.

* Fix monitored item id factory producing collisions by first trying to allocate a free monitored item id in the table, then committing.

* Better diffing

* Enhance logging and session management

Added detailed logging in `AlarmNodeManager.cs` for lifecycle events and simulation control. Introduced a `Reconnecting` property in the `ISession` interface and `Session.cs` to manage session reconnection state, replacing the `m_reconnecting` field. Enhanced logging in `Session.cs` for reconnection and publish operations. Updated `DataGenerator.cs` to use `FrozenDictionary` for performance improvements. Replaced `SyncRoot` with `m_syncRoot` in `NamespaceTable.cs` for synchronization. Improved exception message formatting in `ServiceResult.cs`. Added necessary namespaces for new data structures. Made corrections and added debugging in `DurableSubscriptionTest.cs` for better session handling.

* Dispose simulation
2025-10-11 19:51:38 +02:00
Marc Schier 25ba2a2d08 Make sync client api obsolete (#3174) 2025-08-27 06:05:36 -07:00
romanett fb2a096789 Update GDS Client to use async Client API and deprecate sync Calls. (#3164)
* Update GDS Client to use async Client API
2025-08-18 17:21:58 +02:00
Marc Schier 9830e5d53e Enable roslynizer and fix all analyzer issues (#3157)
* Fix warnings and suggestions
* Enable roslynator
2025-08-16 04:10:24 -07:00
Marc Schier 96efa0a88a Add a LRU node cache for complex type system (#3137) 2025-08-06 12:54:44 +02:00
Marc Schier 5eb9ed8918 Backporting changes in preparation of adding additional fuzz tests (in separate PR) (#3125)
- Replaced `IList<INode>` with `List<INode>` in
  `ComplexTypeSystem.cs` and `BaseComplexType.cs` for
  better type safety.
- Updated string comparison in `ReverseConnectManager.cs`
  to `StringComparison.Ordinal` for improved performance.
- Added a method to `ISession.cs`, `Session.cs`, and
  `SessionAsync.cs` to trigger reloading instance certificates.
- Minor fixes in error handling in `Subscription.cs` and using pattern matching for type checks in several locations to improve code readability.
- Made minor adjustments to comments and documentation
  for clarity and improved code quality.
- Updated `Variant` handling in preparation for immutable struct.
- Changed return types in `BaseObjectState.cs` and `BaseVariableTypeState.cs` to use `ObjectTypeIds` for iperf
- Cleaned up `ExpandedNodeId`
- Enhanced memory management  error handling and readabilityand error handling in encoders
2025-07-31 15:54:42 +02:00
b50d7bcac8 [Client] Allow to modify sensitivity of KeepAliveStopped (#3105)
* Allow to modify sensitivity of KeepAliveStopped

* Apply suggestions from code review

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

---------

Co-authored-by: Marc Schier <marcschier@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-25 10:31:55 +03:00
romanett dc12a4bc9f [Client] Fix closing of shared Transport Channel in Recreate Scenario / Add missing event Handler to Session Constructor / Call RenewUserIdentity on Session.ReCreate (#3055)
* Extend Session.Open Method with new overload allowing to specify if the used channel is closed when the Session.Open fails
Add Missing Event Handler m_RenewUserIdentity om Session constructor taking a template

* Implement Traceable Session
Create Async open overload

* keep channel open on ReCreateAsync with provided channel

* Add Test executing Recreate using existing channel

* fix test

* Add RenewUserIdentity Handler in case of a RecreateSession

* Add Test, fix Recreate to use new Token

* Improve code coverage with async test overload

* fix Parameter checkDomain in Open Session overload
2025-05-23 16:33:14 +03:00
mtx500 57d638ea90 Typos again (#2906)
* Fixed a lot of typos in comments, strings and local variable names. No changes to misspelled function names/parameters.

* Fixed some more typos
2024-12-20 09:33:13 +02:00
ThomasNehring a29d871711 [Client] Read large dictionaries in chunks (#2782)
A new method is implemented which reads byte strings in chunks and therefore allows to read byte strings which exceed the encoding limits of the server. This is used in the method which reads v103 data dictionaries.
2024-10-09 11:30:49 +02:00
ThomasNehring fd762bf9eb Managed Browse and Browse Next for the UA client. (#2673)
Added a new method ManagedBrowse to the UA  client, which takes care of calling browse next after calling browse, when necessary. It also handles the status codes BadNoContinuationPoint and BadInvalidContinuationPoint.

Added support for the new method in ISession, TraceableSession, Session and SessionAsync.

Use the new methods in the NodeCache and other API that use browse (FetchRefrences etc).

Added unit tests which verify correct behavior of the new methods.
2024-08-18 03:36:21 +02:00
Marc Schier 784dde674b Enhance publish request count management to allow limiting the publish requests to a max count (#2681)
* Enhance publish request count management

- Added `MaxPublishRequestCount` property to `ISession` and `Session` in `Opc.Ua.Client`, allowing control over the maximum number of publish requests per session, with validation against predefined constants `kDefaultPublishRequestCount` and `kMaxPublishRequestCountMax` (set to `ushort.MaxValue`).
- Introduced `kMaxPublishRequestCountMax` constant in `Session` class for the maximum publish request count limit.
- Modified `Session` constructor to initialize `m_maxPublishRequestCount` with `kMaxPublishRequestCountMax`.
- Updated `StartPublishing` and `QueueBeginPublish` methods in `Session` to use `GetDesiredPublishRequestCount` for dynamic adjustment of publish requests, considering the session's current state and the new min/max limits.
- Added `GetDesiredPublishRequestCount` method in `Session` to calculate the optimal number of active publish requests, ensuring adherence to the newly established limits.
- Made minor adjustments and refactoring in `Session` for clarity, consistency, and to support the new publish request count management logic.

* Fix build

* Lower logging level for debug level message

* PR feedback
2024-07-31 20:18:55 +03:00
romanett 3479eb686a Opc UA Client Structure (#2668)
* restructure client library to use folder structure as in the server lib
2024-07-11 13:33:30 +02:00