using Opc.Ua;
namespace ZB.MOM.WW.OtOpcUa.Client.Shared.Adapters;
///
/// The one the shared client hands the SDK's non-obsolete (telemetry-taking)
/// overloads. An empty builder keeps SDK internals off any logging provider; the client logs through Serilog.
///
internal static class ClientTelemetry
{
private static readonly Lazy Instance = new(() => DefaultTelemetry.Create(_ => { }));
/// Gets the shared telemetry context.
public static ITelemetryContext Context => Instance.Value;
}
///
/// Bounded sync-over-async for the Dispose paths, which cannot await. Runs on the thread pool so a
/// caller on a synchronization context (the Avalonia UI thread) cannot deadlock, and waits at most
/// so a dead server cannot hang teardown.
///
internal static class DisposeBridge
{
private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(5);
/// Runs to completion (or the timeout), swallowing every failure.
/// The asynchronous work to run; it receives a token that is cancelled when the timeout elapses.
public static void Run(Func work)
{
using var cts = new CancellationTokenSource(Timeout);
try
{
var task = Task.Run(() => work(cts.Token), cts.Token);
// Observe a late fault so it never surfaces as an unobserved-task exception.
_ = task.ContinueWith(t => _ = t.Exception, TaskContinuationOptions.OnlyOnFaulted);
// The CTS's own timer and this Wait race: when Wait loses, `using` would dispose the CTS before
// the token ever fired, leaving the work running while the caller goes on to dispose the session.
// Cancel explicitly so the in-flight Close/Delete is told to stop.
if (!task.Wait(Timeout)) { cts.Cancel(); }
}
catch
{
// Teardown is best-effort, exactly as the synchronous Close/Delete it replaces.
}
}
}