diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI/ZB.MOM.WW.OtOpcUa.Client.CLI.csproj b/src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI/ZB.MOM.WW.OtOpcUa.Client.CLI.csproj
index fa00ef1c..03aa382a 100644
--- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI/ZB.MOM.WW.OtOpcUa.Client.CLI.csproj
+++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI/ZB.MOM.WW.OtOpcUa.Client.CLI.csproj
@@ -4,6 +4,7 @@
Exe
net10.0
enable
+ true
enable
ZB.MOM.WW.OtOpcUa.Client.CLI
diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/OpcUaClientService.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/OpcUaClientService.cs
index fc9ee3b7..b64741ff 100644
--- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/OpcUaClientService.cs
+++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/OpcUaClientService.cs
@@ -237,10 +237,8 @@ public sealed class OpcUaClientService : IOpcUaClientService
hasChildren));
}
- if (continuationPoint != null && continuationPoint.Length > 0)
- (continuationPoint, references) = await _session.BrowseNextAsync(continuationPoint, ct);
- else
- break;
+ if (continuationPoint is not { Length: > 0 } pending) break;
+ (continuationPoint, references) = await _session.BrowseNextAsync(pending, ct);
}
return results;
diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/ZB.MOM.WW.OtOpcUa.Client.Shared.csproj b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/ZB.MOM.WW.OtOpcUa.Client.Shared.csproj
index 4ad02026..a2e8e203 100644
--- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/ZB.MOM.WW.OtOpcUa.Client.Shared.csproj
+++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/ZB.MOM.WW.OtOpcUa.Client.Shared.csproj
@@ -3,6 +3,7 @@
net10.0
enable
+ true
enable
ZB.MOM.WW.OtOpcUa.Client.Shared
diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/ZB.MOM.WW.OtOpcUa.Client.UI.csproj b/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/ZB.MOM.WW.OtOpcUa.Client.UI.csproj
index 5e5a5e0e..db6dcb97 100644
--- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/ZB.MOM.WW.OtOpcUa.Client.UI.csproj
+++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/ZB.MOM.WW.OtOpcUa.Client.UI.csproj
@@ -4,6 +4,7 @@
WinExe
net10.0
enable
+ true
enable
ZB.MOM.WW.OtOpcUa.Client.UI
diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser/OpcUaClientDriverBrowser.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser/OpcUaClientDriverBrowser.cs
index 9a9d822e..af7c2ac0 100644
--- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser/OpcUaClientDriverBrowser.cs
+++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser/OpcUaClientDriverBrowser.cs
@@ -124,17 +124,13 @@ public sealed class OpcUaClientDriverBrowser : IDriverBrowser
/// Cancellation token for the configuration build.
private static async Task BuildBrowseAppConfigurationAsync(CancellationToken ct)
{
- // The default ctor is obsolete in favour of the ITelemetryContext overload; suppress
- // locally rather than plumbing a telemetry context all the way through — the browser
- // emits no per-request telemetry of its own and the SDK's internal fallback is fine
- // for a transient picker session (mirrors the runtime driver's same suppression).
-#pragma warning disable CS0618
- var app = new ApplicationInstance
+ // The ITelemetryContext overload is the non-obsolete ctor; an empty builder keeps SDK
+ // internals off any logging provider.
+ var app = new ApplicationInstance(BrowseTelemetry.Context)
{
ApplicationName = "OtOpcUa AdminUI Browse",
ApplicationType = ApplicationType.Client,
};
-#pragma warning restore CS0618
var pkiRoot = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
@@ -245,3 +241,11 @@ public sealed class OpcUaClientDriverBrowser : IDriverBrowser
private static string ShortName(string uri) =>
uri?.Substring(uri.LastIndexOf('#') + 1) ?? "(null)";
}
+
+internal static class BrowseTelemetry
+{
+ private static readonly Lazy Instance = new(() => Opc.Ua.DefaultTelemetry.Create(_ => { }));
+
+ /// Gets the shared telemetry context.
+ public static Opc.Ua.ITelemetryContext Context => Instance.Value;
+}
diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/OpcUaClientDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/OpcUaClientDriver.cs
index 80ddbd8e..967111cf 100644
--- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/OpcUaClientDriver.cs
+++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/OpcUaClientDriver.cs
@@ -294,17 +294,13 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit
///
private async Task BuildApplicationConfigurationAsync(CancellationToken ct)
{
- // The default ctor is obsolete in favour of the ITelemetryContext overload; suppress
- // locally rather than plumbing a telemetry context all the way through the driver
- // surface — the driver emits no per-request telemetry of its own and the SDK's
- // internal fallback is fine for a gateway use case.
-#pragma warning disable CS0618
- var app = new ApplicationInstance
+ // The ITelemetryContext overload is the non-obsolete ctor; an empty builder keeps SDK
+ // internals off any logging provider (mirrors Client.Shared's ClientTelemetry).
+ var app = new ApplicationInstance(SdkTelemetry.Context)
{
ApplicationName = _options.SessionName,
ApplicationType = ApplicationType.Client,
};
-#pragma warning restore CS0618
var pkiRoot = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
@@ -472,7 +468,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit
endpointConfig.OperationTimeout = (int)_options.Timeout.TotalMilliseconds;
var endpoint = new ConfiguredEndpoint(null, selected, endpointConfig);
- var session = await new DefaultSessionFactory(telemetry: null!).CreateAsync(
+ var session = await new DefaultSessionFactory(SdkTelemetry.Context).CreateAsync(
appConfig,
endpoint,
false, // updateBeforeConnect
diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/SdkTelemetry.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/SdkTelemetry.cs
new file mode 100644
index 00000000..02d2b8f5
--- /dev/null
+++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/SdkTelemetry.cs
@@ -0,0 +1,16 @@
+using Opc.Ua;
+
+namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient;
+
+///
+/// The one the driver hands the SDK's non-obsolete (telemetry-taking)
+/// overloads. An empty builder keeps SDK internals off any logging provider (mirrors Client.Shared's
+/// ClientTelemetry).
+///
+internal static class SdkTelemetry
+{
+ private static readonly Lazy Instance = new(() => DefaultTelemetry.Create(_ => { }));
+
+ /// Gets the shared telemetry context.
+ public static ITelemetryContext Context => Instance.Value;
+}
diff --git a/src/Tooling/ZB.MOM.WW.OtOpcUa.Analyzers/ZB.MOM.WW.OtOpcUa.Analyzers.csproj b/src/Tooling/ZB.MOM.WW.OtOpcUa.Analyzers/ZB.MOM.WW.OtOpcUa.Analyzers.csproj
index 4cb98585..0718be02 100644
--- a/src/Tooling/ZB.MOM.WW.OtOpcUa.Analyzers/ZB.MOM.WW.OtOpcUa.Analyzers.csproj
+++ b/src/Tooling/ZB.MOM.WW.OtOpcUa.Analyzers/ZB.MOM.WW.OtOpcUa.Analyzers.csproj
@@ -5,6 +5,7 @@
(which on .NET Framework 4.7.2 and .NET 6+ equally resolves netstandard2.0). -->
netstandard2.0
enable
+ true
latest
false
true
diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientContinuationPointReleaseTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientContinuationPointReleaseTests.cs
index 3abc109d..e4fcc323 100644
--- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientContinuationPointReleaseTests.cs
+++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientContinuationPointReleaseTests.cs
@@ -37,7 +37,7 @@ public sealed class OpcUaClientContinuationPointReleaseTests
private static Mock BuildCancellingSessionMock()
{
var mock = new Mock(MockBehavior.Loose);
- mock.SetupGet(s => s.MessageContext).Returns(new ServiceMessageContext());
+ mock.SetupGet(s => s.MessageContext).Returns(new ServiceMessageContext(ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.SdkTelemetry.Context));
mock.SetupGet(s => s.NamespaceUris).Returns(new NamespaceTable());
// BrowseAsync — return one Object child plus a non-empty continuation point so
@@ -148,7 +148,7 @@ public sealed class OpcUaClientContinuationPointReleaseTests
using var drv = new OpcUaClientDriver(new OpcUaClientDriverOptions(), "opcua-cp-norelease");
var mock = new Mock(MockBehavior.Loose);
- mock.SetupGet(s => s.MessageContext).Returns(new ServiceMessageContext());
+ mock.SetupGet(s => s.MessageContext).Returns(new ServiceMessageContext(ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.SdkTelemetry.Context));
mock.SetupGet(s => s.NamespaceUris).Returns(new NamespaceTable());
// BrowseAsync — return a result with a continuation point so the loop is entered.
diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientStaleSessionRaceTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientStaleSessionRaceTests.cs
index 23dc9ac1..07a76e7d 100644
--- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientStaleSessionRaceTests.cs
+++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientStaleSessionRaceTests.cs
@@ -53,7 +53,7 @@ public sealed class OpcUaClientStaleSessionRaceTests
// MessageContext. This getter is read in the driver's synchronous prologue (before the
// gate), so signalling here proves the pre-gate capture path has run.
mock.SetupGet(s => s.MessageContext)
- .Returns(new ServiceMessageContext())
+ .Returns(new ServiceMessageContext(ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.SdkTelemetry.Context))
.Callback(() => prologueReached?.TrySetResult());
mock.SetupGet(s => s.NamespaceUris).Returns(new NamespaceTable());
mock.Setup(s => s.HistoryReadAsync(