SL 651-2014 是水文监测领域最核心的通信规约,所有水文测站的遥测终端(RTU)、数采仪、中心站接收软件,只要涉及数据上报与远程控制,报文格式最终都要落到这份标准上。本文按"架构 → 帧结构 → 链路模式 → 报文编码 → 功能码 → 工程落地"的顺序,把这份标准里真正会影响实现的部分梳理一遍。
一、这份标准管什么
SL 651-2014《水文监测数据通信规约》是水利行业标准,规定了水文监测系统中各级各类系统的设计与建设、以及相关设备生产制造应遵守的通信规约。标准第 4 章"总则"明确了三条边界:
- 各级各类水文监测系统的设计与建设,以及相关设备的生产制造,应符合本标准的规定;
- 智能传感器与遥测终端设备之间的接口及数据通信协议,应符合数据采集通信规约;遥测站与中心站之间的数据传输通信协议,应符合报文传输规约;
- 本标准未能详尽的其他水利数据采集、传输规约,可在本标准规定的框架下扩充;
- 涉及水文监测系统的仪器仪表设备制造,除符合本标准外,还应符合相应国家标准、行业标准的要求。
这里第一条非常重要:它管的不只是软件开发,还包括设备制造。所以对接中出现"设备厂商报的报文格式跟标准对不上"时,标准本身就是谈判依据。
一个常见的对照:HJ 212 管的是环保行业污染物在线监控,SL 651 管的是水文监测。两者报文风格相似(都是"帧起始符 + 地址 + 功能码 + 正文 + 校验"的字节帧,正文都用"标识符 + 数据"的信息组),但地址结构、功能码、要素标识符完全不同。做过 212 再切 651,最容易犯的错就是拿 212 的思维去套 651——尤其是功能码和标识符,一个都对不上。
二、两层协议不要混
这份标准的第 5 章和第 6 章,管的是两个完全不同的通信环节。
第 5 章 数据采集通信规约:智能传感器 ↔ 遥测终端设备(RTU)。
- 接口宜采用 RS-485/422、RS-232C、SDI-12 等通用接口标准;通信协议宜采用 Modbus-RTU 和 SDI-12 通信协议;
- 其他接口类水文仪器宜参照 GB/T 19705—2005。
其中 Modbus-RTU 的规定很具体:
- 通信波特率宜采用 1200、2400、4800、9600、19200 bit/s;
- 字节帧结构为 1 个起始位"0"、8 个数据位、1 个停止位"1"、无奇偶校验位(即标准 8N1);
- 数据帧基本格式为:地址(1 字节)+ 功能码(1 字节)+ 数据(不定长)+ 校验(2 字节);地址域在帧的开始部分,范围 0~255。
第 6 章 报文传输规约:遥测站 ↔ 中心站。这是 SL 651 的主体,也是本文后续要讲的部分。
两层协议里有一个容易忽略的细节——CRC 字节序不一致:
- 第 5 章的 Modbus 帧规定"除校验值外,其他数据传输顺序为高位字节在前、低位字节在后",即校验值不适用高位在前,这与经典 Modbus 的 CRC 低字节在前一致;
- 第 6 章的报文帧则明确要求"校验码高位字节在前、低位字节在后"。
同一套系统里,Modbus 层和 SL 651 层的 CRC 字节序是反的,收发两个方向都要按各自规则组帧。
三、帧基本单元与报文控制字符
3.1 帧基本单元
帧的基本单元是字节,每个字节包含 8 个数据位、1 个起始位"0"和 1 个停止位"1",无校验。也就是说,SL 651 报文帧跑在标准 UART 8N1 之上,链路的差错检测完全交给帧尾的 CRC。
3.2 控制字符语义
报文帧的控制字符定义如下,注意帧起始符在两种编码下不同:
| 控制字符代码 | 对应编码 | 功能 | 使用说明 |
|---|---|---|---|
| SOH | 01H | 帧起始 | ASCII 字符编码报文帧起始 |
| SOH | 7E7EH | 帧起始 | HEX/BCD 编码报文帧起始 |
| STX | 02H | 传输正文起始 | — |
| SYN | 16H | 多包传输正文起始 | 多包发送、一次确认的传输模式中使用 |
| ETX | 03H | 报文结束,后续无报文 | 作为报文结束符,表示传输完成,等待退出通信 |
| ETB | 17H | 报文结束,后续有报文 | 在报文分包传输时作为报文结束符,表示传输未完成,不可退出通信 |
| ENQ | 05H | 询问 | 作为下行查询及控制命令帧的报文结束符 |
| EOT | 04H | 传输结束、退出 | 作为传输结束确认帧报文结束符,表示可以退出通信 |
| ACK | 06H | 肯定确认、继续发送 | 作为有后续报文帧的"确认帧"报文结束符 |
| NAK | 15H | 否定应答、反馈重发 | 用于要求对方重发数据包的报文结束符 |
| ESC | 1BH | 传输结束、终端保持在线 | 下行确认帧代替 EOT 作为报文结束符,要求终端在线;保持在线 10min 内若没有接收到中心站命令,终端退回原先设定的工作状态 |
ETX 与 ETB 的区别是工程上最容易忽略的一点:ETB 表示"后面还有",收到 ETB 意味着这次通信不能断;ETX 表示"后面没有了",链路可以释放。同理,ACK 表示"收到、请继续发",ESC 表示"收到、你保持在线待命",EOT 才是"可以退出了"。
3.3 帧结构框架
标准分别规定了上行和下行两套帧结构框架,不管采用何种字符编码,都必须满足这个框架。传输顺序为高位字节在前、低位字节在后。
上行报文(遥测站 → 中心站)结构:
| 序号 | 名称 | 编码说明 |
|---|---|---|
| 1 | 帧起始符 | 01H / 7E7EH |
| 2 | 中心站地址 | 范围 1~255 |
| 3 | 遥测站地址 | — |
| 4 | 密码 | — |
| 5 | 功能码 | — |
| 6 | 报文上下行标识及长度 | — |
| 7 | 报文起始符 | STX / SYN |
| 8 | 包总数及序号 | 报文起始符为 SYN 时编入该组,其他情况下省略 |
| 9 | 报文正文 | — |
| 10 | 报文结束符 | ETB / ETX |
| 11 | 校验码 | 校验码前所有字节的 CRC 校验,生成多项式 X¹⁶+X¹¹+X²+1,高位字节在前,低位字节在后 |
下行报文(中心站 → 遥测站)结构:
| 序号 | 名称 | 编码说明 |
|---|---|---|
| 1 | 帧起始符 | 01H / 7E7EH |
| 2 | 遥测站地址 | — |
| 3 | 中心站地址 | 范围 1~255 |
| 4 | 密码 | — |
| 5 | 功能码 | — |
| 6 | 报文上下行标识及长度 | — |
| 7 | 报文起始符 | STX / SYN |
| 8 | 包总数及序号 | 报文起始符为 SYN 时编入该组,其他情况下省略 |
| 9 | 报文正文 | — |
| 10 | 报文结束符 | ENQ / ACK / NAK / EOT / ESC |
| 11 | 校验码 | 同上行说明 |
上下行的两个地址顺序是相反的:上行是"中心站地址在前、遥测站地址在后",下行是"遥测站地址在前、中心站地址在后"。这是实现解析器时第一个要对齐的地方,写反了会直接把地址解析错位。
四、地址与密码
4.1 遥测站地址
遥测站地址编码由 5 个字节构成(A5~A1),其中 A5 是高位字节、A1 是低位字节,分两种情况:
水文遥测站:采用 5 字节 BCD 码,首字节为 00,后 4 字节编码方法按照 SL 502 的规定执行。
其他遥测站:采用 5 字节混合编码,中心站解码时还原为 6 个字节 BCD 码。其中前三个字节 A5、A4、A3 采用 GB 2260—2007 规定的行政区划代码的前 6 位,A5 为省(自治区、直辖市)码,A4 为地(市、州、盟)码,A3 为县(市、区、旗)码;后 2 个字节 A2、A1 为遥测站地址自定义段,采用 HEX 码,中心站解码时还原为 3 个字节 BCD 码。每个县遥测站地址自定义段范围为 60000~65534,65535 为广播地址,0 为无效地址。
广播地址 65535 是查询类场景的关键:中心站要批量向某片区所有站下发命令时用得上。
地址编码部门应保证遥测站地址的唯一性。
4.2 密码
密码为 2 个字节 HEX 码,由中心站生成,中心站应能远程统一修改遥测终端密码。遥测终端应设定初始密码,入网后应及时更改。
五、四种链路传输模式
链路传输模式分四种,这是设计通信状态机的基础:
| 模式代号 | 模式种类 | 适用工作制式 | 适用信道 |
|---|---|---|---|
| M1 | 发送/无回答 | 自报式 | 单向或双向信道 |
| M2 | 发送/确认 | 自报式 | 双向信道 |
| M3 | 多包发送/确认 | 自报式 | 双向信道 |
| M4 | 查询/响应 | 查询应答式 | 双向信道 |
各模式的应用规定要点:
- M1:遥测站为通信发起端,发出报文后中心站不需响应。可用于发送单帧自报报文,包括测试报、均匀时段水文信息报、遥测站定时报、加报报、小时报、人工置数报;其上行报文结束符为 ETX,没有下行帧。
- M2:遥测站为发起端,中心站接收正确应发"确认"报文,接收无效则不响应。遥测站收不到响应报文应启动重发机制,最多重发 2 次。上行帧结束符为 ETB/ETX,下行帧为"确认"帧、结束符为 EOT/ESC。
- M3:通常情况下遥测站为发起端,连续发出多个报文包,中心站正确接收全部数据包仅回答 1 次确认报文;若有错误数据包,中心站应发包括错误数据包序列号(1 个错误包序列号,每包单独重发)的响应包,遥测站重发相应序列号包数据,最多重发 2 次。适用于发送多帧自报报文,包括图片信息报、均匀时段水文信息报、人工置数报等。
- M4:中心站为发起端,发出查询请求报文后,遥测站接收请求报文正确应发送响应帧;如遥测站接收请求报文无效则不响应。用于查询遥测站数据、设置(修改)遥测站运行状态参数、控制遥测站运行。下行帧为"查询/确认"帧、结束符为 ENQ/ACK/EOT;上行帧为响应帧,结束符为 ETB/ETX。
链路传输基本规则
- 包的字符之间无线路空闲间隔;两包之间的线路空闲间隔则应考虑信道网络延时、中间环节延时、终端响应时间、波特率等因素。两个数据包之间应至少等待一个线路空闲间隔。标准没有给出这个间隔的具体毫秒数,需要按现场信道特性实测确定——这是一个非常典型的"标准留白,靠联调"的地方。
- 自报式工作制式下,通信发起端是遥测站、接收端是中心站;查询应答工作制式下,发起端是中心站、接收端是遥测站。
- 发起端在规定时间内没有正确收到响应报文,应作为超时出错处理,超时出错后应启动重发机制。
- 数据重发由通信发起端控制,应重复 2 次;若连续 3 次超时,应退出通信,等待下次重新建立链路。
- 对于单向信道,遥测站发完报文即退出通信;对于双向信道,中心站负责控制是否退出通信链路。
- 中心站"确认"帧报文结束符是 ESC 时,遥测站应保持通信设备带电值守,随时响应中心站查询/设置命令;是 EOT 时,遥测站退出通信状态。
六、两种字符编码方式
SL 651 与很多同类规约不一样的地方在于:同一套帧结构,有两套字符编码。
6.1 ASCII 字符编码
ASCII 编码下,报文帧起始采用 SOH(01H)。以 M1/M2/M4 上行帧为例,各字段的传输字节数如下:
| 序号 | 名称 | 传输字节数 | 说明 |
|---|---|---|---|
| 1 | 帧起始符 | 1 | SOH |
| 2 | 中心站地址 | 2 | 1 字节 HEX 转换为 2 个 ASCII 字符传输,范围 1~255 |
| 3 | 遥测站地址 | 10 | 编码规则见 6.2.3.2,转换为 10 个 ASCII 字符传输 |
| 4 | 密码 | 4 | 2 字节 HEX 编码,转换为 4 个 ASCII 字符传输 |
| 5 | 功能码 | 2 | 1 个字节功能码转换为 2 个 ASCII 字符传输 |
| 6 | 报文上下行标识及长度 | 4 | 2 字节 HEX 编码转换为 4 个 ASCII 字符传输 |
| 7 | 报文起始符 | 1 | STX |
| 8 | 报文正文 | 不定长 | 自报数据、响应帧内容等 |
| 9 | 报文结束符 | 1 | ETX、ETB |
| 10 | 校验码 | 4 | 2 字节 HEX 编码转换为 4 个 ASCII 字符传输 |
M3 模式则采用 SYN 作为报文起始符,并在其后增加"包总数及序号"字段(6 个 ASCII 字符:前 3 个表示包总数、后 3 个表示本次发送数据包的序号,范围 1~4095,原码采用 HEX 码)。
6.2 HEX/BCD 编码
HEX/BCD 编码下,报文帧起始采用 7E7EH(两个字节)。对应字段的字节数显著缩短:
| 序号 | 名称 | 传输字节数 | 说明 |
|---|---|---|---|
| 1 | 帧起始符 | 2 | 7E7EH |
| 2 | 中心站地址 | 1 | 1 字节 HEX 码,范围 1~255 |
| 3 | 遥测站地址 | 5 | 见 6.2.3.2 |
| 4 | 密码 | 2 | 2 字节 HEX 码 |
| 5 | 功能码 | 1 | 1 字节 HEX 码,定义见附录 B |
| 6 | 报文上下行标识及长度 | 2 | 见下 |
| 7 | 报文起始符 | 1 | STX |
| 8 | 报文正文 | 不定长 | — |
| 9 | 报文结束符 | 1 | ETX、ETB |
| 10 | 校验码 | 2 | 2 字节 HEX |
换算规律很整齐:1 个字节 HEX 对应 2 个 ASCII 字符。中心站地址 1→2、遥测站地址 5→10、密码 2→4、功能码 1→2、报文上下行标识及长度 2→4、校验码 2→4。
6.3 "报文上下行标识及长度"怎么拆
这个 2 字节字段是最容易解析错的,标准给的拆法是:
- 高 4 位作上下行标识:0000 表示上行,1000 表示下行;
- 其余 12 位表示报文正文长度,即报文起始符之后、报文结束符之前的报文正文字节数,允许长度 0001~4095。
ASCII 编码下这个字段被拆成 4 个 ASCII 字符,其中第 1 个字符是上下行标识(0 表示上行、8 表示下行),其余 3 个字符是正文长度。
也就是说,方向位是长度字段的一部分,它不是独立字段——解析时如果直接把这 2 个字节当长度用,长度会平白多出 0x8000。
6.4 两套编码的语义差异
除了字节数,两套编码在数据处理上的要求也不同:
- ASCII 编码:报文帧结构中人工置数数据和图片数据采用原码传输,其他信息组编码均应采用 ASCII 字符传输;
- HEX/BCD 编码:报文信息组不管是 HEX、BCD 编码还是 ASCII 字符,均采用原编码传输。
这一点在人工置数与图片报(功能码 35H、36H)上体现最明显。
七、报文正文结构
7.1 基本结构
在 ASCII 或 HEX/BCD 编码下,报文正文结构一致,只是采用相应的编码编制报文。
上行报文正文结构:
| 序号 | 编码名称 | 编码结构 | 说明 |
|---|---|---|---|
| 1 | 流水号 | 2 字节 HEX 码,范围 1~65535 | — |
| 2 | 发报时间 | 6 字节 BCD 码,YYMMDDHHmmSS | — |
| 3 | 遥测站地址 | 地址标识符 + 遥测站地址 | 根据功能码定义选编 |
| 4 | 遥测站分类码 | 分类码标识符 + 遥测站分类码 | 根据功能码定义选编 |
| 5 | 观测时间 | 观测时间标识符 + 观测时间 | 根据功能码定义选编 |
| 6 | 要素(或参数)信息组 | 标识符 1 + 数据 1,标识符 2 + 数据 2,…… | 见附录 C 和附录 E |
下行报文正文结构:流水号、发报时间、遥测站地址、要素(参数)——其中要素由一组或多组命令参数组成。
7.2 ASCII 编码的正文格式规则
ASCII 编码下,正文信息组由要素(参数)标识符与相应数据构成,标识符在前、数据在后,各要素(参数)标识符、数据之间均用"空格"作为分隔符,即:
要素标识符 空格 数据 空格 要素标识符 空格 数据
两条容易被忽略的补充规定:
- 流水号及发报时间组后不带空格分隔符;
- 报文正文最后的 1 个空格不应省略。
第二条是极阴的坑:报文末尾那个看似多余的空格,是格式的一部分。很多终端在拼接正文时做了 trim,结果中心站按空格分割元素时少了一项。
7.3 各类数据的编码规则
- 要素标识符采用附录 C 规定的"标识符 ASCII 码";
- 遥测站基本参数、运行参数标识符采用附录 D 中标识符引导符并转换为 ASCII 码;
- 数据采用 HEX 码、整型数或十进制浮点数;
- 非字符型数据(图片数据除外)应转换为 ASCII 字符传输。
7.4 流水号规则
流水号表示发送报文的顺序,规则较为精细:
- 上行报文流水号在 01~65535 之间循环;
- 确认帧下行报文的流水号与上行报文相同;
- 由中心站发起的下行报文流水号为 0;
- 重发报文使用原报文流水号;
- 报文正文分包传输时只有第 1 个包有流水号。
最后两条组合起来,直接影响服务端的幂等设计:确认帧和重发帧会复用同一个流水号,因此 流水号单独不能作为唯一键。实践中更稳妥的幂等键是 (遥测站地址, 发报时间, 功能码, 流水号) 的组合。
7.5 发报时间
发报时间表示发送报文的时间,在发送报文时取实时钟,由年、月、日、时、分、秒组成,编码格式为 YYMMDDHHmmSS:
- YY 表示年份,2 位数字,取值 00~99;
- MM 表示月份,2 位数字,取值 01~12;
- DD 表示日期,2 位数字,取值 01~31;
- HH 表示小时,2 位数字,取值 00~23;
- mm 表示分钟,2 位数字,取值 00~59;
- SS 表示秒,2 位数字,取值 00~59。
年份是 2 位——这决定了系统的时间窗口是 2000~2099。做历史数据入库时要注意两位年份的世纪补全逻辑。
八、功能码体系
功能码定义见附录 B,按方向天然分成三段:
| 功能码 | 应用功能描述 | 说明 |
|---|---|---|
| 00H~2EH | 保留 | 扩展功能码 |
| 2FH | 链路维持报 | 遥测站定时向中心站发送链路维持信息 |
| 30H | 测试报 | 报送实时数据 |
| 31H | 均匀时段水文信息报 | 报送等时间间隔数据 |
| 32H | 遥测站定时报 | 报送由时间触发的实时数据 |
| 33H | 遥测站加报报 | 报送由时间或事件触发的加报实时数据 |
| 34H | 遥测站小时报 | 报送以小时为单位的历史数据和实时数据 |
| 35H | 遥测站人工置数报 | 报送人工置数 |
| 36H | 遥测站图片报或中心站查询遥测站图片采集信息 | 查询/报送 JPG 图片信息 |
| 37H | 中心站查询遥测站实时数据 | — |
| 38H | 中心站查询遥测站时段数据 | 以小时为单位查询历史数据 |
| 39H | 中心站查询遥测站人工置数 | — |
| 3AH | 中心站查询遥测站指定要素数据 | — |
| 3BH~3FH | 保留 | 扩展功能码 |
| 40H | 中心站修改遥测站基本配置表 | 遥测站基本配置 |
| 41H | 中心站读取遥测站基本配置表/遥测站自报基本配置表 | — |
| 42H | 中心站修改遥测站运行参数配置表 | 遥测站运行参数配置 |
| 43H | 中心站读取遥测站运行参数配置表/遥测站自报运行参数配置表 | — |
| 44H | 查询水泵电机实时工作数据 | — |
| 45H | 查询遥测终端软件版本 | — |
| 46H | 查询遥测站状态和报警信息 | — |
| 47H | 初始化固态存储数据 | 应与标识符配合使用以提高安全性 |
| 48H | 恢复终端出厂设置 | 应与标识符配合使用以提高安全性 |
| 49H | 修改密码 | — |
| 4AH | 设置遥测站时钟 | — |
| 4BH | 设置遥测终端 IC 卡状态 | — |
| 4CH | 控制水泵开关命令/水泵状态信息自报 | — |
| 4DH | 控制阀门开关命令/阀门状态信息自报 | — |
| 4EH | 控制闸门开关命令/闸门状态信息自报 | — |
| 4FH | 水量定值控制命令 | — |
| 50H | 中心站查询遥测站事件记录 | — |
| 51H | 中心站查询遥测站时钟 | — |
| 52H~DFH | 保留 | 扩展功能码 |
| E0H~FFH | 用户自定义扩展区 | — |
分段的规律值得记一下:3X 段是遥测站"说话"(自报、上报),4X 段是中心站"下命令"(查询、配置、控制、校时),5X 段仍以下行查询为主。47H、48H 这两条危险命令,标准专门要求"应与标识符配合使用以提高安全性"——意思是执行前要有二次确认参数,不能一条命令就把设备清空或恢复出厂。
九、要素标识符与分类报文格式
9.1 标识符引导符体系
要素标识符不是裸的 ASCII 字符串,而是"引导符(1 字节 HEX)+ 标识符 ASCII 码"的组合。附录 C 的汇总表节选:
| 引导符 | 标识符 ASCII 码 | 编码要素 | 最大和单位 |
|---|---|---|---|
| F0H | TT | 观测时间引导符 | — |
| F1H | ST | 测站编码引导符 | — |
| F2H | RGZS | 人工置数 | — |
| F3H | PIC | 图片信息 | KB |
| F4H | DRP | 1h 内每 5min 时段雨量 | 0.1mm |
| F5H | DRZ1 | 1h 内 5min 间隔相对水位 1 | 0.01m |
| F6H~FCH | DRZ2~DRZ8 | 1h 内 5min 间隔相对水位 2~8 | 0.01m |
| FDH | DATA | 流速批量数据传输 | m³ |
| 01H | AC | 断面面积 | — |
| 02H | AI | 瞬时气温 | ℃ |
| 03H | C | 瞬时水温 | ℃ |
| 04H | DRxnn | 时间步长码 | min |
| 05H | DT | 时段长、降水、引排水、抽水历时 | h, min |
| 06H | ED | 日蒸发量 | mm |
| 07H | EJ | 当前蒸发 | mm |
| 08H | FL | 气压 | 百帕 |
| 09H | GH | 闸坝、水库闸门开启高度 | m |
| 0AH | GN | 输水设备、闸门(组)编号 | — |
| 0BH | GS | 输水设备类别 | — |
| 0CH | GT | 水库、闸坝闸门开启孔数 | 孔 |
| 0DH | GTP | 地温 | ℃ |
| 0EH | H | 地下水瞬时埋深 | m |
| 0FH | HW | 波浪高度 | m |
| 10H | M10 | 10cm 处土壤含水量 | % |
| 11H | M20 | 20cm 处土壤含水量 | % |
两个细节值得注意:
- DRZ1~DRZ8 是"同一个要素的多组数据":1h 内 5min 间隔相对水位共有 12 个时段,每个水位要素占 24 字节 HEX(每组 2 字节 HEX,分辨率 0.01m)。对河道站表示河道水位,对闸坝(泵)站分别表示闸(站)上水位或闸(站)下水位。
- F4H DRP 用 12 字节 HEX 编码 1h 内 12 组 5min 雨量,每组 1 字节,最大值 25.4mm,数据中不含小数点,FFH 表示非法数据。用 FFH 做哨兵值是这类定长批量编码的通用做法,解析时必须先判 FFH 再换算。
9.2 分类报文格式:以降水量为例
附录 E 按站类/报文类型给出了每类报文的完整编码格式,表 E.2 是降水量编码格式,共 13 项:
| 序号 | 编码名称 | 编码结构 | 说明 |
|---|---|---|---|
| 1 | 流水号 | 2 字节 HEX 码,范围 1~65535 | — |
| 2 | 发报时间 | 6 字节 BCD 码,YYMMDDHHmmSS | — |
| 3 | 遥测站地址 | 地址标识符 + 遥测站地址 | 编码规则见 6.2.3.2 |
| 4 | 遥测站分类码 | 降水类 | — |
| 5 | 观测时间 | 观测时间标识符 + 观测时间 | 5 字节 BCD 码,YYMMDDHHmm |
| 6 | 降水量 | 降水量标识符 + 降水量 | 十进制浮点数,小数点后保留 1 位;可同时编排多组 |
| 7 | 降雨历时 | 降雨历时标识符 + 降雨历时 | HH.mm;暴雨加报时选编 |
| 8 | 降水量累计值 | 降水量累计值标识符 + 降水量累计值 | 编码中有降水量时 |
| 9 | 蒸发量 | 蒸发量标识符 + 蒸发量 | 选编 |
| 10 | 风向 | 风向标识符 + 风向 | 1~16;选编 |
| 11 | 风速 | 风速标识符 + 风速 | 选编 |
| 12 | 其他信息 | 要素标识符 + 要素值 | 选编 |
| 13 | 电源电压 | 电源电压标识符 + 遥测站工作电压 | 十进制浮点数,小数点后保留 2 位;通常报蓄电池电压 |
注意观测时间在这里是 5 字节 BCD(YYMMDDHHmm),没有秒,而发报时间是 6 字节带秒——两个时间字段精度不同,别搞混。
河道水文信息(附录 E.2)则可包括降水量、蒸发量、水位、流量、波浪高度、水温、风速、风向、气温、气压等,并规定:
- 水位应以 m 计,宜记至 2 位小数,特殊要求可记至 3 位;
- 流量以 m³/s 计,最多记至 3 位小数;
- 流速以 m/s 计,最多记至 2 位小数;
- 波浪高度以 m 计,最多记至 2 位小数;
- 水温以 ℃ 计,记至 1 位小数。
精度约定必须在对接文档里写死,否则同一水位值在不同厂商设备上会出现 0.01m 与 0.001m 混报。
十、CRC 校验的实现
标准对校验码的规定是:校验码为校验码前所有字节的 CRC 校验,生成多项式为 X¹⁶+X¹¹+X²+1,高位字节在前、低位字节在后。
生成多项式 X¹⁶+X¹¹+X²+1 展开为二进制是 1 0000 1000 0000 0101,即 0x8005。
Java 参考实现:
/** SL 651 报文校验码计算:校验码之前的所有字节,高位字节在前 */
public static int crc16(byte[] frame, int offset, int length) {
int crc = 0x0000;
for (int i = offset; i < offset + length; i++) {
crc ^= (frame[i] & 0xFF) << 8;
for (int b = 0; b < 8; b++) {
if ((crc & 0x8000) != 0) {
crc = (crc << 1) ^ 0x8005;
} else {
crc <<= 1;
}
crc &= 0xFFFF;
}
}
return crc;
}
/** 组帧:校验码高位字节在前、低位字节在后 */
static void appendCrc(java.io.ByteArrayOutputStream out, byte[] frame) {
int c = crc16(frame, 0, frame.length);
out.write((c >> 8) & 0xFF);
out.write(c & 0xFF);
}
需要提醒的是:标准只给出了生成多项式,没有规定初值、是否位反转以及是否做结果异或。上例采用工程中常见的初值 0x0000、不做反转、不做结果异或。这个约定必须与对接方书面确认——不同初值算出来的 CRC 完全不同,现场"报文看着对但中心站一直 NAK"十有八九就是这里没对齐。
十一、标准里的一处内部矛盾
翻到 6.5 节时会发现一处对不上的地方,值得记一下。
"报文上下行标识及长度"这个字段,在表 20 中定义为 2 字节 HEX 码,高 4 位为上下行标识、其余 12 位为报文正文长度,允许长度 0001~4095;表 16 对 ASCII 编码的表述一致;表 23(HEX/BCD M3 下行)也明确写"见表 20 说明"。
但表 22(HEX/BCD M3 上行)同一行的说明写的是"允许长度为 0001H~FFFFH"。
FFFFH = 65535 已经超出 12 位所能表达的范围,且与表 20、表 16、表 23 三处定义都不一致。该字段只有 2 个字节,扣掉高 4 位方向标识后只剩 12 位,上限只能是 4095(0FFFH)。所以表 22 的 FFFFH 应为笔误,实现时应以 4095 为界。
这个坑的代价不低:如果按 FFFFH 去设计缓冲区或做长度校验,遇到异常报文时可能被诱导分配超大缓冲区,或者放过本该判定为非法的长度值。
十二、工程落地要点
把上面这些规定收拢成一份 checklist:
- 上下行报头的两个地址顺序相反,解析器不能复用同一段偏移逻辑;
- ETB/ETX 与 ACK/ESC/EOT 的语义决定链路是否释放,状态机要按此设计,不能用"收到报文即断开"的简化实现;
- ASCII 编码正文末尾的空格不能省略,拼接正文时不要 trim;
- "报文上下行标识及长度"的高 4 位是方向位,不能直接当长度使用;正文长度上限 4095,不是 65535;
- 超时重发 2 次、连续 3 次超时退出链路,超时阈值需按信道特性实测标定,标准未给具体值;
- 确认帧与重发帧复用原流水号,幂等键建议用
(遥测站地址, 发报时间, 功能码, 流水号); - 分包传输只有第 1 个包带流水号,服务端重组时要按"包总数及序号"补齐顺序,不能依赖流水号排序;
- 发报时间为 2 位年份,注意世纪的补全与时间窗口(2000~2099);
- 观测时间与发报时间精度不同(前者 5 字节到分、后者 6 字节到秒),落库字段要分开;
- DRP、DRZ 这类定长批量编码用 FFH 表示非法数据,解析时先判哨兵值再换算,且注意"数据中不含小数点";
- CRC 的初值、字节序必须与对接方书面确认,并注意 SL 651 层与 Modbus 层的校验字节序相反;
- 密码入网后必须修改,47H/48H 这类高风险命令要按标准要求配合标识符做二次确认。
十三、小结
SL 651-2014 的结构其实很清晰:一层是传感器到 RTU 的 Modbus/SDI-12,一层是 RTU 到中心站的报文传输;报文层用统一的字节帧框架,通过"两种字符编码 + 一套功能码 + 一套要素标识符"覆盖了从雨量、水位、流量到闸门控制、图片传输的全部业务。
真正花时间的不是读懂帧结构,而是把这些"魔鬼细节"对齐:地址顺序、方向位、末尾空格、流水号复用、CRC 初值、两类时间精度、FFH 哨兵值。这些点在标准里都有明文规定,但散落在不同章节和附录里,只有在现场被 NAK 过几轮之后才会真正记住。
关于作者
长期从事水利、水务及生态环境领域的信息化系统研发,主要技术栈为 Java / Spring Boot,熟悉水文监测数据的采集、传输、存储与展示全链路。
在通信规约方面,熟练使用 SL 651-2014《水文监测数据通信规约》 与 HJ 212《污染物在线监控(监测)系统数据传输标准》,具备遥测终端接入、报文解析、分包重组、CRC 校验、指令下发与远程配置的完整落地经验,对 SL 502 站码体系、GB 2260 行政区划编码、Modbus-RTU / SDI-12 传感器接入亦有实践。
如果你正在做水文测站的数据接入、中心站平台建设,或者被某个厂商的报文格式卡住,欢迎交流。