using System; using System.Threading; using System.Threading.Tasks; using ZB.MOM.WW.OtOpcUa.Host.Domain; namespace ZB.MOM.WW.OtOpcUa.Host.MxAccess { public sealed partial class MxAccessClient { private Task? _monitorTask; /// /// Starts the background monitor that reconnects dropped sessions and watches the probe tag for staleness. /// public void StartMonitor() { if (_monitorCts != null) StopMonitor(); _monitorCts = new CancellationTokenSource(); _monitorTask = Task.Run(() => MonitorLoopAsync(_monitorCts.Token)); Log.Information("MxAccess monitor started (interval={Interval}s)", _config.MonitorIntervalSeconds); } /// /// Stops the background monitor loop. /// public void StopMonitor() { _monitorCts?.Cancel(); try { _monitorTask?.Wait(TimeSpan.FromSeconds(5)); } catch { /* timeout or faulted */ } _monitorTask = null; } private async Task MonitorLoopAsync(CancellationToken ct) { while (!ct.IsCancellationRequested) { try { await Task.Delay(TimeSpan.FromSeconds(_config.MonitorIntervalSeconds), ct); } catch (OperationCanceledException) { break; } try { if ((_state == ConnectionState.Disconnected || _state == ConnectionState.Error) && _config.AutoReconnect) { Log.Information("Monitor: connection lost (state={State}), attempting reconnect", _state); await ReconnectAsync(); continue; } if (_state == ConnectionState.Connected && _probeTag != null) { var elapsed = DateTime.UtcNow - _lastProbeValueTime; if (elapsed.TotalSeconds > _config.ProbeStaleThresholdSeconds) { Log.Warning("Monitor: probe stale ({Elapsed:F0}s > {Threshold}s), forcing reconnect", elapsed.TotalSeconds, _config.ProbeStaleThresholdSeconds); await ReconnectAsync(); } } } catch (Exception ex) { Log.Error(ex, "Monitor loop error"); } } Log.Information("MxAccess monitor stopped"); } } }