2. SECS-II消息结构:数据项、消息格式、流与功能、事务处理
好,咱们接着聊。上一章我们把SECS/GEM的“骨架”——物理层和链路层——搭起来了。现在,该往里面填“血肉”了。这一章要讲的SECS-II,说白了就是设备之间交流的“语言规范”。你想想看,两个人说话,光有电话线(物理层)和拨号规则(链路层)还不够,还得有语法和词汇吧?SECS-II就是干这个的。
我个人习惯把SECS-II看作一个“信封+信纸”的系统。信封是消息格式,信纸上的内容就是数据项。而流(Stream)和功能(Function)呢?嗯,它们就像是信件的分类编号,一看就知道这封信是“生产报告”还是“报警通知”。
2.1 数据项(Data Item)详解
数据项,是SECS-II里最小的信息单元。你可以把它想象成编程语言里的“变量”。每个数据项都有一个类型和长度。
常见的SECS-II数据类型:
| 类型码 | 名称 | 描述 | 长度 |
|---|---|---|---|
| 0x00 | List | 列表,可以包含其他数据项 | 可变 |
| 0x10 | Binary | 二进制数据,比如状态位 | 1~N字节 |
| 0x20 | Boolean | 布尔值,TRUE/FALSE | 1字节 |
| 0x30 | ASCII | 字符串,比如设备ID | 1~N字节 |
| 0x40 | I8 | 8位有符号整数 | 1字节 |
| 0x50 | I1 | 1位有符号整数(其实很少用) | 1字节 |
| 0x60 | I2 | 16位有符号整数 | 2字节 |
| 0x70 | I4 | 32位有符号整数 | 4字节 |
| 0x80 | F8 | 8字节浮点数 | 8字节 |
| 0x90 | F4 | 4字节浮点数 | 4字节 |
| 0xA0 | U8 | 8位无符号整数 | 1字节 |
| 0xB0 | U1 | 1位无符号整数 | 1字节 |
| 0xC0 | U2 | 16位无符号整数 | 2字节 |
| 0xD0 | U4 | 32位无符号整数 | 4字节 |
你看,类型码的高4位决定了数据类型,低4位表示字节长度(对于整数和浮点数)。这个设计挺巧妙的,解析器一看高4位就知道怎么处理了。
核心要点: 数据项是自描述的。每个数据项都包含类型、长度和值三部分。这跟TLV(Type-Length-Value)编码很像,但SECS-II的格式更紧凑。
数据项的编码格式:
| 类型码(1字节) | 长度(1~3字节) | 数据(N字节) |
举个例子,一个ASCII字符串"ABC"的编码是:
0x41 0x03 0x41 0x42 0x43
|类型|长度| A | B | C |
这里0x41是ASCII类型码,0x03表示长度是3字节,后面跟着三个字符。
我的经验: 长度字段的编码有点tricky。如果长度小于128,直接用1字节表示。如果大于等于128,会用2~3字节。具体规则是:第一个字节的最高位为1时,表示后面还有字节。我曾经在解析长度时踩过坑,少读了一个字节,导致整个消息解析错位。嗯,调试了整整一个下午。
2.2 消息格式(Message Format)
消息格式,就是数据项的组合方式。一个SECS-II消息由三部分组成:
- 消息头(Message Header):固定10字节,包含设备ID、流、功能、事务ID等信息。
- 数据体(Data Body):由数据项组成的树形结构。
- 消息尾(Message Trailer):在HSMS中不存在,在RS-232模式下有校验和。
消息头格式:
| 设备ID(2字节) | 控制位(1字节) | 流(1字节) | 功能(1字节) | 等待位(1字节) | 事务ID(4字节) |
这里要注意,设备ID在标准SECS-II中是2字节,但在HSMS中只用了低7位,最高位是控制位。我刚开始做的时候没注意这个细节,跟设备通信老是失败,后来才发现是设备ID的位宽搞错了。
数据体的结构:
数据体是一个树形结构,根节点通常是一个List。比如,一个简单的“设备ID查询”消息,数据体可能是:
List (2个元素)
├── ASCII "DEVICE_ID"
└── U4 12345
编码后就是:
0x00 0x02 0x41 0x09 "DEVICE_ID" 0xD0 0x04 0x00 0x00 0x30 0x39
避坑指南: 我曾经遇到过一个设备,它要求List中的元素顺序必须严格按文档来。我少传了一个元素,设备直接返回了错误码。所以,写代码时一定要对照SEMI标准文档,别想当然。
2.3 流(Stream)与功能(Function)
流和功能,是SECS-II消息的“分类标签”。流是类别,功能是具体操作。
常见的流:
- Stream 1 (S1):设备状态与控制。比如S1F1是“设备ID查询”,S1F13是“建立通信”。
- Stream 2 (S2):设备控制与诊断。比如S2F41是“主机命令”。
- Stream 5 (S5):报警管理。比如S5F1是“报警报告”。
- Stream 6 (S6):数据收集与事件报告。比如S6F11是“事件报告”。
- Stream 7 (S7):工艺程序管理。比如S7F1是“工艺程序加载”。
- Stream 10 (S10):终端服务。比如S10F1是“终端消息”。
每个流下面有多个功能。比如S1F1,S1是流,F1是功能。F1通常是“请求”,F2是“响应”。所以S1F1是请求设备ID,S1F2是返回设备ID。
功能编号的约定:
| 功能编号 | 方向 | 说明 |
|---|---|---|
| 奇数 | 请求(Primary) | 主动发起的消息 |
| 偶数 | 响应(Secondary) | 对请求的回复 |
这个设计很合理。你发一个奇数功能的消息,对方必须回一个偶数功能的消息。这保证了通信的可靠性。
核心逻辑: 流和功能组合起来,唯一标识了一个消息类型。比如S1F1就是“查询设备ID”,S5F1就是“报告报警”。你在写代码时,只需要根据流和功能号,就能知道该解析什么数据。
2.4 事务处理(Transaction)
事务处理,是SECS/GEM通信的“会话机制”。一个事务由一对消息组成:一个请求和一个响应。
事务的生命周期:
- 主机发送一个请求消息(奇数功能),并分配一个事务ID。
- 设备收到后,处理请求,然后发送一个响应消息(偶数功能),使用相同的事务ID。
- 主机收到响应后,事务结束。
事务ID是4字节的整数,在消息头中。它用来匹配请求和响应。如果主机同时发了多个请求,设备返回的响应里会带上对应的事务ID,这样主机就知道哪个响应对应哪个请求了。
事务的超时处理:
在实际项目中,网络可能不稳定,设备可能死机。所以事务必须有超时机制。标准规定,主机发送请求后,必须在T3时间内收到响应。T3的默认值是45秒,但可以配置。
我的建议: 在实现事务管理时,用一个Map来存储待处理的事务。key是事务ID,value是请求信息和超时时间。用一个定时器线程定期检查超时。我曾经见过一个项目,事务ID用完了导致通信卡死,后来改成循环使用才解决。
多事务并发:
主机可以同时发起多个事务,只要事务ID不重复就行。设备会按顺序处理,但响应可能乱序返回。所以主机端必须根据事务ID来匹配响应。
举个例子,主机同时发了两个请求:
请求1: S1F1, 事务ID=100
请求2: S6F11, 事务ID=101
设备可能先处理完请求2,返回:
响应2: S6F12, 事务ID=101
然后才处理完请求1,返回:
响应1: S1F2, 事务ID=100
主机收到响应2时,一看事务ID是101,就知道这是请求2的响应,不会搞混。
避坑指南: 我曾经遇到一个设备,它不支持并发事务。我同时发了两个请求,它只处理了第一个,第二个直接忽略了。后来我查文档才发现,这个设备的事务队列深度只有1。所以,写代码前一定要确认设备的能力。
2.5 知识体系结构图
下面这张图,把SECS-II的核心概念串起来了。你可以看到,数据项是基础,消息格式是框架,流与功能是分类,事务处理是流程控制。
这张图你看懂了吗?数据项是砖块,消息格式是图纸,流与功能是房间编号,事务处理是施工流程。四者缺一不可。
好了,这一章的内容就到这里。SECS-II的消息结构,说白了就是一套严谨的“通信语法”。你只要掌握了数据项的编码、消息头的格式、流与功能的分类、事务的匹配,就能读懂任何SECS-II消息了。下一章,我们会用实际代码来演示如何解析和构造这些消息。