C#与倍福PLC(通过ADS协议)通信上位机源程序实现

简介: C#与倍福PLC(通过ADS协议)通信上位机源程序实现

一、项目结构

├── PLCControlDemo/
│   ├── Models/
│   │   └── PlcVariable.cs       # PLC变量模型
│   ├── Services/
│   │   └── AdsCommunicationService.cs  # ADS通信服务
│   ├── Views/
│   │   ├── MainWindow.xaml      # 主界面
│   │   └── DashboardView.xaml   # 仪表盘视图
│   └── App.xaml.cs            # 应用入口

二、核心代码实现

1. PLC变量模型(Models/PlcVariable.cs)

public class PlcVariable : INotifyPropertyChanged
{
   
    private object _value;
    public string Name {
    get; }
    public string Description {
    get; }
    public object Value
    {
   
        get => _value;
        set
        {
   
            _value = value;
            OnPropertyChanged(nameof(Value));
        }
    }

    public PlcVariable(string name, string description)
    {
   
        Name = name;
        Description = description;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
   
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

2. ADS通信服务(Services/AdsCommunicationService.cs)

using TwinCAT.Ads;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

public class AdsCommunicationService : IDisposable
{
   
    private readonly TcAdsClient _adsClient;
    private readonly string _amsNetId = "192.168.0.1.1.1"; // PLC的AMS Net ID
    private readonly int _port = 851; // 默认ADS端口

    public event EventHandler<AdsEventArgs> ConnectionStateChanged;

    public AdsCommunicationService()
    {
   
        _adsClient = new TcAdsClient();
    }

    public async Task ConnectAsync()
    {
   
        try
        {
   
            await _adsClient.ConnectAsync(_amsNetId, _port);
            ConnectionStateChanged?.Invoke(this, new AdsEventArgs(ConnectionState.Connected));
        }
        catch (AdsException ex)
        {
   
            ConnectionStateChanged?.Invoke(this, new AdsEventArgs(ConnectionState.Failed, ex.Message));
            throw;
        }
    }

    public async Task<T> ReadVariableAsync<T>(string variableName) where T : struct
    {
   
        var handle = await _adsClient.CreateVariableHandleAsync(variableName);
        var value = await _adsClient.ReadAnyAsync(handle, typeof(T));
        _adsClient.DeleteVariableHandle(handle);
        return (T)value;
    }

    public async Task WriteVariableAsync<T>(string variableName, T value) where T : struct
    {
   
        var handle = await _adsClient.CreateVariableHandleAsync(variableName);
        await _adsClient.WriteAnyAsync(handle, value);
        _adsClient.DeleteVariableHandle(handle);
    }

    public void Disconnect()
    {
   
        _adsClient?.Disconnect();
    }

    public void Dispose()
    {
   
        _adsClient?.Dispose();
    }
}

public enum ConnectionState {
    Disconnected, Connected, Failed }
public class AdsEventArgs : EventArgs
{
   
    public ConnectionState State {
    get; }
    public string ErrorMessage {
    get; }

    public AdsEventArgs(ConnectionState state)
    {
   
        State = state;
    }

    public AdsEventArgs(ConnectionState state, string message)
    {
   
        State = state;
        ErrorMessage = message;
    }
}

3. 主界面逻辑(Views/MainWindow.xaml.cs)

using System.Windows;
using System.Windows.Controls;

namespace PLCControlDemo.Views
{
   
    public partial class MainWindow : Window
    {
   
        private readonly AdsCommunicationService _plcService = new AdsCommunicationService();
        private readonly Dictionary<string, PlcVariable> _variables = new Dictionary<string, PlcVariable>();

        public MainWindow()
        {
   
            InitializeComponent();
            InitializeVariables();
            DataContext = this;
            _plcService.ConnectionStateChanged += OnConnectionStateChanged;
        }

        private void InitializeVariables()
        {
   
            // 定义PLC变量映射
            _variables.Add("MAIN.Temperature", new PlcVariable("Temperature", "温度值"));
            _variables.Add("MAIN.StartCommand", new PlcVariable("StartCommand", "启动指令"));
        }

        private async void ConnectButton_Click(object sender, RoutedEventArgs e)
        {
   
            try
            {
   
                await _plcService.ConnectAsync();
                UpdateStatus("已连接");
            }
            catch (Exception ex)
            {
   
                UpdateStatus($"连接失败: {ex.Message}");
            }
        }

        private async void ReadButton_Click(object sender, RoutedEventArgs e)
        {
   
            try
            {
   
                var temp = await _plcService.ReadVariableAsync<double>("MAIN.Temperature");
                TemperatureText.Text = temp.ToString("0.00");
            }
            catch (Exception ex)
            {
   
                MessageBox.Show($"读取失败: {ex.Message}");
            }
        }

        private async void WriteButton_Click(object sender, RoutedEventArgs e)
        {
   
            try
            {
   
                await _plcService.WriteVariableAsync("MAIN.StartCommand", true);
                StartCommandText.Text = "已启动";
            }
            catch (Exception ex)
            {
   
                MessageBox.Show($"写入失败: {ex.Message}");
            }
        }

        private void OnConnectionStateChanged(object sender, AdsEventArgs e)
        {
   
            Dispatcher.Invoke(() =>
            {
   
                ConnectionStatus.Background = e.State switch
                {
   
                    ConnectionState.Connected => System.Windows.Media.Brushes.Green,
                    ConnectionState.Failed => System.Windows.Media.Brushes.Red,
                    _ => System.Windows.Media.Brushes.Gray
                };

                ConnectionStatusText.Text = e.State.ToString() + 
                    (string.IsNullOrEmpty(e.ErrorMessage) ? "" : $"\n错误: {e.ErrorMessage}");
            });
        }

        private void UpdateStatus(string message)
        {
   
            StatusText.Text = message;
        }

        protected override void OnClosed(EventArgs e)
        {
   
            _plcService.Disconnect();
            base.OnClosed(e);
        }
    }
}

4. 主界面XAML(Views/MainWindow.xaml)

<Window x:Class="PLCControlDemo.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="倍福PLC控制面板" Height="450" Width="800">
    <Grid>
        <!-- 状态栏 -->
        <StatusBar VerticalAlignment="Bottom">
            <StatusBarItem Content="{Binding ConnectionStatusText}" 
                           Background="{Binding ConnectionStatus.Background}"/>
            <StatusBarItem Content="{Binding StatusText}"/>
        </StatusBar>

        <!-- 控制面板 -->
        <StackPanel VerticalAlignment="Top" Margin="20">
            <Button Content="连接PLC" Click="ConnectButton_Click" Margin="5"/>
            <Button Content="读取温度" Click="ReadButton_Click" Margin="5"/>
            <Button Content="启动设备" Click="WriteButton_Click" Margin="5"/>

            <!-- 实时数据显示 -->
            <TextBlock Text="温度值:" FontWeight="Bold" Margin="5"/>
            <TextBox x:Name="TemperatureText" Width="200" Margin="5" IsReadOnly="True"/>

            <TextBlock Text="启动指令:" FontWeight="Bold" Margin="5"/>
            <TextBox x:Name="StartCommandText" Width="200" Margin="5" IsReadOnly="True"/>
        </StackPanel>
    </Grid>
</Window>

三、关键功能实现

1. 异步通信优化

// 使用异步方法避免界面冻结
public async Task<double> GetTemperatureAsync()
{
   
    return await _plcService.ReadVariableAsync<double>("MAIN.Temperature");
}

// 批量读取示例
public async Task<PlcDataBatch> GetBatchDataAsync()
{
   
    var data = new PlcDataBatch();
    data.Temperature = await GetTemperatureAsync();
    data.Pressure = await _plcService.ReadVariableAsync<double>("MAIN.Pressure");
    data.Position = await _plcService.ReadVariableAsync<int>("MAIN.Position");
    return data;
}

2. 报警监控系统

// 报警规则定义
public class AlarmRule
{
   
    public string VariableName {
    get; }
    public double HighLimit {
    get; }
    public double LowLimit {
    get; }

    public AlarmRule(string varName, double high, double low)
    {
   
        VariableName = varName;
        HighLimit = high;
        LowLimit = low;
    }

    public bool CheckAlarm(double value)
    {
   
        return value > HighLimit || value < LowLimit;
    }
}

// 报警处理服务
public class AlarmService
{
   
    private readonly AdsCommunicationService _plcService;
    private readonly List<AlarmRule> _rules = new List<AlarmRule>();

    public AlarmService(AdsCommunicationService plcService)
    {
   
        _plcService = plcService;
        InitializeRules();
    }

    private void InitializeRules()
    {
   
        _rules.Add(new AlarmRule("MAIN.Temperature", 80.0, 10.0));
        _rules.Add(new AlarmRule("MAIN.Pressure", 200.0, 50.0));
    }

    public async Task CheckAlarmsAsync()
    {
   
        foreach (var rule in _rules)
        {
   
            var value = await _plcService.ReadVariableAsync<double>(rule.VariableName);
            if (rule.CheckAlarm(value))
            {
   
                ShowAlarm($"[{rule.VariableName}] 超出范围: {value}");
            }
        }
    }

    private void ShowAlarm(string message)
    {
   
        Application.Current.Dispatcher.Invoke(() =>
        {
   
            AlarmLog.AppendText($"{DateTime.Now}: {message}\n");
            System.Media.SoundPlayer alarmSound = new System.Media.SoundPlayer("alarm.wav");
            alarmSound.Play();
        });
    }
}

参考代码 基于C# 编写的倍福plc 应用上位机源程序 www.youwenfan.com/contentalg/113111.html

四、扩展功能实现

1. 历史数据记录

public class DataLogger
{
   
    private readonly string _logFilePath = "plc_data_log.csv";
    private readonly List<PlcDataPoint> _dataBuffer = new List<PlcDataPoint>();

    public void LogData(PlcDataPoint data)
    {
   
        _dataBuffer.Add(data);
        if (_dataBuffer.Count >= 100) // 每100条写入文件
        {
   
            File.WriteAllLines(_logFilePath, _dataBuffer.Select(d => 
                $"{d.Timestamp:yyyy-MM-dd HH:mm:ss},{d.Temperature},{d.Pressure}"));
            _dataBuffer.Clear();
        }
    }
}

public class PlcDataPoint
{
   
    public DateTime Timestamp {
    get; } = DateTime.Now;
    public double Temperature {
    get; set; }
    public double Pressure {
    get; set; }
}

2. 数据可视化仪表盘

<!-- DashboardView.xaml -->
<UserControl x:Class="PLCControlDemo.Views.DashboardView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <!-- 温度仪表盘 -->
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <TextBlock Text="温度监控" FontSize="16" Margin="10"/>

        <Viewbox Grid.Row="1" Stretch="Uniform">
            <Grid>
                <Ellipse Width="200" Height="200" Fill="LightGray"/>
                <RadialGauge 
                    Value="{Binding Temperature}" 
                    Minimum="0" 
                    Maximum="100" 
                    NeedleBrush="Red"
                    TickBrush="Black"
                    ScaleWidth="20"/>
            </Grid>
        </Viewbox>
    </Grid>
</UserControl>

五、部署与调试

1. 环境配置

  • 开发环境:Visual Studio 2022 + .NET 6.0

  • 依赖库

    <!-- PLC通信 -->
    <PackageReference Include="TwinCAT.Ads" Version="6.0.1000.15" />
    <!-- 数据可视化 -->
    <PackageReference Include="MahApps.Metro" Version="2.4.3" />
    

2. 调试技巧

  1. Wireshark抓包分析

    • 过滤条件:tcp.port == 851
    • 验证ADS协议报文结构是否符合规范
  2. 异常捕获增强

    public async Task SafeReadAsync<T>(string varName, out T value)
    {
         
        try
        {
         
            value = await ReadVariableAsync<T>(varName);
        }
        catch (AdsException ex) when (ex.ErrorCode == 0x800A0002)
        {
         
            value = default;
            ShowError($"变量不存在: {varName}");
        }
        catch (TimeoutException)
        {
         
            value = default;
            ShowError("通信超时,请检查网络连接");
        }
    }
    

六、性能优化

  1. 连接池管理

    private static readonly ObjectPool<TcAdsClient> _clientPool = 
        new ObjectPool<TcAdsClient>(() => new TcAdsClient(), 3); // 维护3个连接实例
    
  2. 批量数据传输

    public async Task<byte[]> ReadBulkDataAsync(string[] variables)
    {
         
        using (var ms = new MemoryStream())
        {
         
            foreach (var var in variables)
            {
         
                var handle = await _adsClient.CreateVariableHandleAsync(var);
                var data = await _adsClient.ReadAnyAsync(handle, typeof(byte[]));
                ms.Write(data, 0, data.Length);
                _adsClient.DeleteVariableHandle(handle);
            }
            return ms.ToArray();
        }
    }
    
  3. 数据压缩

    public byte[] CompressData(byte[] rawData)
    {
         
        using (var memoryStream = new MemoryStream())
        {
         
            using (var gzipStream = new GZipStream(memoryStream, CompressionMode.Compress))
            {
         
                gzipStream.Write(rawData, 0, rawData.Length);
            }
            return memoryStream.ToArray();
        }
    }
    

七、测试用例

1. 基本功能测试

测试项 输入值 预期结果
温度读取 显示实时温度值
启动指令写入 true PLC启动状态变为运行
报警触发 温度>80℃ 弹出警告并记录日志

2. 压力测试

// 模拟高频数据采集
public async Task StressTestAsync()
{
   
    var stopwatch = Stopwatch.StartNew();
    for (int i = 0; i < 1000; i++)
    {
   
        await _plcService.ReadVariableAsync<double>("MAIN.Temperature");
    }
    stopwatch.Stop();
    Assert.IsTrue(stopwatch.ElapsedMilliseconds < 500); // 1000次读取应在500ms内完成
}

八、扩展应用场景

  1. 多PLC协同控制

    public class PlcClusterManager
    {
         
        private readonly List<AdsCommunicationService> _plcs = new List<AdsCommunicationService>();
    
        public void AddPlc(string amsNetId)
        {
         
            var plc = new AdsCommunicationService();
            plc.ConnectAsync(amsNetId).Wait();
            _plcs.Add(plc);
        }
    }
    
  2. OPC UA集成

    public class OpcUaGateway
    {
         
        private readonly Opc.Ua.Client
    
相关文章
|
2天前
|
人工智能 JavaScript Linux
【Claude Code 全攻略】终端AI编程助手从入门到进阶(2026最新版)
Claude Code是Anthropic推出的终端原生AI编程助手,支持40+语言、200k超长上下文,无需切换IDE即可实现代码生成、调试、项目导航与自动化任务。本文详解其安装配置、四大核心功能及进阶技巧,助你全面提升开发效率,搭配GitHub Copilot使用更佳。
|
3天前
|
存储 人工智能 自然语言处理
OpenSpec技术规范+实例应用
OpenSpec 是面向 AI 智能体的轻量级规范驱动开发框架,通过“提案-审查-实施-归档”工作流,解决 AI 编程中的需求偏移与不可预测性问题。它以机器可读的规范为“单一真相源”,将模糊提示转化为可落地的工程实践,助力开发者高效构建稳定、可审计的生产级系统,实现从“凭感觉聊天”到“按规范开发”的跃迁。
676 11
|
8天前
|
存储 JavaScript 前端开发
JavaScript基础
本节讲解JavaScript基础核心知识:涵盖值类型与引用类型区别、typeof检测类型及局限性、===与==差异及应用场景、内置函数与对象、原型链五规则、属性查找机制、instanceof原理,以及this指向和箭头函数中this的绑定时机。重点突出类型判断、原型继承与this机制,助力深入理解JS面向对象机制。(238字)
|
7天前
|
云安全 人工智能 安全
阿里云2026云上安全健康体检正式开启
新年启程,来为云上环境做一次“深度体检”
1624 6
|
3天前
|
消息中间件 人工智能 Kubernetes
阿里云云原生应用平台岗位急招,加入我们,打造 AI 最强基础设施
云原生应用平台作为中国最大云计算公司的基石,现全面转向 AI,打造 AI 时代最强基础设施。寻找热爱技术、具备工程极致追求的架构师、极客与算法专家,共同重构计算、定义未来。杭州、北京、深圳、上海热招中,让我们一起在云端,重构 AI 的未来。
|
5天前
|
IDE 开发工具 C语言
【2026最新】VS2026下载安装使用保姆级教程(附安装包+图文步骤)
Visual Studio 2026是微软推出的最新Windows专属IDE,启动更快、内存占用更低,支持C++、Python等开发。推荐免费的Community版,安装简便,适合初学者与个人开发者使用。
713 11
|
8天前
|
缓存 算法 关系型数据库
深入浅出分布式 ID 生成方案:从原理到业界主流实现
本文深入探讨分布式ID的生成原理与主流解决方案,解析百度UidGenerator、滴滴TinyID及美团Leaf的核心设计,涵盖Snowflake算法、号段模式与双Buffer优化,助你掌握高并发下全局唯一ID的实现精髓。
376 160
|
8天前
|
人工智能 自然语言处理 API
n8n:流程自动化、智能化利器
流程自动化助你在重复的业务流程中节省时间,可通过自然语言直接创建工作流啦。
467 8
n8n:流程自动化、智能化利器
|
6天前
|
人工智能 Shell 开发工具
Claude Code 2.1.2超详细更新说明,小白也能10分钟上手
Claude Code 2.1.x重磅更新:Shift+Enter换行、Esc+Esc撤销、Ctrl+B后台运行,Skills技能系统全面升级,支持多语言、通配符权限与动态MCP检测,性能提升50%,迭代速度惊人,开发者效率暴涨!
Claude Code 2.1.2超详细更新说明,小白也能10分钟上手