ADS · WPF

WPF + ADS 通信:心跳检测与指数退避自动重连工程实现

ADS 心跳重连 WPF MVVM 2025-12-30 · 尚工

一、问题背景

工业现场网络并不稳定,网线松动、网络切换、PLC 重启都会导致 ADS 连接断开。如果上位机没有自动重连机制,工程师必须手动重启软件,这在无人值守的产线上是不可接受的。

本文介绍一套经过半导体设备现场验证的心跳检测 + 指数退避自动重连方案,完整 C# MVVM 实现。

二、整体架构

连接状态机包含三个状态:

三、IAdsService 接口定义

// IAdsService.cs — 定义通信接口,便于 Mock 测试
public interface IAdsService
{
    bool IsConnected { get; }
    event Action<bool> ConnectionChanged;
    Task<T> ReadAsync<T>(string symbolName);
    Task WriteAsync<T>(string symbolName, T value);
}

四、心跳检测 + 指数退避核心实现

// AdsService.cs — 指数退避重连(1→2→4→8→15→30s)
private int[] _backoffSeconds = { 1, 2, 4, 8, 15, 30 };
private int  _retryIndex = 0;

private async Task HeartbeatLoopAsync(CancellationToken ct)
{
    while (!ct.IsCancellationRequested)
    {
        await Task.Delay(1000, ct);
        try
        {
            var state = await _client.ReadStateAsync();
            if (state.AdsState != AdsState.Run)
                throw new AdsException("PLC not in Run state");
            _retryIndex = 0;          // 重置退避计数
            SetConnected(true);
        }
        catch
        {
            SetConnected(false);
            await ReconnectWithBackoffAsync(ct);
        }
    }
}

private async Task ReconnectWithBackoffAsync(CancellationToken ct)
{
    var wait = _backoffSeconds[
        Math.Min(_retryIndex, _backoffSeconds.Length - 1)];
    _retryIndex++;
    await Task.Delay(wait * 1000, ct);
    try
    {
        _client.Disconnect();
        _client.Connect(_amsNetId, _port);
        SetConnected(true);
        _retryIndex = 0;
    }
    catch { /* 继续下一轮退避 */ }
}

五、ViewModel 绑定连接状态

// MainViewModel.cs — 连接状态绑定到 UI
public string ConnectionStatus =>
    _adsService.IsConnected ? "● 在线" : "○ 离线";

public string StatusColor =>
    _adsService.IsConnected ? "#2e9e5b" : "#cc3333";

// 构造函数中订阅事件,通过 Dispatcher 回到 UI 线程
_adsService.ConnectionChanged += isConnected =>
    Application.Current.Dispatcher.Invoke(() =>
    {
        OnPropertyChanged(nameof(ConnectionStatus));
        OnPropertyChanged(nameof(StatusColor));
    });

💡 重连后必须重新订阅 ADS Notification:连接断线时已注册的 Notification 句柄会失效,重连成功后需重新调用 AddDeviceNotificationAsync,否则实时数据推送会静默失效——这是最常被遗漏的一步。

完整工程源码含 IAdsService 接口、AdsService 实现、Mock 测试类,已上传至 github.com/tc3-engineer