WinM.S7 3.2.0
WinM.S7
| 3.0.x版本目前与Connection.S7保持一致,只是单独提出仓库,优化部分代码,并未做破坏性改动
用于西门子S7系列PLC通讯的.NET库,提供简单易用的API接口,支持数据读写、状态监控和自动重连等功能。
功能特性
- 简单易用的API:提供直观的接口进行PLC连接和数据交互
- 自动重连:在网络中断时自动尝试重新连接,支持自定义重连策略
- 状态监控:实时监控PLC连接和运行状态,提供丰富的状态事件
- 数据块管理:高效管理和缓存数据块实例,支持并发访问
- 类型安全:支持强类型数据读写,自动类型转换和验证
- 异步操作:全面支持异步通信方法,提升应用程序响应性
- 事件驱动:提供丰富的事件通知机制,包括写入成功/失败事件
- 线程安全:内部实现保证多线程环境下的安全性
- 节点管理:支持S7PlcNode节点模式,简化数据点管理
- 结构体支持:支持S7PlcNodeStruct,实现批量高效读取
- 配置管理:支持JSON配置文件,便于部署和维护
- 缓存机制:内置PLC实例和配置缓存,提升性能
安装
NuGet包
dotnet add package WinM.S7
项目引用
<PackageReference Include="WinM.S7" Version="2.1.1" />
快速开始
基本连接
using WinM.S7;
using S7.Net;
// 创建PLC实例
var plc = new S7Plc(CpuType.S71500, "192.168.0.1", rack: 0, slot: 1);
// 启用自动重连
plc.UseAutoReconnection(enable: true, reconnectionDelay: TimeSpan.FromSeconds(2));
// 连接PLC
await plc.ConnectAsync();
// 检查连接状态
if (plc.IsConnected)
{
Console.WriteLine("PLC已连接");
}
// 使用完毕后释放资源
plc.Dispose();
读写数据
// 读取数据
var value = plc.DB(1).ReadInt("0"); // 从DB1.DBW0读取一个Int值
var realValue = plc.DB(1).ReadFloat("4"); // 从DB1.DBD4读取一个Real值
var stringValue = plc.DB(1).ReadString(8, 20); // 从DB1.DBB8读取一个长度为20的字符串
// 写入数据
plc.DB(1).WriteInt("0", 100); // 向DB1.DBW0写入Int值100
plc.DB(1).WriteFloat("4", 123.45f); // 向DB1.DBD4写入Real值123.45
plc.DB(1).WriteString(8, "Hello S7"); // 向DB1.DBB8写入字符串"Hello S7"
地址字符串读写 (KepServer 风格)
支持类似 KepServer 的地址格式,可以直接使用地址字符串进行读写操作。
支持的地址格式
| 地址格式 | 说明 | 数据类型 |
|---|---|---|
DB1.DBX0.0 |
DB区域位地址 | bool |
DB1.DBB0 |
DB区域字节 | byte |
DB1.DBW0 |
DB区域字 (16位) | short/ushort |
DB1.DBD0 |
DB区域双字 (32位) | int/uint/float |
DB1.STRING0.50 |
S7字符串 (最大长度50) | string |
DB1.STRING0 |
S7字符串 (默认长度254) | string |
DB1.WSTRING0.50 |
S7宽字符串 | string |
DB1.CHAR0.50 |
字符数组 | string |
DB1.DTL0 |
日期时间 | DateTime |
I0.0 |
输入区位地址 | bool |
IB0 |
输入区字节 | byte |
IW0 |
输入区字 | ushort |
ID0 |
输入区双字 | uint |
Q0.0 |
输出区位地址 | bool |
M0.0 |
内存区位地址 | bool |
MB0 |
内存区字节 | byte |
读取示例
// 基本读取 (返回 object)
object value = plc.Read("DB1.DBW0");
// 泛型读取 - 指定返回类型
bool bitValue = plc.Read<bool>("DB1.DBX0.0"); // 读取位
short intValue = plc.Read<short>("DB1.DBW0"); // 读取 INT
ushort uintValue = plc.Read<ushort>("DB1.DBW0"); // 读取 UINT
int dintValue = plc.Read<int>("DB1.DBD0"); // 读取 DINT
uint udintValue = plc.Read<uint>("DB1.DBD0"); // 读取 UDINT
float realValue = plc.Read<float>("DB1.DBD0"); // 读取 REAL
string strValue = plc.Read<string>("DB1.STRING0.50"); // 读取字符串
// 便捷方法
bool b = plc.ReadBool("DB1.DBX0.0");
short s = plc.ReadShort("DB1.DBW0");
int i = plc.ReadInt("DB1.DBD0");
float f = plc.ReadFloat("DB1.DBD0");
string str = plc.ReadString("DB1.STRING0.50");
// 异步读取
float asyncValue = await plc.ReadAsync<float>("DB1.DBD0");
// I/Q/M 区域读取
bool inputBit = plc.Read<bool>("I0.0"); // 输入区位
byte inputByte = plc.Read<byte>("IB0"); // 输入区字节
ushort inputWord = plc.Read<ushort>("IW0"); // 输入区字
bool outputBit = plc.Read<bool>("Q0.0"); // 输出区位
bool memBit = plc.Read<bool>("M0.0"); // 内存区位
写入示例
// 基本写入
plc.Write("DB1.DBX0.0", true); // 写入位
plc.Write("DB1.DBW0", (short)100); // 写入 INT
plc.Write("DB1.DBW0", (ushort)100); // 写入 UINT
plc.Write("DB1.DBD0", 123456); // 写入 DINT
plc.Write("DB1.DBD0", 3.14f); // 写入 REAL
plc.Write("DB1.STRING0.50", "Hello"); // 写入字符串
// 异步写入
await plc.WriteAsync("DB1.DBD0", 3.14f);
// I/Q/M 区域写入
plc.Write("Q0.0", true); // 写入输出区位
plc.Write("M0.0", false); // 写入内存区位
类型说明
- DBW (字): 16位,支持
short(INT) 和ushort(UINT) - DBD (双字): 32位,支持
int(DINT)、uint(UDINT) 和float(REAL) - 读取时使用泛型指定目标类型,如
Read<float>("DB1.DBD0")读取 REAL 值
使用节点
// 创建节点
var node = new S7PlcNode
{
Title = "温度传感器",
Type = "real",
Db = 1,
Offset = "10",
BitLength = 4
};
// 读取节点值
float temperature = plc.ReadNode<float>(node);
// 写入节点值
node.Value = 25.5f;
bool success = plc.WriteNode(node);
// 或者直接写入
success = plc.WriteNode(node, 25.5f);
// 监听节点写入事件
node.WriteSucceeded += (node, value) =>
{
Console.WriteLine($"节点 {node.Title} 写入成功,值: {value}");
};
node.WriteFailed += (node, attemptedValue, exception) =>
{
Console.WriteLine($"节点 {node.Title} 写入失败: {exception.Message}");
};
监视数据变化
// 创建节点
var node = new S7PlcNode
{
Title = "生产计数器",
Type = "int",
Db = 1,
Offset = "20"
};
// 设置监视
plc.Watch(node, value =>
{
Console.WriteLine($"计数器值变化: {value}");
});
事件处理
// 连接事件
plc.OnConnectedHandler += (sender, e) => Console.WriteLine("PLC已连接");
plc.OnDisconnectedHandler += (sender, e) => Console.WriteLine("PLC已断开");
// 状态事件
plc.OnRunningHandler += (sender, e) => Console.WriteLine("PLC运行中");
plc.OnStopedHandler += (sender, e) => Console.WriteLine("PLC已停止");
// 错误事件
plc.OnErrorHandler += (ex) => Console.WriteLine($"发生错误: {ex.Message}");
心跳和就绪状态
// 设置心跳节点
var heartbeatNode = new S7PlcNode
{
Title = "心跳",
Type = "bool",
Db = 1,
Offset = "0.0"
};
// 启用心跳
plc.UseHeartbeat(heartbeatNode, status =>
{
Console.WriteLine($"心跳状态: {status}");
});
// 设置就绪状态节点
var readyNode = new S7PlcNode
{
Title = "就绪",
Type = "bool",
Db = 1,
Offset = "0.1"
};
// 设置就绪状态
plc.UseReadyStatus(readyNode, true);
读取节点结构体 (S7PlcNodeStruct)
S7PlcNodeStruct 类允许您定义一个包含多个独立 S7PlcNode 对象的有序逻辑集合(结构体)。这些节点可以有不同的数据类型和地址。
通过 S7Plc.ReadStruct() 方法,您可以一次性读取结构体中定义的所有节点。该方法足够智能:
- 它会检测结构体内连续的、同DB、同类型的节点序列。
- 对于这样的序列,它会使用一次优化的批量读取操作。
- 不符合连续条件的节点会被单独读取。
为了利用批量读取优化,请确保在将节点添加到
S7PlcNodeStruct时,连续的节点是按其在PLC内存中的顺序添加的。
// 1. 创建 S7PlcNodeStruct 实例
var complexDeviceData = new S7PlcNodeStruct("ComplexDeviceMetrics");
// 2. 向结构体中添加 S7PlcNode 定义 (注意顺序以优化读取)
// 一组连续的Reals
complexDeviceData.AddNode(new S7PlcNode { Title = "Pressure", Db = 5, Offset = "0", Type = "Real" });
complexDeviceData.AddNode(new S7PlcNode { Title = "Temperature", Db = 5, Offset = "4", Type = "Real" });
complexDeviceData.AddNode(new S7PlcNode { Title = "FlowRate", Db = 5, Offset = "8", Type = "Real" });
// 一个单独的Bool
complexDeviceData.AddNode(new S7PlcNode { Title = "SystemEnabled", Db = 5, Offset = "12.0", Type = "Bool" });
// 另一个单独的DInt
complexDeviceData.AddNode(new S7PlcNode { Title = "CycleCounter", Db = 5, Offset = "14", Type = "DInt" });
// 3. 使用 S7Plc 实例的 ReadStruct 方法读取数据
Dictionary<string, object> allValues = plc.ReadStruct(complexDeviceData);
// 4. 访问数据
if (allValues.TryGetValue("Temperature", out object tempObj) && tempObj is float temp)
{
Console.WriteLine($"温度: {temp}");
}
if (allValues.TryGetValue("SystemEnabled", out object enabledObj) && enabledObj is bool enabled)
{
Console.WriteLine($"系统启用: {enabled}");
}
// ... 同样的方式访问其他节点
详细用法请参见 README.S7PlcNodeStruct.md。
高级用法
批量写入节点
// 创建多个节点
var nodes = new List<S7PlcNode>
{
new S7PlcNode { Title = "节点1", Type = "int", Db = 1, Offset = "0", Value = 100 },
new S7PlcNode { Title = "节点2", Type = "real", Db = 1, Offset = "2", Value = 123.45f },
new S7PlcNode { Title = "节点3", Type = "bool", Db = 1, Offset = "6.0", Value = true }
};
// 批量写入
int successCount = plc.WriteNodes(nodes);
Console.WriteLine($"成功写入 {successCount}/{nodes.Count} 个节点");
异步操作
// 异步连接
await plc.ConnectAsync(timeoutSeconds: 5);
// 异步写入
await plc.WriteNodeAsync(node, 100);
// 异步批量写入
await plc.WriteNodesAsync(nodes);
使用配置文件
// 加载配置
var config = new S7PlcConfig();
config.Load();
// 使用配置创建PLC实例
var plc = config.GetPlc();
// 连接
await plc.ConnectAsync();
// 使用配置中的节点
var node = config.GetNode("温度传感器");
if (node != null)
{
float value = plc.ReadNode<float>(node);
Console.WriteLine($"温度: {value}");
}
S7PlcNode 详细使用指南
创建不同类型的节点
// 布尔类型节点
var runningStatus = new S7PlcNode
{
Title = "设备运行状态",
Type = "bool",
Db = 1,
Offset = "0.0", // DB1.DBX0.0
Description = "设备是否正在运行"
};
// 整数类型节点
var counter = new S7PlcNode
{
Title = "生产计数器",
Type = "dint",
Db = 1,
Offset = "4", // DB1.DBD4
Description = "当前生产数量"
};
// 浮点数类型节点
var temperature = new S7PlcNode
{
Title = "炉温",
Type = "real",
Db = 1,
Offset = "8", // DB1.DBD8
Description = "炉子温度,单位:摄氏度"
};
// 字符串类型节点
var deviceName = new S7PlcNode
{
Title = "设备名称",
Type = "string",
Db = 1,
Offset = "20", // DB1.DBB20
BitLength = 50, // 最大50个字符
Description = "设备标识名称"
};
节点事件处理
var setpointNode = new S7PlcNode
{
Title = "温度设定值",
Type = "real",
Db = 1,
Offset = "100",
NeedFeedback = true
};
// 写入成功事件
setpointNode.WriteSucceeded += (node, value) =>
{
Console.WriteLine($"✓ {node.Title} 设置成功: {value}");
// 可以更新UI状态
UpdateUI($"{node.Title} 已更新");
};
// 写入失败事件
setpointNode.WriteFailed += (node, attemptedValue, exception) =>
{
Console.WriteLine($"✗ {node.Title} 设置失败: {exception.Message}");
Console.WriteLine($"尝试设置的值: {attemptedValue}");
// 可以实现重试逻辑
if (exception is TimeoutException)
{
Console.WriteLine("网络超时,将在3秒后重试...");
Task.Delay(3000).ContinueWith(_ =>
{
plc.WriteNode(node, attemptedValue);
});
}
};
// 执行写入操作
await plc.WriteNodeAsync(setpointNode, 75.5f);
批量节点操作
// 创建一组相关的节点
var processNodes = new List<S7PlcNode>
{
new S7PlcNode { Title = "进料温度", Type = "real", Db = 10, Offset = "0" },
new S7PlcNode { Title = "出料温度", Type = "real", Db = 10, Offset = "4" },
new S7PlcNode { Title = "压力值", Type = "real", Db = 10, Offset = "8" },
new S7PlcNode { Title = "流量值", Type = "real", Db = 10, Offset = "12" },
new S7PlcNode { Title = "设备状态", Type = "int", Db = 10, Offset = "16" }
};
// 为所有节点设置统一的事件处理
foreach (var node in processNodes)
{
node.WriteSucceeded += (n, v) =>
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] {n.Title}: {v}");
node.WriteFailed += (n, v, e) =>
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] {n.Title} 失败: {e.Message}");
}
// 批量读取
var results = new Dictionary<string, object>();
foreach (var node in processNodes)
{
try
{
var value = plc.ReadNode<object>(node);
results[node.Title] = value;
}
catch (Exception ex)
{
Console.WriteLine($"读取 {node.Title} 失败: {ex.Message}");
}
}
// 批量写入(设置新的目标值)
var targetValues = new Dictionary<string, object>
{
["进料温度"] = 80.0f,
["出料温度"] = 75.0f,
["压力值"] = 2.5f,
["流量值"] = 150.0f,
["设备状态"] = 1
};
foreach (var node in processNodes)
{
if (targetValues.ContainsKey(node.Title))
{
node.Value = targetValues[node.Title];
}
}
int successCount = await plc.WriteNodesAsync(processNodes);
Console.WriteLine($"成功写入 {successCount}/{processNodes.Count} 个节点");
API参考
S7Plc类
主要的PLC连接和操作类。
属性
IsConnected- 获取当前PLC连接状态IsRunning- 获取PLC是否处于运行状态Name- 获取PLC连接名称
方法
Connect()- 同步连接PLCConnectAsync(int timeoutSeconds = 3)- 异步连接PLCDisconnect()- 同步断开PLC连接DisconnectAsync()- 异步断开PLC连接UseAutoReconnection(bool enable = true, TimeSpan? reconnectionDelay = null)- 配置自动重连DB(int id)- 获取指定编号的数据块实例UseBlock(int db)- DB方法的别名ReadNode<T>(S7PlcNode node)- 从指定节点读取值ReadStruct(S7PlcNodeStruct plcStruct)- 读取S7PlcNodeStruct中定义的所有节点的值WriteNode(S7PlcNode node)- 向指定节点写入值WriteNode(S7PlcNode node, object value)- 向指定节点写入指定值WriteNodeAsync(S7PlcNode node)- 异步向指定节点写入值WriteNodeAsync(S7PlcNode node, object value)- 异步向指定节点写入指定值WriteNodes(IEnumerable<S7PlcNode> nodes)- 批量写入多个节点WriteNodesAsync(IEnumerable<S7PlcNode> nodes)- 异步批量写入多个节点Watch(S7PlcNode node, WatchModel.WatchCallbackDelegate<object> cb)- 设置节点值监视UseHeartbeat(S7PlcNode heartbeatNode, HeartbeatCallback? cb = null)- 使用心跳功能UseReadyStatus(S7PlcNode readyNode, bool ready = true)- 使用就绪状态功能SetReadyStatus(bool ready = true)- 设置就绪状态SetReadyStatus(S7PlcNode readyNode)- 设置指定节点的就绪状态GetReadyStatus(S7PlcNode readyNode)- 获取就绪状态UnWatch(WatchModel model)- 移除数据监视
S7PlcDb类
数据块操作类,提供对特定DB块的读写功能。
方法
ReadBool(string offset)- 读取布尔值ReadByte(string offset)- 读取字节值ReadInt(string offset)- 读取Int值ReadUInt(string offset)- 读取UInt值ReadDInt(string offset)- 读取DInt值ReadUDInt(string offset)- 读取UDInt值ReadFloat(string offset)- 读取Float值ReadDouble(string offset)- 读取Double值ReadString(int offset, int length = 254)- 读取字符串ReadDateTime(string offset)- 读取日期时间WriteBool(string offset, bool value)- 写入布尔值WriteInt(string offset, short value)- 写入Int值WriteUInt(string offset, ushort value)- 写入UInt值WriteDInt(string offset, int value)- 写入DInt值WriteUDInt(string offset, uint value)- 写入UDInt值WriteFloat(string offset, float value)- 写入Float值WriteDouble(string offset, double value)- 写入Double值WriteString(int offset, string value, int length = 254)- 写入字符串WriteDateTime(string offset, DateTime value)- 写入日期时间Watch(WatchModel model)- 添加数据监视Watch(S7PlcNode node, WatchModel.WatchCallbackDelegate<object> callback)- 添加节点数据监视UnWatch(WatchModel model)- 移除数据监视
S7PlcNode类
表示PLC中的一个数据节点,是进行PLC数据交互的基础单元。详细文档请参阅 README.S7PlcNode.md。
核心属性
Title- 节点名称,用作唯一标识符Type- 数据类型(bool, int, uint, dint, udint, real, double, string, datetime)Db- DB块编号Offset- 偏移量(支持位偏移,如"10.0")BitLength- 位长度(主要用于字符串类型,默认255)Value- 节点的当前值(不参与序列化)Description- 节点描述Enabled- 节点是否启用(默认true)
反馈机制属性
NeedFeedback- 是否需要写入反馈确认FeedbackNodeKey- 反馈节点的键(自动生成为".feedback")
地址范围属性
StartAddress- 起始字节地址(用于批量读取优化)EndAddress- 结束字节地址(用于批量读取优化)Items- 节点位置对照列表(用于枚举类型)
事件
WriteSucceeded- 写入成功事件WriteFailed- 写入失败事件
方法
GetNodeDataType()- 获取节点对应的C#数据类型Clone()- 创建节点的浅拷贝Validate()- 验证节点基本有效性Equals(S7PlcNode other)- 比较两个节点是否相等
静态方法
Create(string title, string type, int db, string offset)- 创建节点的工厂方法
接口实现
INotifyPropertyChanged- 属性更改通知ICloneable- 支持克隆操作IEquatable<S7PlcNode>- 支持相等性比较
S7PlcNodeStruct 类
用于定义一个包含多个独立 S7PlcNode 对象的逻辑集合(结构体),以便通过 S7Plc.ReadStruct() 方法一次性读取它们。详细信息请参阅 README.S7PlcNodeStruct.md。
文档结构
本库提供了完整的文档体系,包括:
- README.md - 主文档,包含快速开始和基本使用指南
- README.S7Plc.md - S7Plc类的详细使用说明和API参考
- README.S7PlcNode.md - S7PlcNode类的完整文档,包含所有属性、方法和使用示例
- README.S7PlcNodeStruct.md - S7PlcNodeStruct类的使用指南,用于批量读取优化
- README.WriteNode.md - WriteNode方法的详细使用指南
- README.S7PlcCache.md - S7PlcCache缓存管理的使用说明
建议按照以下顺序阅读文档:
- 首先阅读本文档了解基本概念和快速开始
- 深入学习
README.S7PlcNode.md了解节点的详细使用 - 根据需要查阅其他专门文档
版本历史
v2.1.1 (当前版本)
- 增强了S7PlcNode的事件处理机制
- 改进了批量读写性能
- 完善了文档体系
- 增加了更多使用示例
主要特性演进
- v1.x - 基础PLC连接和数据读写功能
- v2.0 - 引入S7PlcNode节点模式,支持事件驱动
- v2.1 - 增加S7PlcNodeStruct批量读取,完善缓存机制
许可证
Copyright © WinM 2023-2025
此项目基于MIT许可证发布。
贡献
欢迎提交问题和贡献代码。请确保遵循项目的代码风格和贡献指南。
贡献指南
- Fork 本仓库
- 创建特性分支 (
git checkout -b feature/AmazingFeature) - 提交更改 (
git commit -m 'Add some AmazingFeature') - 推送到分支 (
git push origin feature/AmazingFeature) - 开启 Pull Request
联系方式
沈阳维美智能工业科技有限公司
- 网站:https://github.com/WinM-Tech/WinM
- 邮箱:support@winm-tech.com
- 技术支持:请通过GitHub Issues提交问题
常见问题 (FAQ)
Q: 支持哪些西门子PLC型号?
A: 支持所有兼容S7通信协议的PLC,包括S7-1200、S7-1500、S7-300、S7-400等系列。
Q: 如何处理网络连接不稳定的情况?
A: 使用自动重连功能:
plc.UseAutoReconnection(enable: true, reconnectionDelay: TimeSpan.FromSeconds(2));
Q: 可以同时连接多个PLC吗?
A: 可以,每个PLC使用独立的S7Plc实例:
var plc1 = new S7Plc(CpuType.S71500, "192.168.0.1");
var plc2 = new S7Plc(CpuType.S71200, "192.168.0.2");
Q: 如何优化大量数据点的读取性能?
A: 使用S7PlcNodeStruct进行批量读取,将连续的同类型数据点组织成结构体。
Q: 支持哪些数据类型?
A: 支持bool、int、uint、dint、udint、real、double、string、datetime等常用数据类型。
Showing the top 20 packages that depend on WinM.S7.
| Packages | Downloads |
|---|---|
|
WinM.S7.Wpf
用于WPF程序的S7控件展示
|
29 |
|
WinM.S7.Wpf
用于WPF程序的S7控件展示
|
14 |
|
WinM.S7.Wpf
用于WPF程序的S7控件展示
|
13 |
|
WinM.S7.Wpf
用于WPF程序的S7控件展示
|
12 |
|
WinM.S7.Wpf
用于WPF程序的S7控件展示
|
6 |
|
WinM.WpfModule.Module.Imlight
Imlight涂胶检测采集模块
|
6 |
|
WinM.WpfModule.Shell
MainApp通用壳
|
6 |
|
WinM.S7.Wpf
用于SiemensPLC通信
|
5 |
|
WinM.WpfModule.Module.Imlight
Imlight涂胶检测采集模块
|
5 |
|
WinM.WpfModule.Module.Biz
WPF模块开发 - 业务核心
|
5 |
|
WinM.WpfModule.Module.Bos6000
用于BOS6000模块的WPF控件,支持便捷开发与部署。
|
5 |
|
WinM.WpfModule.Shell
MainApp通用壳
|
5 |
|
WinM.WpfModule.Shell
MainApp通用壳
|
4 |
|
WinM.WpfModule.Module.Bos6000
用于BOS6000模块的WPF控件,支持便捷开发与部署。
|
4 |
.NET 8.0
- S7netplus (>= 0.20.0)
- WinM.Config (>= 2.0.0)