简介:面向电力系统自动化领域的IEC 104规约客户端与服务端仿真工具,可供规约开发、联调测试及SCADA集成人员使用。工具内含SCADA客户端模拟和RTU/保护设备服务端模拟,支持遥测、遥信、遥控、遥调等数据交互,覆盖ASDU(A格式)与U-格式传输,便于验证接口稳定性、断线重连、错误处理及流量控制等通信场景。压缩包共137个文件,约14.55MB,主要包含可直接运行的exe和依赖dll、Visual C++工程与源码(cpp/h)、工程配置(dsp/dsw)以及编译中间文件(obj/pdb)等,既可直接运行演示,也可借助源码深入理解协议栈实现。目前已有710人学习/下载。借助这套工具,开发者既能快速搭建IEC 104仿真环境进行功能、压力与性能测试,也能结合源码学习规约细节,提升实际系统的可靠性与开发效率。
1. IEC 104客户端/服务端仿真工具:就是电力调度的“自测沙盒”
调试电力远动的人都知道,IEC 104客户端/服务端仿真工具是那种「平时不起眼、联调时救命」的软件。它相当于把一个完整的主站(客户端)和一个完整的从站(服务端)塞进同一台电脑里,你可以一边模拟调度主站发总召,一边模拟厂站终端上送遥信遥测,两边报文实时对撞,问题当场现形。适合三类人:写采集程序的开发者、做系统联调的现场工程师、还有刚接触远动规约、想搞明白总召和变化上送到底怎么走的新人。它解决的核心问题就一个:在没有真实设备的情况下,把IEC 104这条链路完整跑起来,并且让你看得见每一帧报文。
2. 上手前的准备:运行依赖、界面分区与启动链路
2.1 运行依赖与解压目录
这类仿真工具大多做成绿色软件包,解压即用,但有两个前提条件要先确认:操作系统位数和运行库。我手上的这个工程包是基于64位环境编译的,Windows下依赖常见的运行库(多数机器已经预装),Linux下依赖libc和libpcap这类网络库,装系统时一般都会带上。
拿到压缩包后先看目录结构,不需要所有文件都搞清楚,但有几个关键文件你要认得:
| 文件/目录 | 作用 | 说明 |
|---|---|---|
| server/ | 服务端程序目录 | 模拟从站,监听TCP端口 |
| client/ | 客户端程序目录 | 模拟主站,主动发起连接 |
| config/ | 点位表、公共地址配置 | 从站地址映射和初始值都在这里改 |
| log/ | 运行日志目录 | 每次启动会生成带时间戳的报文记录 |
解压后我一般会先把config目录下的点位表示例文件读一遍,确认这个版本的信息体地址编码是两字节还是三字节。不同厂的实现有差异,有的信息体地址只支持到0-65535,有的支持扩展地址,这直接影响后续点位表怎么填。除了运行库,不需要装数据库,也不需要额外装协议栈插件,工具自带完整的IEC 104协议栈,这点对新手比较友好。
2.2 通信参数先看这几个
IEC 104规约走TCP/IP承载,在动手点按钮之前,先搞明白几个核心参数,仿真工具界面上标得最显眼的也是这几个。
| 参数 | 默认值 | 说明 |
|---|---|---|
| TCP端口 | 2404 | IEC 104标准端口,可在服务端修改 |
| T0(连接建立超时) | 60秒 | 等待TCP连接建立的时长 |
| T1(发送/测试确认超时) | 15秒 | 发送帧后等待确认的时长 |
| T2(接收确认超时) | 10秒 | 收到帧后发送确认帧的响应窗口 |
| T3(空闲测试超时) | 20秒 | 链路空闲时发送测试帧的间隔 |
T2必须小于T1,这是规约明确要求的,否则会出现在接收方还没来得及回确认时,发送方已经判定超时的情况。之前有个同事把T1设成5秒、T2设成10秒,结果每次做总召都报链路中断,查了半天报文才发现是参数配反了。仿真工具里这几个值默认是标准的,建议先不动,等理解了链路状态机的概念再去调。
2.3 启动服务端与客户端:一条链路的最小步骤
先在服务端程序里设置监听地址和端口。监听地址一般填0.0.0.0表示所有网卡接口都监听,端口保持2404。然后点击启动按钮,服务端进入等待连接状态。这时可以在命令行验证端口是否监听成功:
# Windows下查看端口监听状态 netstat -ano | findstr 2404 # Linux下查看端口监听状态 ss -tlnp | grep 2404如果看到TCP监听状态,说明服务端程序起来了。接着打开客户端程序,填对端服务器的IP和端口,公共地址填与从站一致的地址,然后点连接。连接成功后,两端界面的状态栏都会显示TCP已建立,报文窗口会打印出STARTDT激活帧。这个过程就是IEC 104链路建立的握手流程,简单说就是客户端发一个U帧启动指令,服务端回一个U帧启动确认,两边才开始传数据。
这套最小步骤的意义在于帮你确认环境通不通。如果连这一步都过不去,后面所有功能都白搭。我习惯把IP地址配成127.0.0.1先跑通本机回环,确认软件本身没问题,再改成局域网地址做真机联调,排查范围一下子就缩小了。
3. 客户端/主站实操:总召、遥信遥测与报文留证
3.1 连接配置与状态栏
主站侧界面一般分三个区:连接配置区、操作按钮区、报文显示区。连接配置区除了IP和端口,还有一个容易被忽略的选项——公共地址(Common Address)。这个地址在站与站之间必须唯一,它被编码在ASDU的公共地址字段里。仿真工具通常在客户端和服务端各有一个公共地址配置框,两边填不一致时,帧能发出去但服务端会拒收。
状态栏重点看两个字段:发送序号和接收序号。IEC 104的I帧携带序号,每发一帧发送序号加2,每收一帧接收序号加2(因为偶数编码、奇数表示确认,实际是模128递增)。如果你观察到序号长时间不动,说明链路可能卡住了或者对方没有正常响应。
操作按钮区一般是总召、单点召唤、遥控选择/执行、对时命令这类。不同工具叫法略有差异,本质就是封装好的ASDU构造器。按一个按钮,不用自己手工拼帧,这正是仿真工具比手工构造报文高效的地方。
3.2 总召一个周期里发生了什么
总召是主站上线后必做的一个动作,目的是从从站拉全量数据。界面上点“总召”按钮,工具会自动构造一帧总召激活报文,报文窗口会显示类似这样的完整帧:
68 0E 00 00 00 00 64 01 06 00 01 00 00 00 00 14逐字节拆开看:68是启动字符,0E是APDU长度(14字节),00 00是发送序号和接收序号(这是第一帧I帧,序号为0),64是类型标识(总召),01是可变结构限定词(表示1个信息对象),06 00是传送原因(激活),01 00是公共地址,00 00 00是信息对象地址(总召的召唤地址固定为0),14是召唤限定词(QCC,表示周期总召,响应时限为10秒)。
服务端收到总召后有两种行为:先回一帧总召激活确认,然后开始上送全量数据;或者直接开始上送。全量数据通常分遥信、遥测多帧上送,最后以总召结束帧收尾。仿真工具的服务端把这两件事都做了,而且速度很快,界面上你会看到一大波报文涌进来。这时不要急着清屏,先观察总召结束帧有没有到。总召结束帧的类型标识也是64,但传送原因是10(带时标的总召结束)。如果只看到激活确认没有结束帧,说明从站侧数据没发完或链路异常。
3.3 报文导出与抓包配合
仿真工具自带报文窗口虽然能直接看帧,但有些场景需要把报文留作证据或做深度分析,这时要配合抓包工具。抓包的好处是能看到TCP层的状态变化,比如重传、窗口收缩、FIN/RST标志位,这些在应用层报文窗口里是看不到的。
以常见抓包工具为例,过滤规则这样写:
# 命令行抓包,只抓2404端口的报文 tcpdump -i eth0 -s 128 -w iec104.pcap "tcp port 2404" # 抓完导入Wireshark后用显示过滤器 # 只查看I帧 tcp.payload[0] == 0x00 # 只查看U帧 tcp.payload[0] == 0x07 || tcp.payload[0] == 0x0b仿真工具导出的报文文件一般是文本格式,十六进制与解析字段并存。导出时的关键设置是时间戳精度,建议选到毫秒级,后续排查变化上送顺序时会用到。如果只看双击一条报文的解析树,你会看到APCI(应用控制信息)和ASDU(应用服务数据单元)两部分,APCI是传输层的信封,ASDU是业务内容。刚开始学的时候不要直接看抓包原始字节,先看工具解析出来的字段名,建立起“字节到含义”的映射关系,等熟了再看原始十六进制。
我一般会在每次联调前把抓包工具打开,哪怕只用仿真工具自测也开着,采集10分钟的基线报文存起来。后面真出了问题,翻基线报文对照,比对着界面猜原因快得多。
4. 服务端/从站配置:点位表、变化上送与遥控应答
4.1 点位表怎么设计才不会后期推倒
仿真工具的核心价值在服务端侧可配置的点位模拟能力。点位表是服务端的数据字典,它定义了从站对外暴露哪些信息:遥信点位、遥测点位、遥控点位、遥脉点位,每个点位的信息体地址、类型、初始值、变化规则。
点位表通常以文本方式存储,常见格式是CSV或INI。字段一般包括:信息体地址、类型标识、初始值、变化模式。设计点位表的时候,要提前规划地址段,避免后期地址冲突:
| 地址段 | 用途 | 类型标识 |
|---|---|---|
| 1-100 | 遥信(单点) | 1 |
| 101-200 | 遥测(短浮点) | 9 |
| 201-300 | 遥控(单点命令) | 45 |
| 301-400 | 遥脉(累计量) | 15 |
这个地址规划不是规约强制的,但建议按站点子系统分块预留。曾经在联调现场遇到一个点位表,地址乱得毫无规律,主站侧把1号信息体地址配成了遥测,从站侧却把它当遥信用,两边都不报错但数据全是错的。后来花了半天逐条核对,后悔当初没提前切地址段。
点位表改完之后必须重启服务端程序才能生效,这是很多人的第一个坑。有些工具提供热加载功能,但也只针对值的变化不针对结构变化。改完地址后建议先在服务端界面看点位总览,确认加载数量正确。
4.2 遥信变位模拟与抖动抑制
遥信变位是最常被模拟的场景。在服务端界面上双击一个遥信点位的值,把0改为1,工具会自动构造一帧变化上送报文。点位的品质描述词会标记为“当前值”,传送原因通常是3(突发)。这跟总召时的传送原因1(周期)或2(背景扫描)有明显区别。
变化上送的帧结构比总召简单,但有一个关键字段容易被忽略:品质描述词。单点遥信的品质描述词低两位是状态值,高几位是有效性标志。如果工具允许修改IV位(无效标志),模拟设备故障时就把它置1。现场曾经出现过一次误告警,排查半天发现是仿真工具里有个点位品质位默认置了无效,主站侧又没做有效性过滤,直接把无效值当真实值报了。
抖动抑制是仿真工具里值得单独说说的功能。真实设备在开关动作时会产生毫秒级的抖动,连续几次状态翻转后才会稳定。仿真工具一般提供去抖时间配置,单位是毫秒。如果把这个值设成0,你会发现点一下鼠标,从站连续上送好几条变位报文,主站侧可能因为连续变位而产生重复报警。我一般把这个值设成30-50毫秒,模拟实际继电器触点动作的机械延迟。示波器上看到的那种“接不动”效果,用这个参数可大致模拟个七八分像。
4.3 遥控的处理链条:选择、返校和执行
遥控是最容易出问题的环节,它的流程有明确的状态要求。从站收到主站的遥控命令,要经历“选择-返校-执行-确认”四步才算完成。仿真工具在服务端会明确展示每个遥控点位的当前状态,界面上可以看到它停在“已选择待执行”还是“已执行完成”。
选择帧的构造如下:类型标识45(单点遥控),传送原因6(激活),信息体地址指向对应遥控点位,命令字节的低两位分别代表合/分和执行/选择。选择合闸的命令字节是0x03(bit0为合、bit1为选择),执行合闸的命令字节是0x01(bit1置0表示执行)。这是一帧选择合闸的完整报文:
68 0E 0A 00 00 00 2D 01 06 00 01 00 01 00 00 03服务端收到选择帧后回一帧激活确认(传送原因7),此时从站侧遥控点位进入待执行状态。注意,从站这时不会真正输出任何物理动作,只是把“已选择”状态锁存。接着主站发执行帧:
68 0E 0C 00 00 00 2D 01 06 00 01 00 01 00 00 01这里控制域的发送序号从0A变到0C,表示新的一帧I帧。命令字节变成0x01,表示执行合闸。从站收到执行帧后完成输出并回执行确认。
仿真工具在这条链上做得很细:如果主站只发选择帧不发执行帧,从站会在超时后自动清除选择状态。超时时长一般在工具的配置里可以调,默认是T2相关的时间窗口。如果主站直接发执行帧而没有先选择,从站会回一个否定确认,拒绝执行。仿真工具把这个逻辑完整实现了,这在和真实设备联调时特别有用——真实设备也会拒绝未返校的执行命令。
5. 避坑指南:连接失败、遥控超时与报文错位的排查
5.1 连接建立失败:TCP通但规约握手没完成
现象:客户端点连接,界面提示TCP已建立,但状态一直停在“等待STARTDT确认”,后续发任何帧都没响应。
原因:服务端没有收到U帧启动指令,或者启动指令帧格式错误。最常见的是客户端没有自动发送STARTDT激活帧,有些工具把“启动链路”放在手动按钮上,连接成功后必须再点一次才发U帧。
解决:先在报文窗口里搜索0x07开头且长度为4字节的帧(68 04 07 00 00 00),如果没有,手动触发“启动链路”或“建立连接”按钮。如果确认发了但服务端没回0x0B,检查服务端配置里的T0超时设置,有些情况下T0设成0表示禁止连接建立,会直接拒绝握手。
5.2 链路正常但总召没响应:公共地址对不上
现象:连接正常,STARTDT握手完成,发总召后服务端界面没有收到任何帧,客户端也收不到总召确认。
原因:ASDU里的公共地址字段与从站配置文件里的公共地址不一致。有些仿真工具把公共地址默认设为1,但点位表配置文件写的是2,两边默默各干各的,谁都不报错。
解决:打开客户端和服务端的公共地址配置框,确认两边一致。改完后注意,有些服务端程序的公共地址读取是在启动时加载的,改完必须重启服务端进程。我一般会先在客户端发出总召后,马上看服务端报文窗口有没有内容——如果服务端窗口干干净净,基本就是公共地址不匹配,而不是网络问题。
5.3 遥控总是超时:忘了等返校确认
现象:执行遥控后主站立刻报超时,但从站侧显示已经把遥控点位置成“已执行”状态。
原因:主站侧在发完选择帧后没有等返校确认,直接把执行帧发出去了。IEC 104的状态机要求选择后在规定时间内收到返校(激活确认)才能执行,时间窗口通常在几秒内,主站发太快或从站响应慢都会触发这个状态冲突。
解决:先看从站侧的遥控状态是否为“已选择”,如果是,等它的返校确认帧回来后再点执行。如果从站侧已经放弃了选择(状态变回空闲),那就重新发选择帧。仿真工具的自动演示模式一般会自动处理这个时序,但手动模式必须要人工等两步之间的时间间隔。我在做联调时习惯在操作记录里勾选“遥控流程分步执行”,每步之间手动控制节奏。
5.4 上送报文像雪崩:抖动抑制没生效
现象:改一个遥信点位,从站连续发了几条相同地址的变位报文,主站侧重复报警。
原因:点位表的去抖时间配置是0,工具把一次鼠标点击当作多次变位,或者点位表的初始值本身在连续翻转。
解决:在服务端配置里去抖时间填30-50毫秒,保存后重启服务端。如果点位表里已经有连续的变位记录,手动把点位值恢复到目标状态再改一次,让内部状态寄存器先校准。另外检查点位表里是否有重复地址,两条相同地址的遥信记录会导致值互相覆盖和频繁上送,这种问题在点位表加载时就要避免。
5.5 时标和品质位看着不对:时钟同步与无效标志
现象:报文窗口里显示的时标比当前时间差了很久,或者上位机显示的是无效数据但界面上看起来正常。
原因:仿真工具的时标默认用本机时钟,但如果开了“时标回放”模式,它会把报文里的时标改成配置值,不回本机时间。品质描述词里的IV位被置1但界面显示没标红。
解决:时标问题在工具设置里把时标来源改成“本机时钟”,或者做一次对时命令,服务端收到对时后会修正内部时钟。品质位问题在点位表里检查每个点的IV标志,仿真工具通常用一个字段控制,把它置0恢复有效。在联调真实系统时,IV位这个陷阱几乎一定会遇到一次——主站侧一般不显示无效标志,导致调试人员看到一堆丑数还找不到原因。
6. 进阶用法:用仿真工具把回归测试脚本化
仿真工具自带图形界面适合人工操作,但做回归测试时,重复点按钮效率太低。我发现这类工具大多支持本地端口对接,可以用脚本直接与仿真工具的从站端口通信,把测试流程写成自动化用例。
下面是一个用Python构造IEC 104基础交互的示例,模拟主站启动链路、发总召并检查响应:
import socket import time HOST = "127.0.0.1" PORT = 2404 # 建立TCP连接 s = socket.create_connection((HOST, PORT), timeout=10) def send_frame(hex_str): """发送十六进制帧并读取一段响应""" s.sendall(bytes.fromhex(hex_str)) time.sleep(0.5) return s.recv(2048) # 1. U帧:STARTDT激活 resp = send_frame("68 04 07 00 00 00") print("启动链路响应:", resp.hex()) # 2. I帧:总召,发送序号0,公共地址1 resp = send_frame("68 0E 00 00 00 00 64 01 06 00 01 00 00 00 00 14") print("总召响应:", resp.hex()) # 3. 检查总召结束帧(类型标识64,传送原因10) assert b"\x64\x01\x0a\x00" in resp, "未收到总召结束帧" print("总召结束帧确认通过")这段脚本里send_frame函数做了两件事:发送原始十六进制帧,并读取TCP缓冲区里的响应。time.sleep(0.5)是给从站处理留出时间窗口,实际使用时建议改成循环读取直到缓冲区为空,避免把多个响应帧截断成半帧。总召结束帧的判断条件是类型标识0x64(总召)、可变结构限定词0x01、传送原因0x0A(带时标的总召结束)。如果断言失败,优先检查公共地址是否匹配和从站点位表是否加载成功。
脚本化之后,可以把整个测试套件固化成固定的回归流程。每次改动点位表或版本升级,启动一次回归脚本,一两分钟内就知道有没有破坏原有功能。从那以后我每次联调前都会强制走一遍这套流程——先跑通基本链路,再跑一遍全量总召,最后抽查几个遥控点位的选择/执行闭环。这个习惯救过我很多次,尤其是拿到一台新装的环境、不确定软件是否完整时,先用脚本跑一遍比肉眼点按钮可靠得多。希望这个思路帮你在用仿真工具时少走点弯路。
本文还有配套的精品资源,点击获取