chore(build): OpcUaClient driver/browser on ITelemetryContext ctors, TreatWarningsAsErrors on remaining src projects (#621 #606)

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
Joseph Doherty
2026-10-08 06:59:25 -04:00
parent 9b159025f7
commit b007d450d8
10 changed files with 40 additions and 22 deletions
@@ -4,6 +4,7 @@
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework> <TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>ZB.MOM.WW.OtOpcUa.Client.CLI</RootNamespace> <RootNamespace>ZB.MOM.WW.OtOpcUa.Client.CLI</RootNamespace>
</PropertyGroup> </PropertyGroup>
@@ -237,10 +237,8 @@ public sealed class OpcUaClientService : IOpcUaClientService
hasChildren)); hasChildren));
} }
if (continuationPoint != null && continuationPoint.Length > 0) if (continuationPoint is not { Length: > 0 } pending) break;
(continuationPoint, references) = await _session.BrowseNextAsync(continuationPoint, ct); (continuationPoint, references) = await _session.BrowseNextAsync(pending, ct);
else
break;
} }
return results; return results;
@@ -3,6 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net10.0</TargetFramework> <TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>ZB.MOM.WW.OtOpcUa.Client.Shared</RootNamespace> <RootNamespace>ZB.MOM.WW.OtOpcUa.Client.Shared</RootNamespace>
</PropertyGroup> </PropertyGroup>
@@ -4,6 +4,7 @@
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net10.0</TargetFramework> <TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>ZB.MOM.WW.OtOpcUa.Client.UI</RootNamespace> <RootNamespace>ZB.MOM.WW.OtOpcUa.Client.UI</RootNamespace>
</PropertyGroup> </PropertyGroup>
@@ -124,17 +124,13 @@ public sealed class OpcUaClientDriverBrowser : IDriverBrowser
/// <param name="ct">Cancellation token for the configuration build.</param> /// <param name="ct">Cancellation token for the configuration build.</param>
private static async Task<ApplicationConfiguration> BuildBrowseAppConfigurationAsync(CancellationToken ct) private static async Task<ApplicationConfiguration> BuildBrowseAppConfigurationAsync(CancellationToken ct)
{ {
// The default ctor is obsolete in favour of the ITelemetryContext overload; suppress // The ITelemetryContext overload is the non-obsolete ctor; an empty builder keeps SDK
// locally rather than plumbing a telemetry context all the way through — the browser // internals off any logging provider.
// emits no per-request telemetry of its own and the SDK's internal fallback is fine var app = new ApplicationInstance(BrowseTelemetry.Context)
// for a transient picker session (mirrors the runtime driver's same suppression).
#pragma warning disable CS0618
var app = new ApplicationInstance
{ {
ApplicationName = "OtOpcUa AdminUI Browse", ApplicationName = "OtOpcUa AdminUI Browse",
ApplicationType = ApplicationType.Client, ApplicationType = ApplicationType.Client,
}; };
#pragma warning restore CS0618
var pkiRoot = Path.Combine( var pkiRoot = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
@@ -245,3 +241,11 @@ public sealed class OpcUaClientDriverBrowser : IDriverBrowser
private static string ShortName(string uri) => private static string ShortName(string uri) =>
uri?.Substring(uri.LastIndexOf('#') + 1) ?? "(null)"; uri?.Substring(uri.LastIndexOf('#') + 1) ?? "(null)";
} }
internal static class BrowseTelemetry
{
private static readonly Lazy<Opc.Ua.ITelemetryContext> Instance = new(() => Opc.Ua.DefaultTelemetry.Create(_ => { }));
/// <summary>Gets the shared telemetry context.</summary>
public static Opc.Ua.ITelemetryContext Context => Instance.Value;
}
@@ -294,17 +294,13 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit
/// </summary> /// </summary>
private async Task<ApplicationConfiguration> BuildApplicationConfigurationAsync(CancellationToken ct) private async Task<ApplicationConfiguration> BuildApplicationConfigurationAsync(CancellationToken ct)
{ {
// The default ctor is obsolete in favour of the ITelemetryContext overload; suppress // The ITelemetryContext overload is the non-obsolete ctor; an empty builder keeps SDK
// locally rather than plumbing a telemetry context all the way through the driver // internals off any logging provider (mirrors Client.Shared's ClientTelemetry).
// surface — the driver emits no per-request telemetry of its own and the SDK's var app = new ApplicationInstance(SdkTelemetry.Context)
// internal fallback is fine for a gateway use case.
#pragma warning disable CS0618
var app = new ApplicationInstance
{ {
ApplicationName = _options.SessionName, ApplicationName = _options.SessionName,
ApplicationType = ApplicationType.Client, ApplicationType = ApplicationType.Client,
}; };
#pragma warning restore CS0618
var pkiRoot = Path.Combine( var pkiRoot = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
@@ -472,7 +468,7 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit
endpointConfig.OperationTimeout = (int)_options.Timeout.TotalMilliseconds; endpointConfig.OperationTimeout = (int)_options.Timeout.TotalMilliseconds;
var endpoint = new ConfiguredEndpoint(null, selected, endpointConfig); var endpoint = new ConfiguredEndpoint(null, selected, endpointConfig);
var session = await new DefaultSessionFactory(telemetry: null!).CreateAsync( var session = await new DefaultSessionFactory(SdkTelemetry.Context).CreateAsync(
appConfig, appConfig,
endpoint, endpoint,
false, // updateBeforeConnect false, // updateBeforeConnect
@@ -0,0 +1,16 @@
using Opc.Ua;
namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient;
/// <summary>
/// The one <see cref="ITelemetryContext" /> 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
/// <c>ClientTelemetry</c>).
/// </summary>
internal static class SdkTelemetry
{
private static readonly Lazy<ITelemetryContext> Instance = new(() => DefaultTelemetry.Create(_ => { }));
/// <summary>Gets the shared telemetry context.</summary>
public static ITelemetryContext Context => Instance.Value;
}
@@ -5,6 +5,7 @@
(which on .NET Framework 4.7.2 and .NET 6+ equally resolves netstandard2.0). --> (which on .NET Framework 4.7.2 and .NET 6+ equally resolves netstandard2.0). -->
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<IsRoslynComponent>true</IsRoslynComponent> <IsRoslynComponent>true</IsRoslynComponent>
@@ -37,7 +37,7 @@ public sealed class OpcUaClientContinuationPointReleaseTests
private static Mock<ISession> BuildCancellingSessionMock() private static Mock<ISession> BuildCancellingSessionMock()
{ {
var mock = new Mock<ISession>(MockBehavior.Loose); var mock = new Mock<ISession>(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()); mock.SetupGet(s => s.NamespaceUris).Returns(new NamespaceTable());
// BrowseAsync — return one Object child plus a non-empty continuation point so // 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"); using var drv = new OpcUaClientDriver(new OpcUaClientDriverOptions(), "opcua-cp-norelease");
var mock = new Mock<ISession>(MockBehavior.Loose); var mock = new Mock<ISession>(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()); mock.SetupGet(s => s.NamespaceUris).Returns(new NamespaceTable());
// BrowseAsync — return a result with a continuation point so the loop is entered. // BrowseAsync — return a result with a continuation point so the loop is entered.
@@ -53,7 +53,7 @@ public sealed class OpcUaClientStaleSessionRaceTests
// MessageContext. This getter is read in the driver's synchronous prologue (before the // 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. // gate), so signalling here proves the pre-gate capture path has run.
mock.SetupGet(s => s.MessageContext) mock.SetupGet(s => s.MessageContext)
.Returns(new ServiceMessageContext()) .Returns(new ServiceMessageContext(ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.SdkTelemetry.Context))
.Callback(() => prologueReached?.TrySetResult()); .Callback(() => prologueReached?.TrySetResult());
mock.SetupGet(s => s.NamespaceUris).Returns(new NamespaceTable()); mock.SetupGet(s => s.NamespaceUris).Returns(new NamespaceTable());
mock.Setup(s => s.HistoryReadAsync( mock.Setup(s => s.HistoryReadAsync(