ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

IEC101规约报文解析:从链路层到应用层的硬约束与实战避坑

IEC101规约报文解析:从链路层到应用层的硬约束与实战避坑

简介:面向电力自动化从业者与通信调试人员的IEC101通讯规约学习资料,聚焦2002版规约的报文流程与逐字节解析,帮助读者理解主站与RTU之间严格的一问一答机制、ACD位判断一级/二级数据召唤等关键差异,适合需要现场调试或规约开发的中级技术人员。压缩包内为1个doc文档,约121KB,系统梳理了握手请求链路状态、复位链路、总召唤全数据等完整流程,并给出十六进制报文实例,涵盖启动字符、控制域、链路地址、类型标识、传送原因、公共地址、信息体地址、CP56Time2a时标及校验和等字段说明。文中还对比97版与2002版在遥信、遥测、遥控、设点、电度基地址范围上的区别,并说明链路地址、公共地址、传送原因字节数可通过iec101规约配置表.ini配置。已有2782人学习,可作为规约报文对照与排错参考。

1. 一问一答背后的硬约束:IEC101 为什么必须“先确认再送数”

抓过 101 报文的人大多有过这种体验:抓包工具里一串10 49 01 4A 16、68 09 09 68 53 01 64 ...滚过去,看着像天书,可一旦某帧校验错、某个 ACD 位没盯住,整个总召唤就卡在那里,数据一条不上来。IEC101 通讯规约报文流程及解析这件事,本质上不是“背帧格式”,而是搞懂一条串行链路上两个设备如何用极少的字节建立起可信的问答秩序。2002 版与早期 97 版(ZD101)最关键的差异,就藏在“主站每发一个长帧,RTU 先回一个短帧链路层确认,再送被召唤的数据”这条铁律里——它把不可靠的串行物理层,硬生生约束成了严格的一问一答。搞懂这条约束为什么存在、报文里每个字节在替谁说话,你再看那些十六进制流,就不再是玄学。

2. 链路层:固定帧长如何撑起“握手—复位—召唤”的秩序

2.1 固定帧长的五个字段与校验的边界

IEC101 的链路层有两套帧:固定帧长和可变帧长。所有“打招呼”的动作都走固定帧长,它只有五个字段——启动字符10H、控制域、链路地址、校验和、结束字符16H。别小看这五个字节,它承担了链路状态探测、复位、以及“我这边有数据你快来取”的全部信令。

10 49 01 4A 16 │ │ │ │ └─ 结束字符 16H │ │ │ └────── 校验和 = 控制域 + 链路地址(算术和取低8位) │ │ └─────────── 链路地址(RTU 地址) │ └──────────────── 控制域 └───────────────────── 启动字符 10H

校验和只覆盖控制域和链路地址两个字节,不含启动与结束字符,这是固定帧长最容易翻车的地方:很多人写解析器时把10和16也算进去,结果永远对不上。校验算法是纯算术累加取低 8 位,不是 CRC,实现上就是两字节相加再& 0xFF。

控制域是整条链路的“语义中枢”。主站发起的帧 PRM 位为 1,表示启动站;RTU 回的帧 PRM 为 0,表示从动站。功能码四位决定这帧是“请求链路状态”“复位链路”还是“召唤数据”。以握手为例,主站发49,拆开看是 PRM=1、功能码=9(请求链路状态);RTU 回0B,PRM=0、功能码=11(链路完好)。复位时主站发40,功能码=0(复位远方链路),RTU 回00或20——这里就是全文最关键的伏笔:控制域为20时 ACD 位被置 1。

2.2 ACD 位:一个比特决定“现在取还是等会儿取”

ACD(Answer Control Data)位藏在从动站响应的控制域里,含义是“我有一级数据待上送”。一级数据是变位遥信、SOE 这类高优先级事件,二级数据是变化遥测这类低优先级量测。主站收到 ACD=1 的短帧确认后,不能直接等 RTU 把数据推上来——101 没有主动上报,必须由主站再发一帧“召唤一级数据”(控制域7A)去取。

这就是 2002 版严格一问一答的体现:RTU 想送数据,也只能先在一个短帧里把 ACD 置位当“信号旗”,等主站来问。常见的实现思路是,主站侧维护一个状态机,每次收到从动帧先判 ACD,再决定下一帧发7A(召唤一级)还是5A(召唤二级)。如果主站漏判 ACD,RTU 的一级数据就会一直压在缓冲区里,表现为“遥信变位迟迟不上来”,而链路看起来完全正常——这是现场排查里最耗时间的一类问题。

2.3 复位链路与初始化结束帧的时序

复位链路不是可有可无的礼节。主站发10 40 01 41 16后,RTU 回链路层确认;若确认帧控制域为20(ACD=1),主站必须紧接着召唤一级数据,RTU 才会送出初始化结束帧(类型标识0x46)。这个顺序不能颠倒:初始化结束帧意味着 RTU 已完成上电自检、准备好接受总召唤,主站没收到它就开始总召唤,很多 RTU 会直接丢弃总召唤命令。

时序上可以这样理解整条链路的启动契约:

阶段主站动作RTU 响应关键判据
握手请求链路状态49链路完好0B收到即链路通
复位复位链路40确认00/20控制域20则 ACD=1
取初始化召唤一级7A初始化结束0x46收到才可总召唤
总召唤总召唤64确认 + 数据逐帧确认

这张表看着简单,但每一步的“确认”都是独立的一次链路层交互,不是应用层应答。把链路层确认和应用层确认混为一谈,是解析 101 报文时最常见的认知错位。

3. 应用层:可变帧长里类型标识、传送原因与信息体地址怎么咬合

3.1 可变帧长的字段布局与长度字段的坑

可变帧长承载真正的业务数据,结构比固定帧长复杂得多:68H+ 长度 L + 长度 L(重复一次)+68H+ 控制域 + 链路地址 + 类型标识 + 可变结构限定词 + 传送原因 + 公共地址 + 信息体地址 + 信息元素 + 校验 +16H。

68 09 09 68 53 01 64 01 06 01 00 00 14 xx 16 │ │ │ │ │ │ │ │ │ │ │ │ │ └─ 结束 │ │ │ │ │ │ │ │ │ │ │ │ └──── QOI=14H 总召唤 │ │ │ │ │ │ │ │ │ │ │ └─────── 信息体地址 0000H │ │ │ │ │ │ │ │ │ │ └───────────── 公共地址 │ │ │ │ │ │ │ │ │ └──────────────── 传送原因 06H 激活 │ │ │ │ │ │ │ │ └─────────────────── 可变结构限定词 01H │ │ │ │ │ │ │ └────────────────────── 类型标识 64H 总召唤 │ │ │ │ │ │ └───────────────────────── 链路地址 │ │ │ │ │ └──────────────────────────── 控制域 53H │ │ │ │ └─────────────────────────────── 第二个 68H │ │ └──┴────────────────────────────────── 长度 L,重复两次 └──┴─────────────────────────────────────── 启动字符

长度 L 的定义是“从控制域到校验前一字节的字节数”,不含校验和结束符。它重复出现两次,是为了让接收方在丢失首字节时仍能靠第二个68H重新同步——这是串行链路上对抗误码的经典手法。解析时两个 L 必须相等,不等就说明帧被截断或串位,直接丢弃重来,别试图“猜”后半段。

3.2 可变结构限定词:一个字节同时编码“数量”和“是否带地址”

可变结构限定词(VSQ)是 101 里信息密度最高的一个字节。最高位 SQ 决定信息体是“连续地址”还是“每个信息体自带地址”,低 7 位是信息体个数。以遥信帧14 08 14 01 01 00 80 04 ...为例,类型标识0x14是成组单点遥信,VSQ=08表示 SQ=0、8 个信息体,每个信息体都带自己的地址。

SQ=0 时,每个信息体前都有 2 字节地址,适合地址不连续的场景;SQ=1 时,只有第一个信息体带地址,后续地址依次加一,适合连续排列的遥测。这个设计直接决定了报文长度:96 个遥测若用 SQ=1,能省下近 200 字节。现场遇到“遥测帧特别长、接近长度上限”时,先看 VSQ 的 SQ 位是不是被 RTU 设成了 0,这是厂家实现差异的高发区。

3.3 信息体地址与基地址:为什么遥测从 0701H 开始

信息体地址不是随便编的,它按信息类别划分基地址段。97 版和 2002 版的基地址范围不同,这是跨版本对接时最容易踩的坑:

类别97 版基地址2002 版基地址
遥信01H–400H01H–4000H
遥测701H–900H4001H–5000H
遥控B01H–B80H6001H–6100H
设点B81H–C00H6201H–6400H
电度C01H–C80H6401H–6600H

遥测帧里信息体地址01 07是小端序,实际值0701H,减去遥测基地址0701H得到遥测号 0。这个“地址减基地址得序号”的换算,是解析所有信息体的通用公式。如果主站按 2002 版基地址去解 97 版 RTU 的报文,遥测号会整体偏移,表现为“数据全串位”——数值对得上但挂错了点,比完全没数据更难查。

3.4 传送原因与 CP56Time2a:状态语义和时标的字节级拆解

传送原因是一字节(或两字节,看配置),06是激活、07是激活确认、08是停止激活、0A是激活结束、14是响应总召唤、03是突发。总召唤的完整闭环就是靠这几个值串起来的:主站发06,RTU 回07确认,数据帧带14,最后0A结束。少任何一个,主站状态机都可能停在“等待确认”。

CP56Time2a 是 7 字节时标,格式紧凑到每个 bit 都有用途:

# CP56Time2a 解析:7 字节,小端毫秒 + 分/时/日/月/年 def parse_cp56time2a(b): ms = b[0] | (b[1] << 8) # 毫秒,低字节在前 minute = b[2] & 0x3F # 低6位,高2位是 IV/SU 标志 hour = b[3] & 0x1F # 低5位 day = b[4] & 0x1F # 低5位,高3位是星期 month = b[5] & 0x0F # 低4位 year = b[6] & 0x7F # 低7位,2000 起算 return f"20{year:02d}-{month:02d}-{day:02d} {hour:02d}:{minute:02d}:{ms//1000:02d}.{ms%1000:03d}"

毫秒字段占两字节、低字节在前,这是 101 里少见的显式小端;分、时、日、月、年各自的高位被 IV(无效标志)、SU(夏令时)、星期等占用,解析时必须按位与掩码,直接取整字节会把标志位当成数值。对时命令里22 58就是毫秒0x5822,换算成 22562 毫秒,即 22.562 秒。时标解析错一位,SOE 的先后顺序就全乱了,事故追忆直接失去意义。

4. 报文流程实战:总召唤、对时、电度、遥控的逐帧推演

4.1 总召唤:从握手到结束帧的完整状态机

把前面所有机制串起来,一次总召唤的完整流程是这样的:握手(49→0B)→ 复位(40→00/20)→ 若 ACD=1 则召唤一级取初始化结束帧 → 总召唤(64,QOI=14)→ RTU 回链路层确认 → 主站召唤一级 → RTU 回总召唤确认(传送原因07)→ 主站继续召唤一级 → RTU 送遥信帧(类型0x14,传送原因14)→ 主站再召唤一级 → RTU 送遥测帧(类型0x15)→ 主站再召唤一级 → RTU 送总召唤结束帧(传送原因0A)。

注意这里每一次“召唤一级”都是主站主动发的10 7A 01 7B 16,RTU 不会连续推多帧。这就是 2002 版“严格一问一答”的落地形态:数据帧再多,也是一帧一确认地挤出来。DF8900 系统通过 RTU 参数表里的“全数据扫描间隔”(单位分钟)控制总召唤周期,DF1800/DF8002 则在程序里写死,一般 15 分钟一次。周期设太短,串行链路带宽被总召唤占满,变位遥信反而挤不进来;设太长,全数据刷新不及时。这个参数没有标准答案,得按现场信道速率和测点规模权衡。

4.2 电度召唤:先冻结后读取的两段式设计

电度召唤分两步,先冻结再读取,这是很多人第一次看会愣住的地方。冻结帧类型标识0x65、QCC=45,读全电度 QCC=05。为什么要分两步?因为电度是累计量,冻结动作让 RTU 把当前脉冲计数值锁存成一份快照,读取时拿的是同一时刻的一致值。如果边读边累加,主站拿到的 32 个电度可能分属不同时刻,做电量平衡计算时对不上账。

电度帧里每个信息体占 5 字节:4 字节电度值 + 1 字节描述信息。信息体地址从0C01H起,减基地址0C01H得电度序号。冻结和读取之间如果插入其他召唤,快照可能被覆盖,所以常见实现是把冻结、读确认、读数据、结束帧作为一个不可打断的序列处理。现场若发现电度值跳变异常,先查冻结与读取之间有没有被别的总召唤插队。

4.3 遥控:预置—反校—执行—撤销的四步闭环

遥控是 101 里唯一带“反校”的流程,类型标识0x2E(双点遥控)。主站先发预置(传送原因06,信息体值82表示预置控合),RTU 回激活确认(07)并把同样的值反送回来,主站比对反校值一致后才发执行(值02),RTU 再回执行确认。若中途要取消,发撤销(传送原因08),RTU 回停止激活确认(09)。

这个四步闭环的设计动机很直白:串行链路易受干扰,一个 bit 翻转就可能把“控合”变成“控分”。预置阶段只写缓冲区不出口,反校让主站确认 RTU 收到的命令与发出的完全一致,执行阶段才真正驱动出口继电器。跳过反校直接执行,等于把误动的风险全押在信道质量上,血泪经验告诉我们这条捷径不能走。遥控号换算同样是“信息体地址减遥控基地址”,0B03H减0B01H得 2 号遥控。

4.4 二级数据轮循:变化遥测怎么被“问”出来

平时主站轮循召唤二级数据(控制域7B),RTU 无变化时回09(无所请求数据),有变化遥测时回类型0x15的帧。二级数据只送变化量,不送全量,这是 101 在窄带串行链路上省带宽的核心策略。轮循周期要小于变化量的产生速率,否则变化会被覆盖;但也不能太快,否则空轮循把信道占满。这个平衡点和总召唤周期是同一类工程取舍,没有万能值。

5. 避坑与排查:那些让总召唤卡住的现场问题

5.1 校验和把启动/结束字符算进去,永远对不上

现象:解析器对每一帧都报校验错,但抓包看字节明明是对的。原因:固定帧长校验只覆盖控制域和链路地址,可变帧长校验覆盖从控制域到信息元素末尾,都不含10H/68H/16H。解决:把校验范围严格按帧类型切开,固定帧两字节、可变帧从控制域数到校验前一字节,写完拿一帧已知正确的报文回归验证。

5.2 漏判 ACD 位,一级数据永远上不来

现象:链路握手正常、总召唤确认也收到了,但变位遥信迟迟不刷新。原因:主站收到从动帧后没检查控制域 ACD 位,没发召唤一级命令,RTU 的一级数据一直压在缓冲区。解决:在接收状态机里把 ACD 判断做成强制分支,凡从动帧先判 ACD,置 1 就插一帧7A召唤一级,再回到原流程。

5.3 97 版与 2002 版基地址混用,数据整体串位

现象:遥测数值看着合理,但挂到了错误的点号上。原因:主站按 2002 版基地址(遥测4001H起)解析 97 版 RTU(遥测701H起)的报文,序号整体偏移。解决:在配置层显式标注规约版本,基地址表按版本加载,别用一套常量打天下。DF8900 可通过iec101 规约配置表.ini配置链路地址、公共地址、传送原因、信息体地址的字节数,DF8002/DF1800 则是固定 1/1/1/2,配置前先确认系统。

5.4 长度字段两个 L 不一致还硬解

现象:偶发解析出半截帧,后续字节全乱。原因:串行链路误码导致帧被截断,两个长度字节不等时仍继续解析。解决:两个 L 必须相等才进入解析,不等直接丢弃并重新同步到下一个68H,宁可丢一帧也不要解出错帧污染状态机。

5.5 遥控跳过反校直接执行

现象:偶发误动,复现困难。原因:预置后没比对反校值就发执行,信道干扰下 RTU 缓冲区里的值与主站意图不一致。解决:把“预置值 == 反校值”作为发执行的前置条件,不一致就发撤销,这条判断不能省。

6. 设计取舍:为什么 101 宁可啰嗦也不肯“聪明”一点

回头看整条链路,IEC101 的每一个“啰嗦”设计都有明确的代价交换。一问一答牺牲了吞吐,换来的是任何时刻链路上只有一个未决事务,状态机简单到可以用几十行代码写对;固定帧长只有五字节,牺牲了表达力,换来的是握手信令极低的解析成本;ACD 位把“有数据”和“送数据”拆成两步,牺牲了实时性,换来的是主站对链路节奏的完全掌控;遥控四步闭环牺牲了操作速度,换来的是误动概率的数量级下降。这些取舍的共同前提是:101 跑在窄带、易受扰、半双工的串行信道上,可靠性优先级高于一切。

验证一套 101 实现是否靠谱,我一般不看它能不能收数据,而是看三个边界:两个长度字段不等时是否丢弃、ACD 置位时是否插入召唤一级、遥控反校不一致时是否拒绝执行。这三处能正确处理,状态机的基本功就过关了。再进一步,可以拿本文的总召唤帧序列做回归用例,逐帧比对控制域、传送原因和 VSQ,任何一帧对不上就定位到具体阶段。

从那以后我每次对接新的 101 链路,都强制先把握手、复位、总召唤、对时、电度、遥控这六段报文各抓一遍存成基线,后面任何异常都拿基线做差分,比对着协议文档逐字猜快得多。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进