自制工业控制系统:MES对接实战——寄存器映射、报警码定稿与MTConnect起步
Contents
起因
Modbus 比这台机床用的所有协议都老:1979 年 Modicon(后来被施耐德买下)为自己的 PLC 定的内部点对点协议,那时候可编程逻辑控制器刚从继电器柜里长出来,寄存器是 16 位的——不是设计者偷懒,是当时的小型机和早期微处理器本来就是 16 位字长,协议跟着硬件的骨架长,四十多年后这条骨架没换,换的是外面套的信封(串口 RTU 到以太网 TCP,网络篇已经把这层信封讲透了)。
“MES"这个词比 Modbus 年轻,1992 年 AMR Research(先进制造业研究机构)给它下了第一个定义:一套驱动车间执行的动态信息系统。九十年代各家系统功能越做越杂,1997 年 MESA(制造企业解决方案协会)出面给 MES 划范围,再往后 ISA-95 标准把整个企业信息栈切成三层——企业层(ERP,算钱算料算单)、执行层(MES,派工排产追溯)、控制层(我们这台机床)。这台机床坐在最底层,MES 对接要解决的问题从来只有一个:控制层的状态怎么原原本本地爬到执行层去,执行层的派工指令怎么原原本本地落到控制层来。
行业后来又长出一层新协议,原因值得记一句:2006 年 Sun 的 David Edstrom 和伯克利的 David Patterson 在一次制造技术协会(AMT)的会上把话说穿了——机床联网这件事,行业比消费电子落后了二十年,每家机床厂的协议各自为政,MES 厂商接一台新机床就要写一套新驱动。MTConnect 就是冲着这个问题设计的:开放、免费、用 HTTP/XML 这种互联网早就验证过的技术,2008 年第一版发布。它和 Modbus 的分工不是竞争,是接力——Modbus 是"你问我答"的寄存器协议,MTConnect 是"我把状态摆成一棵标准结构的树,你随时来看"的只读展示协议,两者服务的场景不同。
我们已经有的底子在网络篇里:自研 TCP/IP 协议栈跑通了,Modbus/TCP 列进了应用层最小集,连接表 8 槽够用;放电模块那边(放电篇)已经用 Modbus-RTU 兼容帧跟上位机说话。这篇要做的是把两头接上:上位机自己也要当一次 Modbus 服务端,被 MES 拉取;往上再加薹一层 MTConnect agent。国际化篇留了个没定的账——报警码是 u32,Modbus 寄存器是 u16,这篇把它定下来。
一、寄存器模型:把机床状态摆成一张表
Modbus 的应用层四十多年没变过,四个地址空间各管各的:线圈(1 位,可读写)、离散输入(1 位,只读)、输入寄存器(16 位,只读)、保持寄存器(16 位,可读写)。功能码是收发双方的公共语言——0x03 读保持寄存器、0x04 读输入寄存器、0x06/0x10 写单个/多个寄存器,这套契约本站Modbus 基础篇已经讲过一遍,这里不重复,直接定我们自己的映射。
先分类装表。机床状态天生分两种脸:只读的遥测(位置、电流、状态位)进输入寄存器区,MES 只看不改;可写的指令(启停、暂停、规准切换请求)进保持寄存器区,写这一区就是下指令。这个分类不是 Modbus 的规定,是我们自己定的纪律——防止一次误写把只该看的东西改了。
| 地址段 | 空间 | 内容 |
|---|---|---|
| 30001–30020 | 输入寄存器 | 运行状态字、当前段号、当前规准组号、四轴位置(X/Y/U/V 各两寄存器) |
| 30021–30040 | 输入寄存器 | 平均电流、短路率、累计切割面积(两寄存器)、当前报警码 |
| 30041–30060 | 输入寄存器 | 服役档案:总加工时长、总开机次数、断丝总次数(各占位按需扩展) |
| 40001–40010 | 保持寄存器 | 控制字(启动/暂停/急停请求位)、请求切换的规准组号 |
这张表和插补篇自研运动卡的寄存器表是同一层职责,只是面向的对象换了——那张表是软件对硬件的合同,这张是上位机对 MES 的合同,两份合同分别站在两层架构的两端。
u16 宽度是个硬约束,得先把账算清楚。系统内部全部走 i64 µm(解析器篇的拒 f64 纪律),位置动辄六位数微米,一个寄存器塞不下;累计面积、总加工时长同理。约定统一取两寄存器拼一个 32 位值,高位寄存器在前(地址小的那个放高 16 位)——这不是 Modbus 强制的顺序,行业里高位在前、低位在前两种都有厂商在用,我们的选择就是选一种并写进文档,不留隐式约定:
|
|
位置(i64 µm)截成两段处理:行程 ±2m 折 ±2×10⁶ µm,32 位有符号数上限约 21 亿,富余两个数量级,直接按 i32 编码复用上面这对函数(位模式相同,读出来按需转 i64)。
二、报警码的账:u32 定义,u16 上路
国际化篇把报警码定成 u32,理由是留够码空间——工控系统卖十几年,子系统要往里加,码段不够用比啥都尴尬。但真正在跑的代码只用了这个空间的一小角:码 = 子系统段(4 位)+ 类别(4 位)+ 序号(8 位),16 位刚好装满,高 16 位这些年一直是 0。这不是巧合,是国际化篇当时就按这个预算切的段——u32 是给未来留的容量,不是眼前用满的宽度。
这条遗留决策现在能定稿了:当前报警码走单寄存器(30040 那一格,直接搬 16 位有效值上去,高位不带,MES 侧的解析脚本简单一行搬),但寄存器表在它旁边留一格保留位、恒填 0,不是隐式截断——将来某天真的要往高 16 位塞东西,是新增语义、不是破坏现有对接。历史报警导出(维护屏那本服役档案往外吐 CSV 的场景)另算,那条路径走两寄存器的 32 位编码,给足够长的未来:
|
|
这条 debug_assert 是给自己留的报警——码表真长到超预算那天,开发机上跑测试第一时间会响,不会等到现场排障才发现寄存器截断丢了信息。
三、Modbus TCP Server:在自研栈上加一个新角色
网络篇把 TCP 栈立起来时,应用层想的是我们主动去连别人(DHCP、TFTP 拉程序)。这篇要反过来:MES 主动连我们,我们当服务端——协议栈本身不用改一行,新增的是监听和分发这层。
MBAP 头(7 字节:事务标识、协议标识、长度、单元标识)加 PDU 是老规矩,不重复讲;新东西是分发表——收到功能码,查表调对应的读写函数,读函数直接从上面那张寄存器表里搬值,写函数落进控制字后由 Core1 协作层下一拍解读执行:
|
|
单帧读寄存器上限 125 个(PDU 253 字节的老约束),我们这张表还不到六十格,一次读全表都够用,不用分次读的麻烦。事务标识要原样搬回响应——MES 侧允许并发挂多个请求,这是配对的唯一凭据,漏了这一步现场排障时"数据串行"这个报警会很难查。连接异常(对端断线)按老规矩处理:清空连接槽位,不特殊报警——TCP 层的事,不冒泡到业务层。
这一层不摸卡寄存器,和界面篇的老口径一脉相承——运行侧真实数据从状态回传 SPSC 环搬进 RegMap,这层只读那张镜像表,插补器和 MES 服务谁都不直接碰谁。
四、往上一层:MTConnect agent 的 HTTP/XML
Modbus 是问答式的,MES 每要一个数就发一次请求;MTConnect 换了个模型——设备把自己的能力和状态摆成一棵标准结构的 XML 树,MES 一次 HTTP GET 拿到全貌,轮询频率自己定。这层协议对我们最贵的部分不是 XML,是要先有个 HTTP 服务端——网络篇那套最小 TCP 栈上目前只挂了 NTP/TFTP/Modbus,HTTP 是这篇新加的第四个应用层住户,而且第一次要当服务端(TFTP/NTP 之前都是客户端角色)。
好消息是 MTConnect 用的 HTTP 子集极窄——只要 GET、只要 200/404 两种状态码、不需要 keep-alive 之外的任何花活,几十行就能撑住:
|
|
响应体是三类数据项的 XML,MTConnect 把状态天生分成三桶,恰好跟我们寄存器表分的两类对得上、还细一档:
- Samples(连续量):位置、电流、切割面积——对应我们输入寄存器区的数值字段
- Events(离散状态):运行/暂停/停止、当前规准组号——对应状态字
- Condition(报警与异常):当前报警码,连同 Warning/Fault 等级——单独成一类,不是普通数值,MES 系统看这一类的眼神跟看别的不一样
|
|
这个函数是纯字符串拼接,不碰任何硬件——跟第六节要说的可测性正好对上。设备能力描述(/probe,静态的那部分:这台机器有几根轴、叫什么名字)一次写死进固件,不用动态生成;真正要跑起来的只有 /current 这条热路径。
五、OPC UA/umati:知道它在哪,但现在不做
分期表上第三级是 OPC UA,umati(VDW/VDMA/OPC 基金会推的机床开放接口)是这条路线在机床行业的具体落地。诚实交代为什么现在不碰:OPC UA 不是一个简单的报文格式,是一整套地址空间模型(节点、引用、方法调用)加二进制编码加安全通道协商——量级跟我们自己写的 TCP/IP 栈相当,不是往应用层加一个函数能糊弄过去的。umati 的信息模型(OPC 40501 机床配套规范)定义得很细,认真接一次是另立一篇甚至一个系列的工程量。这篇的分期表停在 MTConnect,OPC UA 留在路线图上,不写成"下一篇"的空头支票。
六、落进系统:第六个开发机可测模块
数一数纯函数模块:解析器、插补器、锥度展开器、直纹面生成器、输入法引擎——五个都能 cargo test。这篇加的 Modbus 分发表和 MTConnect XML 拼接是第六个:输入是 RegMap(一份纯数据快照)和收到的 PDU/HTTP 请求,输出是响应字节流,没有一处摸网卡、摸中断、摸硬件寄存器——协议逻辑和硬件收发天生分层,跟插补器"几何递推"与"FPGA 播放"的分离是同一个道理。
测试三条:
- 编解码往返:
encode_u32/decode_u32任意值互逆;报警码debug_assert在构造超预算测试码时真的触发 - Modbus 契约:构造标准帧(参考帧来自协议文档的示例),分发结果逐字节对——这条防的是字节序、功能码判断这类低级但要命的错
- MTConnect 结构:解析自己吐出来的 XML,能反查出写进去的位置、状态、报警码,往返一致——防的是格式字符串拼错导致的静默数据错位
运行侧挂接:RegMap 由 Core1 协作层每拍从状态回传环刷新,Modbus/HTTP 服务和输入法引擎、网络协议栈本身一样,全跑在协作层——这条边界从多核篇立起来,到这篇第六次兑现。
总结
实战清单:MES 三层模型(企业/执行/控制,机床坐最底层)决定对接分几期;寄存器映射(输入区放遥测、保持区放指令,两寄存器拼 32 位值、高位在前的字序约定写进文档不留隐式默认);报警码遗留决策定稿(u32 是给未来的预算、当前 16 位实际码走单寄存器,超预算用 debug_assert 报,历史导出走两寄存器 32 位真值);Modbus TCP Server(在自研栈上新增服务端角色,MBAP+功能码分发表,单帧一次读全表);MTConnect agent(HTTP 服务端是新住户,极简 GET+XML,Samples/Events/Condition 三桶正好对上我们的寄存器分类);OPC UA/umati 留路线图不写空头支票;协议逻辑与硬件收发分层=第六个开发机可测模块(编解码往返/Modbus 契约/MTConnect 结构三测试)。
跑通标志:MES 模拟器(或 Modbus Poll 这类工具)按上面地址段读出的位置、报警码跟界面篇仿真屏显示的数字逐字段一致;构造一个真实报警,单寄存器码值跟国际化篇的码表条目对得上;curl 一次 /current 拿到能被标准 MTConnect 客户端解析的 XML;两次 MTConnect 请求之间机床状态不变,响应逐字节一致(跟仿真屏的确定性回放是同一类验收标准)。
MES 这条线的软件账算完了,机床本体那个知识黑洞(工作液、走丝、导轮系统)还没填,下一篇往那个方向去,或者先把等手册的运动控制卡驱动接上——两条路都在排队。
参考链接
- Wikipedia: Modbus——1979 年 Modicon 发布的协议来历
- Wikipedia: MTConnect——2006 年 AMT 大会动议、2008 年首版发布的行业来历
- ISA: Enterprise-Control System Integration (ISA-95)——企业/执行/控制三层模型
- 本站:Modbus TCP和Modbus RTU协议的区别——功能码、MBAP 头、字节序踩坑记录
- 本系列:网络驱动与协议栈实战(自研 TCP/IP 栈与 Modbus/TCP 起步)、国际化实战(报警码 u32 的码段设计)、机床界面实战(平均电流/短路率显示与服役档案)、放电加工与高频电源(放电模块 Modbus-RTU 帧)、插补实战(自研卡寄存器表,同一层职责的另一份合同)
Author 软件开发大郭
LastMod 2026-09-11