# Description Nullable reference types are now enabled once, in `common.props`, and every project in the repository builds warning-clean with them. **Configuration** - `<Nullable>enable</Nullable>` lives in `common.props`. All per-project `<Nullable>` settings are removed: 178 project files (enable, annotations, and the `disable` in the three Aot test projects). - 419 per-file `#nullable enable`/`restore` directives and the 4 `#nullable disable` directives are removed. - Kept on purpose: - The 5 `<auto-generated>` schema files (`UANodeSet.cs`, `UA Type Dictionary.cs`, `OPCBinarySchema.cs`, `SecuredApplication.cs`, `PInvokeHelper.cs`) keep `#nullable enable`. The compiler ignores the project setting in generated code, so the directive is required there. - `tests/Opc.Ua.Interop.LegacyPeer` keeps `disable`. It builds against the 1.5.378 packages and does not import the repository props. - `docs/DeveloperGuide.md` and `.github/copilot-instructions.md` now describe the central setting. **Migration** (the 43 unset and 13 annotations-only projects, roughly 10k diagnostics) - **Production code: annotated at the declarations.** Covers `tools/Opc.Ua.SourceGeneration.Core`, `src/Opc.Ua.Lds.Server` and the LDS sample. - Members, parameters and returns are nullable where null actually flows. - Null-check helpers carry `[NotNullWhen]` (`IsNull`, `IsValidXmlName`, `IsMethodTypeDesign`). - `CoreUtils.Format`, `Utils.Format`, `ITemplateWriter.WriteLine` take `params object?[]`. `Template.AddReplacement` takes `string?` (rendering already skips null replacements). - `IRegisteredServerStore.RegisterAsync` returns `Task<RegistrationEntry?>`: it returns null when a registration is removed. - Dereferences backed by an invariant (for example, the validated model) use `!`, so runtime behaviour is unchanged. - **Behavioural changes in production code** (small, deliberate): - `NodeSetToModelDesign` throws `InvalidOperationException` when the file is not a NodeSet. Before, it threw a NullReferenceException on the next line. - `ModelDesignValidator` starts its lookup tables empty instead of null. A redundant `if (m_nodes == null) return;` guard is gone. - **Test code: minimal annotations.** - `?` on values that can be null, `!` where the test asserts non-null. - `= null!` for fixture members assigned in SetUp. - `TryGetStructure<T>(out T? x)` where the inferred `T?` broke the `IEncodeable` constraint. - A few redundant `== null` checks on structs (`NodeId`, `ArrayOf<T>`) folded into the adjacent `IsNull`/`Count` checks. **Notes for reviewers** - The diff is large (~1,500 files) but almost entirely mechanical: `?`, `!` and directive removal. The non-annotation changes are listed above. - Known follow-up, not done here: `IModelDesign.Namespaces/TargetNamespace/NamespaceUris` stay non-nullable even though the validator returns null before it loads a dictionary. Making them nullable would ripple through every generator call site. ## Related Issues - Fixes #4439 ## Checklist - [ ] I have signed the [CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf) and read the [CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md) doc. - [ ] I have added tests that prove my fix is effective or that my feature works and increased code coverage. - [x] I have added all necessary documentation. - [x] I have verified that my changes do not introduce (new) build or analyzer warnings. - [ ] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. - [ ] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. - [ ] I have addressed **all** PR feedback received. The "no new warnings" box covers net10.0 and net48: full non-incremental builds of `UA.slnx`, `tools/SourceGeneration.slnx`, `tools/MigrationAnalyzer.slnx` and the Aot test projects finished with 0 warnings and 0 errors under the repository's warnings-as-errors settings. net9.0 and net8.0 also had no nullable diagnostics, but they were not rebuilt after the final fixes. Tests have not been run locally yet. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Console Reference PubSub
A single, self-contained OPC UA Part 14 PubSub reference application built on the
fluent PubSubApplicationBuilder + dependency injection + .NET Generic Host surface.
One executable exposes three command-line-selectable modes, and publishes as a
NativeAOT-ready single-file executable.
Modes
ConsoleReferencePubSubClient <mode> [options]
| Mode | Purpose |
|---|---|
publisher |
Publishes a built-in sample DataSet over UDP/UADP or MQTT (UADP/JSON). |
subscriber |
Receives DataSets and logs each decoded message to the console. |
external |
Bridges an external OPC UA server to PubSub via the Opc.Ua.PubSub.Adapter library (publisher / subscriber / responder direction). |
publisher
ConsoleReferencePubSubClient publisher --profile udp-uadp --interval 1000
ConsoleReferencePubSubClient publisher --profile mqtt-json --endpoint mqtt://localhost:1883
Options: --profile udp-uadp|mqtt-uadp|mqtt-json, --config-file <xml>,
--publisher-id, --writer-group-id, --data-set-writer-id, --endpoint, --interval.
subscriber
ConsoleReferencePubSubClient subscriber --profile udp-uadp
Options: --profile, --config-file <xml>, --publisher-id-filter,
--writer-group-id-filter, --data-set-writer-id-filter, --endpoint.
Publisher and subscriber on one computer
The default UDP endpoint, opc.udp://239.0.0.1:4840, is a multicast address.
Multicast loopback is disabled by default, so a subscriber on the publisher's
computer can miss the messages. To run both on one computer, give both the
unicast loopback endpoint:
ConsoleReferencePubSubClient subscriber --profile udp-uadp --endpoint opc.udp://127.0.0.1:4840
ConsoleReferencePubSubClient publisher --profile udp-uadp --endpoint opc.udp://127.0.0.1:4840
See Run a first publisher and subscriber for the expected output and the settings that must match.
external
Bridges an external OPC UA server (defaults to the repository's ConsoleReferenceServer
at opc.tcp://localhost:62541/Quickstarts/ReferenceServer; override with --endpoint or
the OPCUA_EXTERNAL_ENDPOINT environment variable).
# Read an external server and publish its values (cyclic Read each cycle)
ConsoleReferencePubSubClient external --mode publisher --read-mode cyclic
# Read via a client Subscription cache, one subscription per WriterGroup
ConsoleReferencePubSubClient external --mode publisher --read-mode subscription --affinity writergroup
# Write received PubSub values back to an external server
ConsoleReferencePubSubClient external --mode subscriber
# Map an inbound PubSub Action to an external server method call
ConsoleReferencePubSubClient external --mode responder
# Run a bidirectional bridge in one process
ConsoleReferencePubSubClient external --mode publisher,subscriber
Options: --mode publisher|subscriber|responder (comma-separated list accepted),
--read-mode cyclic|subscription, --affinity writergroup|datasetwriter,
--endpoint <external server>, --pubsub-endpoint <udp url>, --hot-reload.
Hot reload
Add --hot-reload to the external bridge to run the opt-in live reconfiguration
demo:
ConsoleReferencePubSubClient external --mode publisher,subscriber --hot-reload
The bridge writes pubsub-config.xml next to the executable before starting and
loads adapter options from the copied appsettings.json in the same directory.
Edit and save either file while the bridge is running:
appsettings.json: changeExternalPublisher:ReadModefromCyclictoSubscription, or changeExternalPublisher:AffinitytoDataSetWriter, to rewire the external-server publisher options.pubsub-config.xml: add or remove aDataSetWriterto change the PubSub topology. The watching XML store raises a configuration change and the adapter incrementally rewires the affected binding.
The samples connect to the external server unsecured (
SecurityMode.None) for zero-config interop. A production bridge must useSignAndEncryptwith a provisioned application instance certificate. See docs/PubSub.md external-server adapter section.
Build / publish
dotnet build samples/PubSub/ConsoleReferencePubSubClient/ConsoleReferencePubSubClient.csproj
dotnet publish samples/PubSub/ConsoleReferencePubSubClient/ConsoleReferencePubSubClient.csproj -r win-x64
See docs/PubSub.md for the full PubSub guide.