一个个人开发者,怎么啃下 12 种工控协议?

一个个人开发者,怎么啃下 12 种工控协议?

Modbus、西门子 S7、三菱 MC、欧姆龙 FINS(UDP/TCP/串口/HostLink 四种传输)、欧姆龙 CIP、罗克韦尔 CIP、OPC UA------这个清单经常被人问两个问题:"你怎么可能一个人写完?"和"写这些不是重复造轮子吗?"

这篇文章把过程摊开讲:为什么做、按什么套路做、每个协议各有什么坑、以及哪些部分我用了现成库、哪些只能自己写。工控协议没有传说中的高深,但坑是真的多。

github:github.com/Ellisata/io... · gitee:**[gitee.com/wang4856304...](https://link.juejin.cn?target=https%3A%2F%2Fgitee.com%2Fwang4856304%2Fiot-gateway**(AGPL-3.0 "https://gitee.com/wang4856304/iot-gateway**(AGPL-3.0") 开源)


一、为什么是 12 种:不是野心,是现场

做第一个项目时我只实现了 Modbus.TCP,觉得够用了。直到现场来了设备清单:两台西门子、三台欧姆龙、一台三菱,还有一台罗克韦尔------车间不按我的技术选型采购,这是工业软件的第一课。

异构设备是常态。所以协议覆盖不是"功能丰富"的卖点,而是能不能拿下项目的门槛。清单就是这么被现场逼出来的:

协议 传输 实现方式
Modbus RTU / TCP 串口 / 以太网 goburrow/modbus
Siemens S7 ISO-on-TCP robinson/gos7
三菱 MC 3E TCP / 串口 自研帧协议
欧姆龙 FINS UDP / TCP / 串口 / HostLink.TCP 自研帧协议 + 四种传输
欧姆龙 CIP EtherNet/IP 自研(共享 CIP 封装层)
罗克韦尔 CIP EtherNet/IP 自研(共享 CIP 封装层)
OPC UA 以太网 gopcua/opcua

用库还是自研的判断标准只有一条:库的质量和覆盖够不够。Modbus/S7/OPC UA 有成熟维护的 Go 库,不重复造;MC/FINS/CIP 的 Go 生态要么没有要么没人维护,只能自己对着手册写。

二、套路:每个协议都是同一道题的不同解法

写第三个协议之后我发现,协议驱动其实是同一道题的五个子问题:

  1. 报文格式:请求/响应的字节布局、校验(FCS/CRC)、事务号匹配;
  2. 传输层:TCP 要粘包处理和连接管理,UDP 要自己应答匹配和重传,串口要帧间隔;
  3. 地址解析 :把 DB1.DBW0D100Tag_Motor 变成协议二进制地址;
  4. 类型解码 :字节数组 → 按数据类型和字节序/字序还原成值;
  5. 区间合并:连续地址拼成一帧读,决定容量(这是另一篇文章的主题)。

于是项目里沉淀了统一抽象:driver.Driver 接口(Connect/Ping/Read/Close)+ 注册表(子包 init() 注册,引擎代码零感知)+ 类型注册表(协议作用域前缀,如 modbus.int16/s7.int16)。新增协议 = 实现一个接口 + 写一个 init,不动任何引擎代码。

三、各协议的坑,逐个说

Modbus:最简单,但字节序是重灾区

协议本身一页纸就能讲完,坑全在数据表示:float32 到底是大端还是小端?字序 ABCD 还是 CDAB?不同厂商设备答案都不同。所以驱动把字节序/字序做成了一等公民配置,注册表里每种组合都是独立类型(float/float.swap......)。这个问题没有"正确答案",只有"按设备配"。

S7:地址语法是设计出来的,协议是历史长出来的

用 gos7 之后主要工作是地址解析:DB10.DBD56M0.0IW64......DB 区/标志位区/输入/输出映像区各有语法。S7 的报文格式带着三十年的历史包袱(COTP → S7comm),幸亏有 gos7,自己啃 PDU 协商不划算。真正的容量优势:一帧 462 字节,是所有协议里帧数最省的。

三菱 MC 3E:第一个完整自研的帧协议

SLMP 3E 帧格式还算规整:副头部 + 网络号 + PC 号 + 监视定时器 + 指令 + 软元件点数 + 起始软元件地址。坑在于软元件地址编码:D 寄存器、M 置位区、X/Y,各自的地址空间和编码方式都不同,位单位和字单位还是两套请求。串口版(帧头 ENQ/帧尾 CR)是又一套封装。自研的直接收益:字读上限可以调到 960(默认 100),一帧的容量差 10 倍。

欧姆龙 FINS:一个帧协议,四种传输,四种性格

FINS 帧本身统一(ICF/RSV/GCT/DA1/SA1/SID + 指令码 + 参数区),但我写了四个传输实现,因为每种传输的性格完全不同:

  • UDP:协议不管可靠性,应答匹配、超时重传、SID 配对全要自己做------这是四个里最费工的;
  • TCP:FINS/TCP 有自己的握手帧( handshake),之后逐帧交互;
  • 串口:RS-232C 封装,帧间隔和超时都按串口的节奏来;
  • HostLink :伪装成 FINS 传输的ASCII 文本协议 ,@ 开头、* 结尾、两位 FCS 异或校验。FCS 的计算范围有个著名歧义------SID(服务标识)算不算进校验?我在真机上核实过:取窗是 NOSID 方式,@ 本身排除、SID 排除,从节点号取到报文末尾。这种细节手册里一笔带过,只能在真机上拿示波器式抓包对。

CIP:EtherNet/IP 的通用底座 + 两家方言

欧姆龙和罗克韦尔都说 CIP,底座共用:Encapsulation 头 → Register Session → SendRRData 装一个 CIP 请求。这块我上移成了共享封装层(driver/cip),两家驱动只写方言部分:

  • 欧姆龙:数据表标签寻址,0x4C 读服务,路径段是 0x91 符号段(标签名字节长 + ASCII + 奇数补零);
  • 罗克韦尔:Logix 标签寻址,数组标签可以按 ≤125 元素合并读,独立标签逐点读;还有桥接路径(经模块背板到目标槽位)。

真机上修过两个只看手册看不出来的问题:SendRRData 的地址项必须是 Null 地址项(0x0000)------我最初按某实现带了非空地址项,真机直接拒绝;以及标签不存在时响应回 0x04 路径段错误,解析器必须先判状态再要求数据长度,否则会把 4 字节错误响应误报成"响应过短",掩盖真实拒绝原因。

OPC UA:换了一个维度的协议

前面全是"字节拼装",OPC UA 是二进制序列化 + 会话 + 订阅的完整协议栈,自研不现实,gopcua 很称职。工程上的主要工作是寻址模式选择:直接 NodeID 寻址无额外往返,浏览路径(Device/Tag)首轮要 Translate 且重连失效------文档里如实建议生产用 NodeID。

四、质量怎么保证:假 PLC 服务器 + 互操作测试

协议驱动最怕两件事:文档错了不知道 ,和改代码把帧格式改漂移了

对策是给每种协议写一个假 PLC 服务器 :真实 TCP 监听,逐帧解析请求、回合法响应(Modbus 的 MBAP、MC 的 3E 帧、FINS 的握手+内存区读、S7 的 ISO CR/CC+PDU 协商、CIP 的 RegisterSession+SendRRData、OPC UA 直接复用 gopcua 进程内 server 包)。驱动 ↔ 假服务器之间跑集成测试------帧格式漂移一行,CI 直接红。

这套东西后来还兼职当了压测工具(容量压测报告就是这么来的),一份投入两次回报。

五、给想入门的人三条实在建议

  1. 别一上来铺协议。 先用一个协议(推荐 Modbus)把 采集→推送→控制台 全链路打通,再横向加协议。接口抽象是写了第二个协议之后才长对的;
  2. 文档三件套 :官方协议手册(欧姆龙 Wxxx 系列、三菱 SLMP 手册都是公开 PDF)、同类开源实现(如 HslCommunication)当参照、抓包当最终裁判。手册与真机冲突时,真机永远是对的;
  3. 每个未验证的细节在代码注释里标注"真机核实状态"。我仓库里每处真机验证过的坑都写着日期和现象,没验证的如实标"待真机核实"------这不是心虚,是工业软件的基本职业操守。

六、诚实的边界

  • FINS/TCP 已真机实测通过;HostLink 的 FCS 取窗(NOSID)已真机核实;FINS 部分内存区代码仍待真机校准,仓库内标注了;
  • CIP 的 0x0A 多服务批量读是新特性,报文格式以 HslCommunication 为参照,上线前须在目标 NJ/NX 上验证(默认关闭,详见优化实录一篇);
  • 12 种协议 ≠ 100+ 种设备。协议对但固件方言咬合不对的情况一定会有,这靠用户共建------Issue 里贴抓包,我来修。

七、项目信息

iot-gateway,AGPL-3.0 开源:上述 12 种协议/传输驱动,推送 MQTT / TDengine v3 / InfluxDB v3,断网 outbox 补发,单二进制部署,内嵌 Web 控制台(协议参数动态表单,加协议不用改前端)。

你在某个协议上踩过别的坑?Issue/评论区聊聊,说不定就是下一个修复记录。


系列相关:《点位容量不是点数决定的,是帧数 × 往返延迟决定的》《CIP 驱动从 69.8% 掉点到 0%:一次 0x0A 批量读优化实录》。

相关推荐
Bs_MoneyMagnet2 小时前
基于springboot+vue的旅游景区点评系统的设计与实现 源码+文档
java·vue.js·spring boot·后端·spring·旅游
cpp_learner2 小时前
Qt 5.14.2 x86_64 静态编译 —— 从零搭建完整手册
后端
企业数字化笔记3 小时前
固定资产历史数据怎么导入系统?Excel模板、字段映射和数据校验
android·java·数据库·后端
西瓜太郎12343 小时前
request_id 如何串起请求、路由、计费和日志
后端·python
by组态3 小时前
Ricon组态系统通信配置指南
前端·后端·物联网
sbjdhjd4 小时前
ThinkPHP 5.0.10 缓存写入型 RCE 复盘:从手动搭建、换行绕过到源码单步验证 | 05
java·后端·安全·spring·网络安全·数据挖掘·php
LEE4 小时前
Agent 的控制权,为什么正在回到模型手里?
前端·后端
沃夫上校4 小时前
跨域这件事,其实浏览器在替你挡刀
前端·后端·nginx
用户298698530144 小时前
Python 将 Word 文档转换为图片的实践指南
后端·python·api