一、通讯方式概述
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)
-
启用OPC UA服务器:
- 双击PLC设备 → 属性 → OPC UA
- 勾选"启用OPC UA服务器"
- 设置服务器名称和端点URL(如
opc.tcp://192.168.0.1:4840)
-
变量暴露:
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);
}
}
}
七、资源与参考
-
官方资源:
- S7NetPlus GitHub: https://github.com/killnine/s7netplus
- NuGet包: https://www.nuget.org/packages/S7netplus/
- 西门子官方文档: https://support.industry.siemens.com
-
学习资料:
- 《C#上位机对接西门子PLC完整实战指南》
- 《深入解析S7NetPlus:工业环境下的.NET西门子PLC通信实战指南》
-
调试工具:
- Wireshark(网络抓包分析)
- S7-PLCSIM Advanced(PLC仿真)
- OPC UA Expert(OPC UA客户端测试工具)