news 2026/10/21 3:17:28

电网101/104规约测试:报文解析与模拟主站实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
电网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 用解析工具拆一帧:贴上去、看字段、对点表

打开解析报文工具后,操作很直接:

  1. 在链路类型里选 104,端口相关参数不用改,工具默认按 APDU 解析。
  2. 把抓到的十六进制报文粘贴到输入框,点“解析”。
  3. 查看分层结果:链路层/APCI 区显示帧类型和序号,ASDU 区显示类型标识、传送原因、公共地址、信息体地址。
  4. 如果是遥测帧,切到数据视图,工具会按归一化、标度化、短浮点三种格式各算一版,方便你对照点表确认。

下面是一帧典型的 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 闭环验证:总召唤、单点遥控和意外报文测试

参数配好后,推荐按下面这个顺序做闭环验证:

  1. 新建通道,启动模拟主站,等待终端建立 TCP 连接。
  2. 在工具上发总召唤命令,确认收到终端的响应总召唤报文。
  3. 看完整收到的信息体数量,和点表总数对比。少了点先查链路窗口是否溢出。
  4. 发一条单点遥控预置命令,看终端回“激活确认”,再发执行命令。
  5. 对终端进行短接或模拟量输入变化,看工具是否收到 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 调小到 8,w_value 调到 6,让确认更频繁。回放速度也不要拉满,保持在每条报文间隔 50 毫秒到 100 毫秒。现场抓的报文经常带重复帧,回放前先做一次去重,否则会把序号推乱,后面所有帧都会被判为“意想不到的序号”。

5. 进阶技巧:把现场报文变成自动回归脚本

很多人在现场好不容易把链路调通、报文也正常了,拍个照就收工,结果下次改个程序又来一遍。我后来养成的习惯是:每次联调结束,把现场抓的报文存成文本文件,然后用脚本自动回放,做一次回归。

原理很简单:把模拟主站工具收到的上行报文按顺序记录下来,再用工具回放给终端,比对终端每一帧的响应是否符合预期。这个方法尤其适合验证终端程序升级后,原来的异常报文是否被正确处理。

下面是一个最小回放脚本,配合模拟主站工具的“TCP 回放”功能用:

import socket import time def replay_frames(host, port, frame_file, interval_ms=50): # 读取报文文件,每行一帧十六进制 frames = [] with open(frame_file, "r", encoding="utf-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), timeout=10) 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_ms=50) EOF EOF replay_frames("127.0.0.1", 2404, "captured_104.txt", interval_ms=50)

逻辑说明:脚本逐行读取报文文件,跳过空行和以 0x 开头的注释行,然后把每帧十六进制字符串转成字节数组,通过 TCP 通道按顺序注入模拟主站工具。间隔默认 50 毫秒,避免风暴式发送触发对方窗口保护。

参数说明:host指向运行模拟主站工具的机器,本机运行就是 127.0.0.1;port是工具里配置的监听端口;frame_file是现场保存的报文文件,建议每行一帧、不带换行外多余内容;interval_ms控制两帧间隔,终端处理能力弱就调到 100 以上。

用这套办法,我后来在模拟项目X里验证一个终端程序改动,把 2000 多帧历史报文全部回放完,只花了三分钟,抓出来一个“规约复位后首帧丢失”的隐蔽问题。从那以后,我每次测规约都强制走一遍“解析、回放、回归”这串流程,宁可在电脑前多花十分钟,也不去现场熬夜。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 1:25:08

Claude Code Prompt Cache 工程实践:上下文熵值调控与 token 节省

1. 这不是“缓存”&#xff0c;是 Claude Code 的推理成本控制中枢最近在几个技术社区里&#xff0c;频繁看到开发者讨论Claude Code 的 Prompt Cache——这个词听起来像传统 Web 开发里的 Redis 缓存&#xff0c;但实际完全不是一回事。我带团队落地过 3 个基于 Claude Code 的…

作者头像 李华
网站建设 2026/10/21 10:27:17

AI辅助论文写作的学术自主性实践指南

1. 这不是“AI代写”&#xff0c;而是把论文写作变成一场可控的协同实验“如何借助 AI 撰写一篇优秀的论文&#xff5c;Part Ⅴ”——看到这个标题&#xff0c;我第一反应不是点开&#xff0c;而是暂停三秒。因为过去两年里&#xff0c;我带过十几位不同学科背景的研究生和青年…

作者头像 李华
网站建设 2026/10/17 13:59:54

二叉树递归实战:从左叶子之和到路径总和的解题框架

二叉树这块&#xff0c;很多朋友学到“二叉树的进阶操作”就开始犯懵&#xff0c;尤其是遇到“左叶子之和”和“路径总和”这种一听名字就带着“递归味儿”的题目&#xff0c;往往看答案能看懂&#xff0c;自己一上手就不知道从哪开始写。这背后其实不是智商问题&#xff0c;而…

作者头像 李华
网站建设 2026/10/20 21:16:57

EmbeddingGemma 2 + Gemma 4:端侧RAG轻量级落地实践

1. 项目概述&#xff1a;为什么端侧 RAG 正在从“概念验证”走向“真实可用”最近在几个技术社区和开发者群聊里&#xff0c;反复看到一个组合词被高频提及&#xff1a;“EmbeddingGemma 2 Gemma 4”。它不像过去那些堆参数、拼算力的模型方案&#xff0c;而是带着一种明确的落…

作者头像 李华
网站建设 2026/10/15 5:20:36

慢SQL优化实战:从索引策略到执行计划的性能排查指南

凌晨一点&#xff0c;电话响了。业务方声音焦急&#xff1a;用户端查询订单列表的接口从平时的200毫秒变成了3秒&#xff0c;后台管理系统的月度报表查询更是跑了两分钟还没出结果。我打开慢查询日志&#xff0c;几条执行时间接近20秒的SQL赫然列在最前面。这就是典型的慢SQL问…

作者头像 李华
网站建设 2026/10/15 5:35:36

MySQL批量插入慢?JDBC必开参数rewriteBatchedStatements=true详解

很多人第一次遇到“批量插入很慢”这个问题时&#xff0c;第一反应都是换驱动、换数据库版本&#xff0c;甚至有人直接改表结构、上分区表。排查到最后&#xff0c;往往发现根因根本不是数据库不行&#xff0c;而是一个藏在 JDBC URL 里的开关没有打开——rewriteBatchedStatem…

作者头像 李华