电网101/104规约测试:报文解析与模拟主站实战指南 简介一套面向电力系统自动化领域的规约测试工具组合专用于IEC 60870-5-101/104通信规约的调试与验证适合电网设备厂商、调试工程师及变电站运维人员使用。压缩包共169个文件、约21.62MB其中dat/cfg配置样本用于组织测试场景msg报文记录可用于回放解析dll动态库与exe主程序构成可直接运行的工具链涵盖单点遥信、双点遥信、远程升级、文件读取、定值设置等功能符合DL/T634.5104-2009实施细则。解析报文工具可协助逐帧解读101/104报文快速定位数据格式与通信异常模拟主站工具则支持远程下发控制命令、读取设备文件、修改定值等操作能有效验证子站设备的通信性能与规约实现适用于入网测试、产品研发和现场排故。资源已有796人学习下载是一份可落地的国网101/104规约测试实用资料。1. 电网101和104的规约测试先说服自己链路是通的再去讨论数据对不对在配电终端和主站的联调现场最怕的不是数据不对而是看不到链路在干什么。电网101和104的规约测试里抓包软件只能看到 TCP 层终端自带日志又只给你一个“链路中断”的结果中间那一大段 ASDU 就成了黑匣子。这套 101/104 测试软件刚好补上这段左边的解析报文工具把 101/104 帧拆到链路层、APCI、ASDU 三层右边的模拟主站测试工具能在自己电脑上跑起一个完整主站直接连终端做总召唤、遥控、单点测试。适合做设备入网验收、规约联调、异常报文复现的人。你要做的就是把终端接上来把报文贴进去看它到底回了什么。2. 报文解析工具把 104 帧拆到字节级2.1 101 和 104 的关系应用层同一套传输层两套实际调试里101 和 104 是 60870-5-101/104 规约族里用得最多的两个。101 走串口常见波特率 9600、19200帧结构带链路层控制域有主站轮询、从站应答的说法104 走以太网默认端口 2404应用层的内容和 101 基本一致只是用 APCI 换掉了链路层封装。先记住这个关系解析报文的时候才不容易绕晕无论哪条链路核心都是 ASDU差异只在外层怎么套。工具里常见的帧类型有两类。一类是 104 的 I 帧、S 帧、U 帧I 帧带发送序号和接收序号是真正装数据的S 帧只确认对方序号不带数据U 帧是 START、STOP、TESTFR 这类链路控制。另一类是 101 的链路层帧比如复位远方链路、请求链路状态这类命令回的是确认帧和总召唤激活回的数据报文不是一回事。初学者最容易把“链路复位”和“总召唤激活”混在一起其实前者是链路层命令后者是应用层命令一个管链路通不通一个管数据发不发。在真实调试里我习惯用“两层判断”法先在工具左边选好是 101 还是 104再贴报文。如果贴的是抓包软件拿到的十六进制串先找 0x68 启动字符再数长度字节。104 报文在网络包里大多数是 68 开头看到这个就可以先送进工具试一遍工具会自动判断是 I 帧、S 帧还是 U 帧。2.2 用解析工具拆一帧贴上去、看字段、对点表打开解析报文工具后操作很直接在链路类型里选 104端口相关参数不用改工具默认按 APDU 解析。把抓到的十六进制报文粘贴到输入框点“解析”。查看分层结果链路层/APCI 区显示帧类型和序号ASDU 区显示类型标识、传送原因、公共地址、信息体地址。如果是遥测帧切到数据视图工具会按归一化、标度化、短浮点三种格式各算一版方便你对照点表确认。下面是一帧典型的 104 总召唤报文68 0C 08 00 00 00 02 00 64 01 14 06 00 00 00 00 00 00 00为了让你看清工具背后到底在判断什么我写一个最小的解析脚本思路和工具完全一致def parse_104_apdu(frame_hex): data bytes.fromhex(frame_hex) if len(data) 6: return {error: 帧长度不够} # 第 0 字节启动字符 0x68 if data[0] ! 0x68: return {error: 不是 101/104 帧启动字符应为 0x68} # 第 1 字节APDU 长度不含启动字符和长度字节本身 apdu_len data[1] if len(data) ! 2 apdu_len: return {error: 帧长度和声明不一致抓包可能截断或粘包} # 第 2~5 字节APCI 控制域两个 16 位序号 control int.from_bytes(data[2:4], little) is_i_frame (control 0x01) 0 # 最低位为 0 是 I 帧 send_seq control 1 # I 帧的发送序号 # 从第 6 字节开始是 ASDU asdu data[6:] type_id asdu[0] # 类型标识决定后续数据体格式 cot int.from_bytes(asdu[2:4], little) # 传送原因2 字节 common_addr int.from_bytes(asdu[4:6], little) # 公共地址2 字节 ioa int.from_bytes(asdu[6:9], little) # 信息体地址3 字节 return { is_i_frame: is_i_frame, send_seq: send_seq, type_id: hex(type_id), cot: cot, common_addr: common_addr, ioa: ioa } frame 68 0C 08 00 00 00 02 00 64 01 14 06 00 00 00 00 00 00 00 print(parse_104_apdu(frame))逻辑说明这个脚本先校验启动字符和声明长度再读 APCI 里的控制域判断帧类型和发送序号最后把 ASDU 的几个关键字段拆出来。工具本身的判断流程也是这样只是把界面显示和点表配置做成了一体。参数说明type_id是 0x14代表总召唤cot是 0x0006代表“激活”表示这是一条主动下发命令common_addr是 0x0164 换算后的值对应你配置的公共地址ioa是全零因为总召唤的请求是针对全站的不指定具体信息体地址。你在工具里看到报错时先确认这四项和你配置的主站参数是不是对齐。2.3 类型标识、传送原因和公共地址三个字段卡住八成问题解析工具页面里必看的是下面这张表里的常用类型标识字节值类型说明0x01单点遥信一个 bit 表示一个开关量0x03双点遥信两个 bit 表示合/分/中间态0x09归一化遥测有符号整数需要乘系数才是物理量0x0B标度化遥测短整型同样要乘系数0x0D短浮点遥测浮点格式直接读数值0x14总召唤主站请求全数据0x2D单点遥控单个开关的合/分指令0x64电度量累计量传送原因常见的就几个0x06 激活、0x07 激活确认、0x0A 激活结束、0x14 响应总召唤、0x03 突发。你看到“激活确认”但后面没有数据说明从站认可命令但没带数据看到 0x03 突发的遥信报文说明是开关变位即时上报不是总召唤的周期数据。公共地址又叫厂站地址是 ASDU 区前两个字节小端序。解析工具里这里做得很细公共地址显示成十进制同时把“链路层地址”和“ASDU 公共地址”分开显示因为 101 串口里经常有人把两者配反104 里链路地址由 APCI 控制公共地址才是真正对账的参数。这一段如果对不上总召唤命令会石沉大海。提示拿到一帧报文先看公共地址和点表是不是一致再看传送原因最后才看数据体。顺序反了会越看越乱。3. 模拟主站测试工具把参数配对称终端才会乖乖回话3.1 主站模拟方式先想清楚自己是 Server 还是 Client模拟主站工具的核心是把它当作一个完整的主站连真实的终端设备按规约流程发命令、收响应。配之前先明确一个角色问题104 链路里主站通常是 TCP Server终端是 Client 主动连上来。默认监听端口是 2404如果现场拓扑是终端做 Server主站做 Client那要在通道类型里切到 TCP_CLIENT填终端的 IP 和端口。这一步选错了后面所有测试都白搭。我一般建议第一次联调先跑 104再跑 101。原因很简单104 链路层由 TCP 兜底你只要把顺序号和确认搞好数据就能通101 串口要自己处理 F 帧轮询、链路状态复位、从站待答超时多一层时序逻辑出错概率高。先用 104 把终端的 ASDU 收发逻辑验完再切 101 只查链路层问题问题面会小很多。模拟主站提供一个收发列表界面每一帧会显示发送/接收方向、帧类型、序号和 ASDU 摘要。你在这边点“总召唤”工具会把 0x14 类型标识、0x06 传送原因的报文发出去然后等终端的激活确认和全数据。调试时不要一次点一堆命令先发一条看清回应再发下一条。3.2 通道参数与超时配置一份可直接套用的模板模拟主站工具的配置项里最常用到的是通道参数、链路参数和召唤周期。下面这份配置是某项目的实测模板直接对应工具里的“通道设置”页{ channel: { type: TCP_SERVER, local_address: 0.0.0.0, port: 2404, t0: 30, t1: 15, t2: 10, t3: 20, k_value: 12, w_value: 8 }, link: { common_address: 1, max_asdu_len: 240 }, interrogation: { type_id: 20, cot: 6, periodic_seconds: 60 } }参数说明t0是 TCP 连接建立超时30 秒内终端连不上就报链路失败t1是发送方等待确认的时间发完一帧 I 帧后 15 秒没有收到 S 帧就认为丢帧t2是接收方延迟确认时间收到 I 帧后最多 10 秒必须回 S 帧t3是测试帧周期20 秒内没有任何收发就发 U 帧 TESTFR 探活。k_value是发送窗口最多 12 个未确认 I 帧w_value是接收确认窗口收到 8 个 I 帧必须回一次 S 帧。这组参数有个容易踩的点终端侧的超时参数必须和主站侧一致或大于主站侧。比如你 t3 设 20 秒终端 t3 设 10 秒那终端就会先断开链路。联调之前把两份配置摆在一起逐项对一遍比在报文里找原因快得多。3.3 闭环验证总召唤、单点遥控和意外报文测试参数配好后推荐按下面这个顺序做闭环验证新建通道启动模拟主站等待终端建立 TCP 连接。在工具上发总召唤命令确认收到终端的响应总召唤报文。看完整收到的信息体数量和点表总数对比。少了点先查链路窗口是否溢出。发一条单点遥控预置命令看终端回“激活确认”再发执行命令。对终端进行短接或模拟量输入变化看工具是否收到 0x03 突发的遥信/遥测。在测试过程中把工具左侧的“报文解析”和右侧的“在线监控”同时开起来。这样你发出去的帧和终端回来的帧都实时进解析器哪个字段不对当场就能看出来。比如遥控命令的 COT 是 0x06但终端回的是 0x0E 表示“未知信息体地址”那就是 IOA 配错了如果回 0x0F 表示“未知类型标识”那是 type_id 和终端不支持的数据类型对不上。工具把这些原因直接翻译成人话比对着裸报文猜省太多时间。4. 避坑规约调试中的五个高发问题4.1 现象链路建立了总召唤也回确认但几十秒后链路又断这是 104 联调里出现频率最高的问题。主站和终端的测试帧周期 t3 不一致或者主站侧没开周期测试帧。总召唤完成后通道长时间没有新数据主站也不发 TESTFR终端侧按自己的 t3 超时判定链路死了主动断开连接。解决方式把主站和终端的 t3 调成一致通常 20 秒同时在模拟主站工具里勾选“链路上无报文时自动发送测试帧”。这个后手能保证链路始终有活跃信号现场可以少掉七八成的“链路频繁中断”投诉。4.2 现象遥测解析出来是负数或者异常大数从站回了一帧 0x09 归一化遥测值看起来像-30000或是一个明显不可能的数值。这类问题根本原因是数据格式选错。某项目曾经把 0x0B 标度化值当成短浮点读结果电网频率 49.98 变成了 5.89e-05 这种莫名数字还有一次是把归一化值的符号位当成了符号扩展负温度变成 60000 多的假值。解决方式在解析工具里切数据格式视图每一帧同时看“归一化原始值”“标度化原始值”“短浮点值”三列和点表里的量纲对照。注意归一化和标度化都要乘系数系数在站端点表里有常见的是 0.01、0.001。不要相信工具默认系数一定拿一个已知量的实测数据校准。4.3 现象101 串口上帧发出去了没有回文先确认是不是真的发出去了串口工具里有没有看到 68 开头的字节。如果发了没回最常见的原因是链路层地址和 ASDU 公共地址配反。101 帧里链路层地址在帧的控制字段后面ASDU 公共地址在应用层里两个都叫“地址”但作用不同现场很多人填成一个值导致从站链路层认了地址ASDU 层又对不上结果就是链路层正常但没有应用层响应。解决方式先用 101 的“复位远方链路”命令如果这个命令能收到确认说明链路层地址对了问题在 ASDU 公共地址如果连这个命令都没回先查链路地址、波特率、串口号。用工具把这两个地址分开配置不要图省事填同一个值。4.4 现象信息体地址和点表对不上主站收到的数据量对但打开画面一看遥控点、遥信点张冠李戴。原因几乎都是信息体地址IOA从站起点和主站点表起点不一致。有的终端 IOA 从 0 开始有的从 1 开始有的把编号跳了区间主站点表还按连续地址配。解决方式在模拟主站工具的“点表映射”里设置 IOA 起始偏移或者要求终端提供完整的信息体地址表。测试时挑三个典型点首点、中间点、末点分别比对原始地址。三个点对得上整张表基本没问题。4.5 现象模拟主站回放报文时总是丢帧用工具回放历史报文时发送窗口超出 k 值导致对端丢弃或者本端没及时回 S 帧导致对方窗口阻塞。k 值太大、w 值太小是最常见的配置组合。解决方式把 k_value 调小到 8w_value 调到 6让确认更频繁。回放速度也不要拉满保持在每条报文间隔 50 毫秒到 100 毫秒。现场抓的报文经常带重复帧回放前先做一次去重否则会把序号推乱后面所有帧都会被判为“意想不到的序号”。5. 进阶技巧把现场报文变成自动回归脚本很多人在现场好不容易把链路调通、报文也正常了拍个照就收工结果下次改个程序又来一遍。我后来养成的习惯是每次联调结束把现场抓的报文存成文本文件然后用脚本自动回放做一次回归。原理很简单把模拟主站工具收到的上行报文按顺序记录下来再用工具回放给终端比对终端每一帧的响应是否符合预期。这个方法尤其适合验证终端程序升级后原来的异常报文是否被正确处理。下面是一个最小回放脚本配合模拟主站工具的“TCP 回放”功能用import socket import time def replay_frames(host, port, frame_file, interval_ms50): # 读取报文文件每行一帧十六进制 frames [] with open(frame_file, r, encodingutf-8) as f: for line in f: line line.strip() if line.startswith(0x) or not line: continue frames.append(bytes.fromhex(line)) s socket.create_connection((host, port), timeout10) print(f已连接 {host}:{port}共 {len(frames)} 帧) for i, frame in enumerate(frames): s.sendall(frame) time.sleep(interval_ms / 1000.0) print(f[{i 1}/{len(frames)}] 已发送 {len(frame)} 字节) s.close() if __name__ __main__: # 参数主站工具所在主机的地址、端口、报文文件路径 replay_frames(127.0.0.1, 2404, captured_104.txt, interval_ms50) EOF EOF replay_frames(127.0.0.1, 2404, captured_104.txt, interval_ms50)逻辑说明脚本逐行读取报文文件跳过空行和以 0x 开头的注释行然后把每帧十六进制字符串转成字节数组通过 TCP 通道按顺序注入模拟主站工具。间隔默认 50 毫秒避免风暴式发送触发对方窗口保护。参数说明host指向运行模拟主站工具的机器本机运行就是 127.0.0.1port是工具里配置的监听端口frame_file是现场保存的报文文件建议每行一帧、不带换行外多余内容interval_ms控制两帧间隔终端处理能力弱就调到 100 以上。用这套办法我后来在模拟项目X里验证一个终端程序改动把 2000 多帧历史报文全部回放完只花了三分钟抓出来一个“规约复位后首帧丢失”的隐蔽问题。从那以后我每次测规约都强制走一遍“解析、回放、回归”这串流程宁可在电脑前多花十分钟也不去现场熬夜。希望帮到你。本文还有配套的精品资源点击获取