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消息由三部分组成:

  1. 消息头(Message Header):固定10字节,包含设备ID、流、功能、事务ID等信息。
  2. 数据体(Data Body):由数据项组成的树形结构。
  3. 消息尾(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通信的“会话机制”。一个事务由一对消息组成:一个请求和一个响应。

事务的生命周期:

  1. 主机发送一个请求消息(奇数功能),并分配一个事务ID。
  2. 设备收到后,处理请求,然后发送一个响应消息(偶数功能),使用相同的事务ID。
  3. 主机收到响应后,事务结束。

事务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 消息 数据项(Data Item) 消息格式(Message Format) 流(Stream)与功能(Function) 事务处理(Transaction) 类型码 长度编码 消息头 数据体 流编号 功能编号 事务ID 超时机制

这张图你看懂了吗?数据项是砖块,消息格式是图纸,流与功能是房间编号,事务处理是施工流程。四者缺一不可。

好了,这一章的内容就到这里。SECS-II的消息结构,说白了就是一套严谨的“通信语法”。你只要掌握了数据项的编码、消息头的格式、流与功能的分类、事务的匹配,就能读懂任何SECS-II消息了。下一章,我们会用实际代码来演示如何解析和构造这些消息。


专注资料整理