4、SECS消息解析:Message结构、Stream与Function、Item数据类型、编码与解码
各位工程师,今天我们来聊聊SECS消息解析。说实话,这是整个设备数据采集中最核心的一环。你想想看,设备吐出来的数据就是一堆二进制流,你得知道它说了什么,对吧?
我在刚入行那会儿,第一次看到SECS报文,满屏的十六进制,头都大了。后来跟着老工程师一点点啃规范,才明白这东西其实有章可循。今天我就把这块掰开了讲给你听。
4.1 Message结构:报文长什么样?
一条SECS消息,说白了就是一个数据包。它由两部分组成:消息头和消息体。
消息头固定10个字节,里面包含了设备ID、控制码、Stream号、Function号、等待位这些关键信息。消息体则是真正的数据内容,由Item嵌套组成。
我习惯把消息头比作快递单,消息体比作包裹里的东西。快递单告诉你从哪来、到哪去、什么类型,包裹里才是你真正要的东西。
消息头结构(10字节)
- 字节0-1:设备ID(Device ID)
- 字节2:控制码(Control Code)
- 字节3:Stream号
- 字节4:Function号
- 字节5:等待位(W-Bit)
- 字节6-9:系统字节(System Bytes)
举个例子,一条S1F1消息,Stream=1,Function=1,这是最基础的通信握手消息。设备收到S1F1,必须回复S1F2。嗯,这里要注意,如果设备不回,说明通信链路有问题。
4.2 Stream与Function:消息的身份证
Stream和Function,合起来叫SF码。每个SF码代表一种特定的消息类型。Stream是类别,Function是具体操作。
我个人习惯把Stream比作书的章节,Function比作具体段落。比如:
- S1F1:通信请求(Are you there?)
- S1F2:通信响应(I'm here)
- S6F11:事件报告发送(设备上报数据)
- S6F12:事件报告确认(主机确认收到)
我在项目中遇到过最坑的事,就是有人把S6F11和S6F12搞反了。设备发了报告,主机却回了S6F11,结果设备一直等确认,生产线直接卡死。所以,SF码一定要记清楚。
小技巧:Stream号奇数表示主机发起的消息,偶数表示设备发起的消息。Function号奇数表示请求,偶数表示响应。比如S1F1是主机请求,S1F2是设备响应。
4.3 Item数据类型:数据的基本单元
消息体里的数据,是由Item组成的。每个Item就是一个数据单元,包含类型、长度和值三部分。
SECS定义了多种数据类型,我列个表给你看:
| 类型码 | 名称 | 说明 | 字节数 |
|---|---|---|---|
| 0x00 | List | 列表,可包含多个子Item | 不定 |
| 0x10 | Binary | 二进制数据 | 1字节 |
| 0x20 | Boolean | 布尔值(0/1) | 1字节 |
| 0x30 | ASCII | 字符串 | 1字符 |
| 0x40 | I8 | 8位有符号整数 | 1字节 |
| 0x50 | I1 | 1位有符号整数 | 1字节 |
| 0x60 | I2 | 2字节有符号整数 | 2字节 |
| 0x70 | I4 | 4字节有符号整数 | 4字节 |
| 0x80 | F8 | 8字节浮点数 | 8字节 |
| 0x90 | F4 | 4字节浮点数 | 4字节 |
| 0xA0 | U8 | 8位无符号整数 | 1字节 |
| 0xB0 | U1 | 1位无符号整数 | 1字节 |
| 0xC0 | U2 | 2字节无符号整数 | 2字节 |
| 0xD0 | U4 | 4字节无符号整数 | 4字节 |
你可能会问,为什么有这么多整数类型?说白了,就是为了节省带宽。设备上报温度,用U1就够了,没必要用U4。我曾经见过有人不管什么数据都用U4,结果报文体积大了好几倍,网络拥堵得一塌糊涂。
4.4 编码与解码:二进制到数据的转换
编码就是把数据变成二进制流,解码就是反过来。SECS的编码规则其实很简单,就是类型码+长度+数据。
举个例子,编码一个ASCII字符串"ABC":
- 类型码:0x30(ASCII)
- 长度:0x03(3个字符)
- 数据:0x41 0x42 0x43(A、B、C的ASCII码)
所以最终二进制流就是:30 03 41 42 43
再复杂一点,编码一个List,里面包含一个整数和一个字符串:
List (0x00 0x02) // 列表,包含2个元素
├── I4 (0x70 0x04 0x00 0x00 0x00 0x64) // 整数100
└── ASCII (0x30 0x03 0x41 0x42 0x43) // 字符串"ABC"
解码就是反过来,先读类型码,再读长度,最后读数据。如果类型码是List,就递归解析子Item。
注意:长度字段的编码方式有两种:短格式(1字节)和长格式(3字节)。当长度小于256时用短格式,否则用长格式。我曾经踩过这个坑,解析时没判断长度格式,结果数据全乱了。
4.5 实战:手写一个简单的解码器
光说不练假把式。我写个简单的Python解码函数,你感受一下:
def decode_secs_item(data, offset):
"""解码一个SECS Item"""
# 读取类型码
type_byte = data[offset]
offset += 1
# 判断长度格式
if data[offset] & 0x80: # 长格式
length = ((data[offset] & 0x7F) << 16) | (data[offset+1] << 8) | data[offset+2]
offset += 3
else: # 短格式
length = data[offset]
offset += 1
# 根据类型码解析数据
if type_byte == 0x00: # List
items = []
for _ in range(length):
item, offset = decode_secs_item(data, offset)
items.append(item)
return items, offset
elif type_byte == 0x30: # ASCII
return data[offset:offset+length].decode('ascii'), offset + length
elif type_byte == 0x70: # I4
value = int.from_bytes(data[offset:offset+4], 'big', signed=True)
return value, offset + 4
# ... 其他类型类似
这段代码虽然简单,但核心逻辑都在了。实际项目中,你还要处理各种边界情况,比如数据截断、类型不匹配等。
4.6 知识体系总览
说了这么多,我画张图帮你理清思路:
这张图把SECS消息解析的脉络都串起来了。从消息头到消息体,从Item类型到编解码,每一步都有章可循。
我的建议:刚开始接触SECS解析时,别急着写代码。先拿一个真实的报文,用手工方式逐字节解析一遍。我当年就是这么干的,虽然慢,但理解特别深。等你把几个典型报文都手解过一遍,写代码就是水到渠成的事。
好了,SECS消息解析这块就讲到这里。记住,核心就是三件事:看懂消息头、理解Item嵌套、掌握编解码规则。把这三点吃透了,设备数据采集的大门就算真正打开了。