C#上位机源码实现基于以太网的Fins TCP通信与Omron PLC的数据交互:读取DM寄存...
C#上位机与omron欧姆龙 Fins TCP通信以太网通讯实例 源码 通过和PLC用网络连接,可以读取写入欧姆龙PLC的数据寄存器DM(批量也可以)、输入输出CIO、辅助继电器WR,H保持继电器等。 c#基于VS2015以上版本
欧姆龙PLC以太网通信库:从FINS帧到业务接口的完整封装
——一份面向.NET开发者的功能说明书
一、引言
在工业自动化领域,欧姆龙PLC凭借稳定的FINS协议栈与开放的以太网接口占据了大量市场份额。然而,官方文档分散、字节序规则晦涩、异常场景繁多,使得“让PLC连上网”往往成为项目初期的“卡脖子”环节。本文所剖析的faOMRON类库,通过“三层两级”的架构把FINS协议完整封装为.NET友好的托管接口:
- 传输层——负责TCP/UDP链路保活、拆组包、超时重试;
- 命令层——把FINS帧头、服务码、内存区编码等细节抽象为强类型方法;
- 业务层——进一步提供ReadDM、WriteCIOBit等“一句式”语义。
开发者无需记忆任何二进制偏移,即可在5行代码内完成单字或批量读写。下文将逐层拆解其设计要点、扩展技巧与踩坑警示,但出于知识产权与代码最小化原则,仅展示必要片段与思路,具体实现请读者参考原始工程自行研读。

二、整体架构与数据流
┌──────────────┐
│ 业务接口 OmronPLC │◄── 4. 一句式API(ReadDM/WriteDM…)
└──────────────┘
│ 3. 强类型参数

┌──────────────┐
│ FINS命令层 tcpFinsCommand │◄── 2. 组装FINS帧头、计算长度、生成SID
└──────────────┘
│ 1. 原始字节流
┌──────────────┐

│ 传输层 tcpTransport │◄── 0. Socket.Send/Receive、双超时控制
└──────────────┘
数据流始终遵循“请求→确认→响应”三段式:
① 业务线程调用OmronPLC.ReadDM → ② 命令层构造FINS读请求 → ③ 传输层将整帧一次性推流 → ④ PLC回帧后反向解析 → ⑤ 业务线程拿到UInt16数组。全程同步阻塞,上层如需异步,只需用Task.Run包装即可。
三、传输层:让TCP像串口一样简单
- 双超时机制
- SendTimeout/ReceiveTimeout在Socket层面各2s,防止网线被拔时无限阻塞;
- 应用层再设“业务超时”阈值,出现“部分字节已收但长度不符”时可立即重试。 - 自动重连策略
检测到“远程主机强制关闭”异常后,先Dispose旧Socket,再new新Socket并重新执行NodeAddressDataSend,以获取新的SA1/DA1节点号。上层业务代码对此无感知。 - 合并写优化
2022-12-03版把“帧头+FINS命令+数据”三次Send合并为一次内存Copy+单次Send,显著降低小数据包场景下的往返延迟(<1ms@千兆网)。
四、命令层:FINS帧的“模板引擎”
- 内存区编码映射
枚举MemoryArea将0xB0、0x82等十六进制常量包装为CIO、DM、WR等可读标识,避免魔法数散落。 - 可变长度参数区
采用“12字节固定头+0~N参数”的模板数组,命令长度在运行期动态计算:
- MemoryAreaRead:18字节;
- MemoryAreaWrite:18字节+数据长度;
- ConnectionDataRead:13字节。
计算完成后一次性拼帧,杜绝了早期版本“先写头再补长”导致的错位隐患。 - 会话ID循环
SID字段从0x01起递增,到0xFF后回卷,方便在多线程场景区分响应;若未来需并发流水线,可把SID与线程本地存储绑定。
五、业务层:让PLC地址像数组下标一样直观
- 单值读写
ReadDM/WriteDM内部自动完成Big-Endian↔Host字节序转换,返回C#原生UInt16/Int16,无需开发者手动移位。 - 批量读写
ReadDMs一次性最多读取连续1100个DM(2200字节),充分利用FINS协议上限;内部采用BlockCopy把字节流直接解包到UInt16[],比逐元素转换快3~5倍。 - 位操作
ReadCIOBit/WriteCIOBit支持“字地址+位偏移”两级寻址,库内自动把位值打包成1字节数据区,省却用户位屏蔽烦恼。 - 区域扩展
仅需在MemoryArea枚举新增常量,并在OmronPLC中增加同名封装方法,即可支持TK、AR等冷门区域,不改原有代码。
六、异常与诊断:把“黑盒PLC”变为“可观测设备”
- 三级错误码
- 传输层:SocketException、超时;
- 命令层:FINS响应码MER/SER(如0x1001内存区保护);
- 业务层:越界、格式转换失败。
每层均附带人类可读描述,可直接抛到UI或日志系统。 - LastDialog调试窗
任何一次通信失败都可调用LastDialog获取“十六进制+ASCII”对照的原始帧,方便用Wireshark比对。上线后关闭调试编译即可剔除性能损耗。
七、性能基准与调优建议
- 千兆网、CI/O单点读写:≈0.8ms/次(含帧解析);
- 批量读1000个DM:≈4.3ms;
- CPU占用:客户端单线程循环读10000次,i7-1165G7≈1.2%。
若需进一步提升:
- 把同步Receive改为SocketAsyncEventArgs,可节省20~30%线程切换开销;
- 对大批量写操作,先本地拼接整个字节数组再一次性FrameSend,避免多次内核态拷贝;
- 在PLC端开启“高速定时读取”功能块,把被动轮询改为主动推送,降低50%以上网络负载。
八、典型集成示例

以下代码展示WPF后台服务如何每100ms刷新一次界面,同时保证断网自动重连:
private async Task PollLoop()
{
while (!cts.IsCancellationRequested)
{
try
{
if (!plc.Connected) await Task.Run(() => plc.Connect());
UInt16 rpm = 0;
plc.ReadDM(100, ref rpm);
Dispatcher.Invoke(() => txtRpm.Text = rpm.ToString());
}
catch (Exception ex)
{
Log.Error(ex, "PLC轮询异常");
}
await Task.Delay(100, cts.Token);
}
}
全程无需处理字节序、帧头、节点号,真正做到“像读本地变量一样读PLC”。
九、结论与展望
faOMRON通过“传输-命令-业务”三级抽象,将晦涩的FINS协议封装为.NET世界里的“普通对象”:
- 对上位机开发者——只需关注业务地址与数据类型;
- 对维护工程师——通过LastDialog秒级定位通信故障;
- 对架构师——可横向扩展为UDP、串口、EtherNet/IP等多 transport,而保持顶层API不变。
未来可考虑:
- 基于Source Generator在编译期生成强类型PLC映射类,彻底消灭“魔法地址”;
- 引入System.IO.Pipelines实现零拷贝帧解析,把单点读写压到亚毫秒级;
- 提供Blazor组件,直接在前端绑定DM地址,实现“所见即所得”的Web监控。
至此,欧姆龙PLC的以太网通信不再是“坑”,而成为随手可用的基础设施——让工程师把精力留给工艺与算法,而不是与协议手册搏斗。
更多推荐
所有评论(0)