C#与西门子S7-1200通讯实例

一、通讯方式概述

C#与西门子S7-1200 PLC通讯主要有两种主流方式:

方式 协议 特点 适用场景
S7NetPlus S7协议(Profinet) 直接、高效、实时性强 局域网内高速数据交换
OPC UA OPC UA协议 跨平台、安全、标准化 跨网络、多系统集成

二、S7NetPlus完整实例(推荐)

1. 环境准备

NuGet包安装:

bash 复制代码
Install-Package S7netplus

或通过Visual Studio的NuGet包管理器搜索安装S7netplus(最新版本0.20.0+)。

2. 核心连接类

csharp 复制代码
using S7.Net;
using System;
using System.Threading.Tasks;

public class S71200PlcClient : IDisposable
{
    private Plc _plc;
    private bool _isConnected = false;
    
    // PLC连接参数
    public string IpAddress { get; set; } = "192.168.0.1";
    public short Rack { get; set; } = 0;      // S7-1200固定为0
    public short Slot { get; set; } = 1;      // S7-1200固定为1
    public int Timeout { get; set; } = 5000;  // 5秒超时
    
    /// <summary>
    /// 连接PLC
    /// </summary>
    public async Task<bool> ConnectAsync()
    {
        try
        {
            // 创建PLC实例
            _plc = new Plc(CpuType.S71200, IpAddress, Rack, Slot);
            _plc.ReadTimeout = Timeout;
            _plc.WriteTimeout = Timeout;
            
            // 异步连接
            await Task.Run(() => _plc.Open());
            
            _isConnected = _plc.IsConnected;
            return _isConnected;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"连接失败: {ex.Message}");
            return false;
        }
    }
    
    /// <summary>
    /// 断开连接
    /// </summary>
    public void Disconnect()
    {
        try
        {
            if (_plc != null && _plc.IsConnected)
            {
                _plc.Close();
            }
            _isConnected = false;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"断开连接失败: {ex.Message}");
        }
    }
    
    /// <summary>
    /// 读取PLC状态
    /// </summary>
    public string GetPlcStatus()
    {
        if (!_isConnected) return "未连接";
        
        try
        {
            byte statusCode = _plc.ReadStatus();
            return statusCode switch
            {
                0x08 => "运行中(RUN)",
                0x04 => "停止(STOP)",
                0x05 => "启动中(STARTUP)",
                _ => $"未知状态: {statusCode:X2}"
            };
        }
        catch (Exception ex)
        {
            return $"读取状态失败: {ex.Message}";
        }
    }
    
    /// <summary>
    /// 读取单个位(如I0.0)
    /// </summary>
    public bool ReadBit(string address)
    {
        if (!_isConnected) throw new InvalidOperationException("PLC未连接");
        
        try
        {
            return (bool)_plc.Read(address);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"读取位失败 {address}: {ex.Message}");
            throw;
        }
    }
    
    /// <summary>
    /// 读取整数(如DB1.DBW2)
    /// </summary>
    public short ReadInt(string address)
    {
        if (!_isConnected) throw new InvalidOperationException("PLC未连接");
        
        try
        {
            return (short)_plc.Read(address);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"读取整数失败 {address}: {ex.Message}");
            throw;
        }
    }
    
    /// <summary>
    /// 读取浮点数(如DB1.DBD4)
    /// </summary>
    public float ReadReal(string address)
    {
        if (!_isConnected) throw new InvalidOperationException("PLC未连接");
        
        try
        {
            return (float)_plc.Read(address);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"读取浮点数失败 {address}: {ex.Message}");
            throw;
        }
    }
    
    /// <summary>
    /// 写入单个位(如Q0.0)
    /// </summary>
    public void WriteBit(string address, bool value)
    {
        if (!_isConnected) throw new InvalidOperationException("PLC未连接");
        
        try
        {
            _plc.Write(address, value);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"写入位失败 {address}: {ex.Message}");
            throw;
        }
    }
    
    /// <summary>
    /// 写入整数(如MW10)
    /// </summary>
    public void WriteInt(string address, short value)
    {
        if (!_isConnected) throw new InvalidOperationException("PLC未连接");
        
        try
        {
            _plc.Write(address, value);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"写入整数失败 {address}: {ex.Message}");
            throw;
        }
    }
    
    /// <summary>
    /// 写入浮点数(如DB1.DBD0)
    /// </summary>
    public void WriteReal(string address, float value)
    {
        if (!_isConnected) throw new InvalidOperationException("PLC未连接");
        
        try
        {
            _plc.Write(address, value);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"写入浮点数失败 {address}: {ex.Message}");
            throw;
        }
    }
    
    /// <summary>
    /// 批量读取多个变量
    /// </summary>
    public object[] ReadMultiple(params string[] addresses)
    {
        if (!_isConnected) throw new InvalidOperationException("PLC未连接");
        
        try
        {
            var results = new object[addresses.Length];
            for (int i = 0; i < addresses.Length; i++)
            {
                results[i] = _plc.Read(addresses[i]);
            }
            return results;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"批量读取失败: {ex.Message}");
            throw;
        }
    }
    
    /// <summary>
    /// 读取字节数组(如DB1.DBB0开始的10个字节)
    /// </summary>
    public byte[] ReadBytes(DataType dataType, int db, int startByte, int count)
    {
        if (!_isConnected) throw new InvalidOperationException("PLC未连接");
        
        try
        {
            return _plc.ReadBytes(dataType, db, startByte, count);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"读取字节数组失败: {ex.Message}");
            throw;
        }
    }
    
    /// <summary>
    /// 写入字节数组
    /// </summary>
    public void WriteBytes(DataType dataType, int db, int startByte, byte[] data)
    {
        if (!_isConnected) throw new InvalidOperationException("PLC未连接");
        
        try
        {
            _plc.WriteBytes(dataType, db, startByte, data);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"写入字节数组失败: {ex.Message}");
            throw;
        }
    }
    
    public void Dispose()
    {
        Disconnect();
        _plc?.Dispose();
    }
}

3. 使用示例

csharp 复制代码
class Program
{
    static async Task Main(string[] args)
    {
        using var plcClient = new S71200PlcClient
        {
            IpAddress = "192.168.0.100"  // 替换为实际PLC IP
        };
        
        try
        {
            // 连接PLC
            bool connected = await plcClient.ConnectAsync();
            if (!connected)
            {
                Console.WriteLine("连接PLC失败");
                return;
            }
            
            Console.WriteLine("PLC连接成功");
            Console.WriteLine($"PLC状态: {plcClient.GetPlcStatus()}");
            
            // 读取输入位 I0.0
            bool input0 = plcClient.ReadBit("I0.0");
            Console.WriteLine($"I0.0 = {input0}");
            
            // 读取输出位 Q0.0
            bool output0 = plcClient.ReadBit("Q0.0");
            Console.WriteLine($"Q0.0 = {output0}");
            
            // 读取中间位 M0.0
            bool memory0 = plcClient.ReadBit("M0.0");
            Console.WriteLine($"M0.0 = {memory0}");
            
            // 读取DB块数据
            float temperature = plcClient.ReadReal("DB1.DBD0");
            Console.WriteLine($"温度 = {temperature}°C");
            
            short speed = plcClient.ReadInt("DB1.DBW2");
            Console.WriteLine($"速度 = {speed} RPM");
            
            // 写入数据
            plcClient.WriteBit("Q0.1", true);  // 置位Q0.1
            plcClient.WriteReal("DB1.DBD4", 25.5f);  // 写入温度设定值
            
            // 批量读取
            var batchResults = plcClient.ReadMultiple(
                "I0.0", "I0.1", "M0.0", "DB1.DBD0", "DB1.DBW2");
            
            Console.WriteLine("批量读取结果:");
            for (int i = 0; i < batchResults.Length; i++)
            {
                Console.WriteLine($"  地址{i}: {batchResults[i]}");
            }
            
            // 读取字节数组(DB1从字节0开始的10个字节)
            byte[] rawData = plcClient.ReadBytes(DataType.DataBlock, 1, 0, 10);
            Console.WriteLine($"原始数据: {BitConverter.ToString(rawData)}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"操作失败: {ex.Message}");
        }
        finally
        {
            Console.WriteLine("程序结束");
        }
    }
}

4. WinForms界面示例

csharp 复制代码
using System;
using System.Windows.Forms;
using S7.Net;

namespace S71200Monitor
{
    public partial class MainForm : Form
    {
        private Plc _plc;
        private Timer _pollTimer;
        
        public MainForm()
        {
            InitializeComponent();
            InitializeControls();
        }
        
        private void InitializeControls()
        {
            // 连接参数
            txtIpAddress.Text = "192.168.0.100";
            txtRack.Text = "0";
            txtSlot.Text = "1";
            
            // 定时器设置(100ms轮询)
            _pollTimer = new Timer { Interval = 100 };
            _pollTimer.Tick += PollTimer_Tick;
        }
        
        private async void btnConnect_Click(object sender, EventArgs e)
        {
            try
            {
                CpuType cpuType = CpuType.S71200;
                string ipAddress = txtIpAddress.Text;
                short rack = short.Parse(txtRack.Text);
                short slot = short.Parse(txtSlot.Text);
                
                _plc = new Plc(cpuType, ipAddress, rack, slot);
                await Task.Run(() => _plc.Open());
                
                if (_plc.IsConnected)
                {
                    btnConnect.Enabled = false;
                    btnDisconnect.Enabled = true;
                    _pollTimer.Start();
                    
                    AddLog($"成功连接到PLC: {ipAddress}");
                    AddLog($"PLC状态: {GetPlcStatusString()}");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"连接失败: {ex.Message}", "错误", 
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        
        private void btnDisconnect_Click(object sender, EventArgs e)
        {
            try
            {
                if (_plc != null && _plc.IsConnected)
                {
                    _plc.Close();
                    _pollTimer.Stop();
                    
                    btnConnect.Enabled = true;
                    btnDisconnect.Enabled = false;
                    
                    AddLog("已断开PLC连接");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"断开失败: {ex.Message}", "错误", 
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        
        private void PollTimer_Tick(object sender, EventArgs e)
        {
            if (_plc == null || !_plc.IsConnected) return;
            
            try
            {
                // 实时读取数据并更新UI
                bool motorRunning = (bool)_plc.Read("M0.0");
                lblMotorStatus.Text = motorRunning ? "运行中" : "停止";
                lblMotorStatus.BackColor = motorRunning ? Color.Green : Color.Red;
                
                float temperature = (float)_plc.Read("DB1.DBD0");
                lblTemperature.Text = $"{temperature:F1}°C";
                
                short speed = (short)_plc.Read("DB1.DBW2");
                lblSpeed.Text = $"{speed} RPM";
                
                // 更新状态栏
                toolStripStatusLabel.Text = $"最后更新: {DateTime.Now:HH:mm:ss}";
            }
            catch (Exception ex)
            {
                AddLog($"轮询错误: {ex.Message}");
            }
        }
        
        private void btnWrite_Click(object sender, EventArgs e)
        {
            if (_plc == null || !_plc.IsConnected)
            {
                MessageBox.Show("请先连接PLC", "提示", 
                    MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }
            
            try
            {
                // 写入设定值
                float setValue = float.Parse(txtSetValue.Text);
                _plc.Write("DB1.DBD4", setValue);
                
                AddLog($"已写入设定值: {setValue}");
            }
            catch (Exception ex)
            {
                MessageBox.Show($"写入失败: {ex.Message}", "错误", 
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        
        private string GetPlcStatusString()
        {
            try
            {
                byte statusCode = _plc.ReadStatus();
                return statusCode switch
                {
                    0x08 => "运行中(RUN)",
                    0x04 => "停止(STOP)",
                    0x05 => "启动中(STARTUP)",
                    _ => $"未知状态: {statusCode:X2}"
                };
            }
            catch
            {
                return "状态未知";
            }
        }
        
        private void AddLog(string message)
        {
            if (txtLog.InvokeRequired)
            {
                txtLog.Invoke(new Action<string>(AddLog), message);
                return;
            }
            
            txtLog.AppendText($"[{DateTime.Now:HH:mm:ss}] {message}\r\n");
            txtLog.ScrollToCaret();
        }
        
        // 控件声明(设计器生成)
        private TextBox txtIpAddress, txtRack, txtSlot, txtSetValue, txtLog;
        private Button btnConnect, btnDisconnect, btnWrite;
        private Label lblMotorStatus, lblTemperature, lblSpeed;
        private StatusStrip statusStrip;
        private ToolStripStatusLabel toolStripStatusLabel;
    }
}

5. PLC地址格式说明

地址类型 格式 示例 说明
输入位 I字节. I0.0 输入字节0的位0
输出位 Q字节. Q0.1 输出字节0的位1
中间位 M字节. M10.5 中间字节10的位5
数据块位 DB块号.DBX字节. DB1.DBX0.0 DB1字节0的位0
数据块字 DB块号.DBW字节 DB1.DBW2 DB1从字节2开始的字
数据块双字 DB块号.DBD字节 DB1.DBD4 DB1从字节4开始的双字
定时器 T编号 T0 定时器0
计数器 C编号 C1 计数器1

三、OPC UA通讯方式

1. PLC端配置(TIA Portal)

  1. 启用OPC UA服务器

    • 双击PLC设备 → 属性 → OPC UA
    • 勾选"启用OPC UA服务器"
    • 设置服务器名称和端点URL(如opc.tcp://192.168.0.1:4840
  2. 变量暴露

    lad 复制代码
    // 在OB1中定义变量并设置为OPC UA可见
    Network 1:
    LD  "Temp_Sensor"
    T   "Device1".Temp  // Real型温度变量
    
    // OPC UA配置
    CALL "OPC UA Variable Config"
    IN := "Device1".Temp
    VISIBLE := TRUE
    BROWSENAME := "Device1_Temp"

2. C# OPC UA客户端

csharp 复制代码
using Opc.Ua;
using Opc.Ua.Client;
using System;
using System.Threading.Tasks;

public class S71200OpcClient
{
    private Session _session;
    private string _endpointUrl;
    
    public S71200OpcClient(string endpointUrl)
    {
        _endpointUrl = endpointUrl;
    }
    
    public async Task ConnectAsync()
    {
        try
        {
            // 选择端点
            var endpoints = CoreClientUtils.SelectEndpoint(_endpointUrl, false);
            
            // 创建配置
            var config = new ApplicationConfiguration
            {
                ApplicationName = "S7-1200 OPC UA Client",
                ApplicationType = ApplicationType.Client,
                SecurityConfiguration = new SecurityConfiguration
                {
                    ApplicationCertificate = new CertificateIdentifier(),
                    AutoAcceptUntrustedCertificates = true
                }
            };
            
            // 建立会话
            _session = await Session.Create(
                config,
                new ConfiguredEndpoint(null, endpoints[0], EndpointConfiguration.Create()),
                false,
                false,
                config.ApplicationName,
                60000,
                null,
                null);
            
            Console.WriteLine($"已连接到OPC UA服务器: {_endpointUrl}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"连接失败: {ex.Message}");
            throw;
        }
    }
    
    public async Task<object> ReadNodeAsync(string nodeId)
    {
        if (_session == null || !_session.Connected)
            throw new InvalidOperationException("会话未连接");
        
        try
        {
            var readValue = new ReadValueId
            {
                NodeId = new NodeId(nodeId),
                AttributeId = Attributes.Value
            };
            
            var request = new ReadRequest
            {
                NodesToRead = new ReadValueIdCollection { readValue }
            };
            
            var response = await _session.ReadAsync(request);
            
            if (response.Results.Count > 0 && 
                StatusCode.IsGood(response.Results[0].StatusCode))
            {
                return response.Results[0].Value;
            }
            
            throw new Exception($"读取失败: {response.Results[0].StatusCode}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"读取节点失败 {nodeId}: {ex.Message}");
            throw;
        }
    }
    
    public async Task WriteNodeAsync(string nodeId, object value)
    {
        if (_session == null || !_session.Connected)
            throw new InvalidOperationException("会话未连接");
        
        try
        {
            var writeValue = new WriteValue
            {
                NodeId = new NodeId(nodeId),
                AttributeId = Attributes.Value,
                Value = new DataValue(new Variant(value))
            };
            
            var request = new WriteRequest
            {
                NodesToWrite = new WriteValueCollection { writeValue }
            };
            
            var response = await _session.WriteAsync(request);
            
            if (response.Results.Count > 0 && 
                !StatusCode.IsGood(response.Results[0]))
            {
                throw new Exception($"写入失败: {response.Results[0]}");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"写入节点失败 {nodeId}: {ex.Message}");
            throw;
        }
    }
    
    public void Disconnect()
    {
        _session?.Close();
        _session?.Dispose();
    }
}

3. OPC UA使用示例

csharp 复制代码
class Program
{
    static async Task Main(string[] args)
    {
        var opcClient = new S71200OpcClient("opc.tcp://192.168.0.1:4840");
        
        try
        {
            // 连接
            await opcClient.ConnectAsync();
            
            // 读取温度变量(节点ID需根据实际配置)
            var temperature = await opcClient.ReadNodeAsync("ns=2;s=Device1_Temp");
            Console.WriteLine($"温度: {temperature}°C");
            
            // 写入设定值
            await opcClient.WriteNodeAsync("ns=2;s=Device1_Setpoint", 25.5);
            Console.WriteLine("设定值写入成功");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"操作失败: {ex.Message}");
        }
        finally
        {
            opcClient.Disconnect();
        }
    }
}

四、两种方式对比与选择建议

特性 S7NetPlus OPC UA
性能 ⭐⭐⭐⭐⭐(毫秒级) ⭐⭐⭐⭐(10-100ms)
实时性 极高
安全性 基础(IP过滤) ⭐⭐⭐⭐⭐(证书、加密)
跨平台 仅Windows/.NET ⭐⭐⭐⭐⭐(全平台)
配置复杂度 简单 中等
标准化 西门子专有 国际标准(IEC 62541)
网络要求 局域网 局域网/广域网

选择建议:

  • S7NetPlus:适合局域网内高速数据采集、实时控制、单系统集成
  • OPC UA:适合跨网络、多系统集成、高安全性要求、长期维护项目

参考代码 C#与西门子1200通讯实例 www.youwenfan.com/contentcst/38493.html

五、常见问题与解决方案

1. 连接失败问题

csharp 复制代码
// 常见错误代码及解决方案
public enum ConnectionError
{
    NoError = 0,
    IpAddressInvalid = 1,      // IP地址无效
    PortBlocked = 2,           // 端口被防火墙阻挡
    PlcNotReachable = 3,       // PLC不可达
    WrongCpuType = 4,          // CPU类型错误
    AccessDenied = 5,          // 访问被拒绝
    Timeout = 6                // 连接超时
}

public static string GetErrorSolution(ConnectionError error)
{
    return error switch
    {
        ConnectionError.IpAddressInvalid => 
            "检查IP地址格式,确保与PLC IP一致",
        ConnectionError.PortBlocked => 
            "检查防火墙设置,开放102端口(S7协议默认端口)",
        ConnectionError.PlcNotReachable => 
            "1. 使用ping测试网络连通性\n2. 检查网线连接\n3. 确认PLC已上电",
        ConnectionError.WrongCpuType => 
            "确认PLC型号,S7-1200使用CpuType.S71200",
        ConnectionError.AccessDenied => 
            "检查PLC访问权限设置,可能需要密码",
        ConnectionError.Timeout => 
            "1. 增加超时时间\n2. 检查网络延迟\n3. 减少同时连接数",
        _ => "未知错误,请检查日志"
    };
}

2. 数据读写异常处理

csharp 复制代码
public class PlcDataManager
{
    private Plc _plc;
    private int _retryCount = 3;
    private int _retryDelay = 100; // ms
    
    /// <summary>
    /// 带重试机制的读取
    /// </summary>
    public async Task<object> ReadWithRetryAsync(string address)
    {
        for (int i = 0; i < _retryCount; i++)
        {
            try
            {
                return _plc.Read(address);
            }
            catch (PlcException ex) when (i < _retryCount - 1)
            {
                Console.WriteLine($"第{i+1}次读取失败,{_retryDelay}ms后重试: {ex.Message}");
                await Task.Delay(_retryDelay);
            }
        }
        throw new Exception($"读取失败,已重试{_retryCount}次");
    }
    
    /// <summary>
    /// 心跳检测
    /// </summary>
    public async Task StartHeartbeatAsync(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            try
            {
                // 读取PLC状态作为心跳
                byte status = _plc.ReadStatus();
                Console.WriteLine($"心跳正常,PLC状态: {status:X2}");
                
                await Task.Delay(5000, cancellationToken); // 5秒一次
            }
            catch (Exception ex)
            {
                Console.WriteLine($"心跳检测失败: {ex.Message}");
                // 触发重连逻辑
                await ReconnectAsync();
            }
        }
    }
    
    private async Task ReconnectAsync()
    {
        Console.WriteLine("尝试重新连接PLC...");
        
        for (int i = 0; i < 5; i++) // 最多重试5次
        {
            try
            {
                if (_plc.IsConnected)
                    _plc.Close();
                
                await Task.Run(() => _plc.Open());
                
                if (_plc.IsConnected)
                {
                    Console.WriteLine("PLC重连成功");
                    return;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"第{i+1}次重连失败: {ex.Message}");
                await Task.Delay(2000 * (i + 1)); // 指数退避
            }
        }
        
        throw new Exception("PLC重连失败,请检查网络连接");
    }
}

3. 性能优化建议

csharp 复制代码
public class HighPerformancePlcClient
{
    private Plc _plc;
    private ConcurrentQueue<PlcRequest> _requestQueue;
    private SemaphoreSlim _semaphore;
    
    /// <summary>
    /// 批量读取优化
    /// </summary>
    public async Task<Dictionary<string, object>> BatchReadAsync(
        IEnumerable<string> addresses, int batchSize = 20)
    {
        var results = new ConcurrentDictionary<string, object>();
        var addressList = addresses.ToList();
        
        // 分批处理
        var batches = addressList
            .Select((addr, index) => new { addr, index })
            .GroupBy(x => x.index / batchSize)
            .Select(g => g.Select(x => x.addr).ToList())
            .ToList();
        
        // 并行处理批次
        await Parallel.ForEachAsync(batches, async (batch, ct) =>
        {
            foreach (var address in batch)
            {
                try
                {
                    var value = _plc.Read(address);
                    results[address] = value;
                }
                catch (Exception ex)
                {
                    results[address] = new { Error = ex.Message };
                }
            }
        });
        
        return new Dictionary<string, object>(results);
    }
    
    /// <summary>
    /// 异步写入队列
    /// </summary>
    public async Task EnqueueWriteAsync(string address, object value)
    {
        var request = new PlcRequest
        {
            Address = address,
            Value = value,
            Timestamp = DateTime.Now
        };
        
        _requestQueue.Enqueue(request);
        
        // 触发处理
        _ = Task.Run(async () =>
        {
            await _semaphore.WaitAsync();
            try
            {
                await ProcessWriteQueueAsync();
            }
            finally
            {
                _semaphore.Release();
            }
        });
    }
}

六、实际项目部署建议

1. 配置文件示例(appsettings.json)

json 复制代码
{
  "PlcSettings": {
    "IpAddress": "192.168.0.100",
    "Rack": 0,
    "Slot": 1,
    "Timeout": 5000,
    "RetryCount": 3,
    "HeartbeatInterval": 5000,
    "DataPoints": [
      {
        "Name": "Temperature",
        "Address": "DB1.DBD0",
        "DataType": "Real",
        "PollingInterval": 1000
      },
      {
        "Name": "MotorSpeed",
        "Address": "DB1.DBW2",
        "DataType": "Int",
        "PollingInterval": 500
      }
    ]
  },
  "Logging": {
    "Level": "Information",
    "FilePath": "logs/plc_communication.log"
  }
}

2. 依赖注入配置

csharp 复制代码
// Startup.cs 或 Program.cs
public static IServiceCollection AddPlcServices(this IServiceCollection services, IConfiguration configuration)
{
    var plcSettings = configuration.GetSection("PlcSettings").Get<PlcSettings>();
    
    services.AddSingleton(plcSettings);
    
    services.AddSingleton<IPlcClient>(provider =>
    {
        var settings = provider.GetRequiredService<PlcSettings>();
        var logger = provider.GetRequiredService<ILogger<PlcClient>>();
        
        return new PlcClient(settings, logger);
    });
    
    services.AddHostedService<PlcBackgroundService>();
    
    return services;
}

3. 监控与诊断

csharp 复制代码
public class PlcDiagnosticService
{
    private readonly IPlcClient _plcClient;
    private readonly ILogger<PlcDiagnosticService> _logger;
    private readonly PerformanceCounter _readCounter;
    private readonly PerformanceCounter _writeCounter;
    
    public PlcDiagnosticService(IPlcClient plcClient, ILogger<PlcDiagnosticService> logger)
    {
        _plcClient = plcClient;
        _logger = logger;
        
        // 性能计数器
        _readCounter = new PerformanceCounter(
            "PlcCommunication", "ReadOperations", false);
        _writeCounter = new PerformanceCounter(
            "PlcCommunication", "WriteOperations", false);
    }
    
    public async Task<DiagnosticReport> GetDiagnosticReportAsync()
    {
        var report = new DiagnosticReport
        {
            Timestamp = DateTime.Now,
            ConnectionStatus = _plcClient.IsConnected,
            PlcStatus = await _plcClient.GetStatusAsync(),
            PerformanceMetrics = new PerformanceMetrics
            {
                ReadOperationsPerSecond = _readCounter.NextValue(),
                WriteOperationsPerSecond = _writeCounter.NextValue(),
                AverageResponseTime = CalculateAverageResponseTime(),
                ErrorRate = CalculateErrorRate()
            },
            RecentErrors = GetRecentErrors(50)
        };
        
        return report;
    }
    
    public void LogOperation(string operation, TimeSpan duration, bool success)
    {
        _logger.LogInformation(
            "PLC操作: {Operation}, 耗时: {Duration}ms, 状态: {Status}",
            operation, duration.TotalMilliseconds, 
            success ? "成功" : "失败");
        
        if (!success)
        {
            _logger.LogWarning("PLC操作失败: {Operation}", operation);
        }
    }
}

七、资源与参考

  1. 官方资源

  2. 学习资料

    • 《C#上位机对接西门子PLC完整实战指南》
    • 《深入解析S7NetPlus:工业环境下的.NET西门子PLC通信实战指南》
  3. 调试工具

    • Wireshark(网络抓包分析)
    • S7-PLCSIM Advanced(PLC仿真)
    • OPC UA Expert(OPC UA客户端测试工具)
相关推荐
gc_229917 分钟前
C#测试调用Net.Codecrete.QrCodeGenerator库生成二维码的基本用法
c#·二维码·qrcodegenerator
techdashen26 分钟前
Rust 中的小字符串:smol_str 与 smartstring 的对决
开发语言·后端·rust
devilnumber32 分钟前
java自定义事件处理器极简版:「外卖点餐」场景
java·开发语言
小何code36 分钟前
C语言【初阶】第1节,初识C语言
c语言·开发语言
代码小书生39 分钟前
getpass,一个安全输入的 Python 库!
开发语言·python·安全
莫陌尛.1 小时前
Fuzzy C-Mean Clustering (FCM)
c语言·开发语言
YOU OU1 小时前
案例综合练习-博客系统
java·开发语言
其实防守也摸鱼1 小时前
告别单个变量,用列表和字典批量管理你的 Python 数据
开发语言·网络·软件测试·python·web安全·数据结构,编程教程
瑞雪兆丰年兮1 小时前
[从0开始学Java|第十八、十九天]API(常见API&对象克隆&正则表达式)
java·开发语言
KobeSacre1 小时前
JVM G1 垃圾回收器
java·开发语言·jvm