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.
* 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
* 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
* 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.
* 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
* Use Interfaces Introduced with 1.5.378
Refactor to use interfaces for node managers and server
Replaced concrete types with interfaces (e.g., IMasterNodeManager, ICoreNodeManager, IDiagnosticsNodeManager, IConfigurationNodeManager, IServerBase) across method signatures, properties, and internal structures to improve abstraction and flexibility.
* Refactor CoreNodeManager to be Based on CustomNodeManager2
* Add Test
* Add INodeManager3 with role permission validation
Introduce INodeManager3 interface extending INodeManager2, adding synchronous role permission validation methods. Update all node manager interfaces and classes to use INodeManager3. Extend IAsyncNodeManager with async permission validation methods and update adapters for sync/async bridging. Deprecate FindNodeInAddressSpace in favor of new async FindNodeInAddressSpaceAsync. Refactor MonitoredNode2 to use thread-safe collections and locking for monitored items. Enhance event reporting with security and permission checks. Update constructors and documentation to reflect new interfaces and best practices.
* Implement AsyncCustomNodeManager
* fix comment
* fix
* Add tests
* fix
* Implement better locking strategy
* fix
* Add more tests
* Add tests
* Add tests
* Add Tests
* cleanup
* cleanup
* Add tests that verifies for AnalogItemState the instrument range is checked brefore writing
* only assign new NodeID in default factory if NodeId isNull
* Run all tests also with sampling groups
* Add Docs
* cleanup
* Add chaos test
* fix test
* Use AsyncCustomNodeManager for ReferenceNodeManger & imrpove ReadAttributes method of NodeState
* fixes
* fixes and code review comments
Generate stack and application code using source generators
Change built in types to readonly structs
Update localized text implementation to use JSON text serializer
Status code with symbolic names
Immutable node ids
No boxing for all built in types <= 8 bytes in Variant.
Split user identity token and token handling via token handler
Add migration guide for breaking changes
This pull request modernizes the OPC UA .NET codebase and tests, removes .NET Standard 2.0 support from certificate/security code, and updates test code to use C# 12 and .NET 8+ features for clarity and maintainability.
- Certificate and BouncyCastle interop files (e.g., CertificateBuilder.cs, X509Extensions.cs, X509SignatureFactory.cs) are refactored to drop NETSTANDARD2_0 support and use modern .NET APIs and conditional compilation.
- Test code across the codebase is updated to use default, strongly-typed Parse methods, collection expressions, and improved async/await patterns.
- Exception handling and assertions in tests are modernized, and test method naming is made consistent with Async suffixes.
- The project and build files are updated to clarify Roslynator analyzer usage and remove obsolete netstandard tags.
- General code and test cleanup includes improved null safety, type safety, and removal of deprecated or unused code.
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