连接PLC前必须确认IP地址、端口、站号、槽号4个参数,错误会导致NetworkConnectionException或超时;地址格式需严格匹配厂商规范,异步操作须检查IsSuccess并正确调度UI更新,心跳与重连需自主实现。

连接 PLC 前必须确认的 4 个通信参数
不填对 IpAddress、Port、Station(部分协议如 Modbus TCP 不需要)、Slot(如西门子 S7),HslCommunication 会直接抛出 NetworkConnectionException 或超时。常见错误是把仿真器 IP 写成本地 127.0.0.1,而实际 PLC 在局域网中,真实地址应是类似 192.168.1.100 的设备 IP。
以三菱 FX 系列为例,需开启「编程口通信」并设置为「QnA 兼容 3E 帧」模式;西门子 S7-1200 要在 TIA Portal 中启用「允许从远程 partner(PLC)访问」并勾选「PUT/GET 通信」;Modbus TCP 则需确认 PLC 侧已启动 Modbus Server 功能且端口未被防火墙拦截。
-
IpAddress必须是 PLC 物理网卡 IP,不是虚拟机 NAT 地址或 Docker 网桥地址 - 西门子
S7Net实例中Slot通常为0(CPU 槽位),但 ET200SP 等分布式 I/O 可能为1或2 - 三菱
McNet的Port默认是6000,但某些固件版本可能用5000,需查手册 - 所有协议首次连接前,建议用
ping+telnet {ip} {port}验证基础连通性
读写数据时别直接用 string 地址,先看协议地址映射规则
HslCommunication 对不同 PLC 的地址语法完全不同,硬套 "D100" 或 "DB1.DBW2" 会返回 OperateResult 中 IsSuccess == false 且 Message 含「地址格式错误」。根本原因是各厂商地址空间设计差异大:三菱 D 区是字寄存器,而西门子 DB 块需指定 DB 编号 + 起始偏移 + 数据类型长度。
正确做法是查对应协议类的文档说明,例如:
-
McNet读 D100:用"D100",读 R1000(文件寄存器):用"R1000",不能混用 -
S7Net读 DB1 中起始地址 2 的 int:用"DB1.DBW2";读 M10.0(位):用"M10.0";读整个 DB 块:用"DB1"(需配合ReadBytes) -
ModbusTcpNet读保持寄存器 40001:地址填0(因为协议内部自动 +1),不是40001 - 所有读操作建议统一用泛型方法如
ReadInt16("D100"),避免手动解析字节数组出错
异步读写要小心 OperateResult 泛型丢失和线程上下文
调用 ReadInt16Async 返回的是 Task,如果直接 await 后只取 .Content,一旦 PLC 断线或地址非法,.Content 是默认值 0,错误被静默吞掉。更危险的是在 WinForms 中用 Task.Run(...).Wait() 阻塞主线程,可能引发死锁。
必须检查 IsSuccess 并处理失败路径:
var result = await mcNet.ReadInt16Async("D100");
if (!result.IsSuccess)
{
// 记录 result.Message,比如 "连接已断开" 或 "地址 D100 超出范围"
return;
}
short value = result.Content;
另外,HslCommunication 的异步方法内部不捕获 UI 线程同步上下文,所以 WPF/WinForms 中更新控件需显式调度:
- WinForms:用
this.Invoke((MethodInvoker)delegate { label1.Text = value.ToString(); }); - WPF:用
Dispatcher.Invoke(() => textBlock.Text = value.ToString()); - 控制台或服务程序则无需调度,但要注意
async void仅用于事件处理器,其他一律用async Task
心跳检测和重连逻辑不能依赖库内置机制
HslCommunication 提供了 ConnectServer 和 DisconnectServer,但没有自动重连或心跳维持。实际工业现场网络抖动频繁,单纯靠 IsConnected 属性判断不可靠——它可能返回 true,但下一次读就超时。
可靠方案是自己实现轻量心跳:
- 用
Timer每 5 秒调用一次ReadBool("M0.0")(选一个固定存在的位地址) - 若连续 2 次失败,执行
DisconnectServer()→ 等待 1 秒 →ConnectServer() - 避免高频心跳(如 1 秒一次),否则可能被 PLC 限流或触发通信异常
- 所有读写操作都应包裹在
try/catch中捕获TimeoutException和SocketException,而不是只依赖IsSuccess
真正的难点不在连接本身,而在如何区分「PLC 关机」、「网线拔掉」、「IP 冲突」这三种断连场景——它们的日志表现一致,只能靠上层业务逻辑结合多点探测(如同时 ping 网关 + 读多个地址)来交叉验证。










