ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

IEC104模拟器:支持master/slave双角色的电力规约调试利器

IEC104模拟器:支持master/slave双角色的电力规约调试利器 简介面向电力自动化领域的IEC 60870-5-104协议开发与测试人员这份在VS2015环境下的模拟器资源同时提供Master与Slave双端实现覆盖主站与从站之间的连接建立、报文交互、错误检测及性能评估等典型场景能够帮助开发者快速验证协议解析与响应逻辑。压缩包含104个文件整体约14.18MB以C/C源码工程为主包括17个头文件、14个源文件及工程配置等同时附带2个可执行程序与动态库既可直接运行体验也可在Visual Studio 2015中二次开发。已有1442人学习下载适合涉及变电站自动化、配电自动化SCADA通信的工程师与学习者。借助该模拟器可以深入理解ASDU、APDU帧结构、信息对象及Q0-Q3服务质量等核心概念并通过I帧、S帧、U帧的交互测试来排查通信问题提升系统稳定性。1. 为什么需要一个能当双方角色的IEC104模拟器搞电力自动化、变电站监控、配网终端这块的朋友对IEC104肯定不陌生。IEC 60870-5-104是电力系统中最常用的远动规约之一简单说就是调度主站和变电站终端之间通信的“普通话”。它走TCP/IP网络默认端口2404承载遥测、遥信、遥控、SOE、总召唤、时钟同步这些核心功能。说到调试工具大家第一反应是用真实设备或真实主站去测。但问题很明显现场设备就那么几台不能随便动新写的协议解析代码出了Bug总不能每次都跑到现场去连一次还有一些演示、培训、比赛场景不可能拉一套真实的前后台系统过来。这个时候一个稳定好用的IEC104模拟器就成了刚需。我这次做的是一个同时支持master和slave两种角色的模拟器。它既可以当主站主动发起连接去“骗”终端把数据吐出来也可以当从站模拟一台真实的RTU或保护装置等着调度主站来连接、总召唤、遥控。可能有人问市面上开源的IEC104实现不少为什么还要自己造一个轮子这个问题等下细说但结论是现成的库要么太底层要么绑定特定平台要么master和slave不能一键切换真正贴合日常调试和教学场景的反而不多。这个模拟器就是冲着这个缺口去的适合三类人做变电站监控系统、调度主站开发的同学需要在没有物理终端的环境下验证主站逻辑做RTU、DTU、继保装置等终端设备的嵌入式工程师需要模拟主站来测试从站功能学电力规约、准备技能竞赛或带学生做课程设计的老师同学需要一套能“拆开看”的工具来理解104规约的交互过程。2. 整体设计思路master/slave双角色架构2.1 为什么master和slave要放在同一个程序里先解释一个关键设计选择为什么把主站和从站做在同一个程序里而不是像常见做法那样分别做两个独立的工具。最直接的原因当然是省事但同时启动两个进程就能模拟一条完整的链路。比如你调试的主站软件还没接真实设备这边模拟器当从站监听2404端口那边主站软件就能连上来走流程。反过来你写了一个终端设备的协议栈想让它在没有调度中心的环境里跑起来模拟器切到master模式主动连接你设备开放的服务端口就能模拟调度中心做总召唤、下发遥控。更重要的原因在于调试场景的对称性。104规约是典型的请求-响应式协议主站发命令、从站回确认和响应。如果你手里只有一个主站模拟器没法验证从站侧收到命令后的处理逻辑是否严谨如果只有一个从站模拟器又没法验证主站侧的定时轮询、超时重传是否符合预期。把两种角色放在一个程序里意味着你在测试自己开发的任何一侧时另一侧永远有一个“标准对手”这在联调阶段节省的时间非常可观。2.2 语言选型与底层通信框架实现语言选的是C#跑在.NET 8上。选C#不是单纯的个人偏好有两个实际考量第一电力行业尤其是国内调度自动化领域Windows环境的占比很高很多调试工具和仿真平台都是Windows应用。C#对UI的生态天然友好后续哪怕要加一个图形化的报文监视窗口WinForms或Avalonia都顺手。第二C#的异步编程模型处理长连接非常舒服。IEC104是长连接TCP主站同时要跟多个从站保持连接从站要并发处理多个主站的接入这些场景用async/await写起来逻辑清晰不容易出现线程安全这类老生常谈的问题。底层用TCP Socket不依赖第三方通信框架保证整个程序干净、可控、容易交叉编译。TCP处理这块有一个值得注意的点IEC104虽然基于TCP但协议本身对“连接”有状态管理。它依赖TCP的保活机制同时规约层又定义了STARTDT、STOPDT这样的启停控制。这意味着底层Socket不能只做透传还需要维护一个明确的连接状态机。在实现时我把Socket收发、APCI状态流转、ASDU编解码三个层次分开每个层次只干一件事这样不管是排查问题还是扩展功能都有清晰的边界。2.3 整体模块划分三层结构整个模拟器在代码上分成三个模块对应我在上面提到的三个层次第一层是传输层负责TCP连接的建立、断开、心跳保活、接收缓冲区的粘包拆包。104的APDU分为I帧、S帧、U帧三类传输层要做的是把字节流按APDU长度字段切成一条条完整报文至于报文是哪种帧类型不归它管。第二层是APCI解析层负责识别帧类型、维护发送接收序号。这一层是104协议状态机的核心收发序号对不上要报错收到U帧的STARTDT确认要切换状态超时没收到确认要重发这些逻辑都在这一层。第三层是ASDU处理层负责具体业务数据的编解码——单点遥信、双点遥信、遥测、遥控、SOE、总召唤、时钟同步这些都归ASDU处理。模拟器的“数值可配置”“周期上送频率可调整”“遥控返校可模拟成功或失败”全部在这一层实现。3. 核心模块拆解与实现要点3.1 master端连接管理、总召唤与轮询控制master端要解决的第一个问题是怎么管理多条连接。实际调度系统里一台主站要面对几十甚至上百个从站模拟器虽然不做那么大的规模但至少要支持同时连接多个从站才能模拟出接近真实的环境。我在master端做了一个连接管理表每个从站对应一个独立的任务任务内部有独立的收发队列、超时计时器和统计计数器。master端的标准操作序列值得说清楚。建立TCP连接之后第一件事是发U帧的STARTDT激活从站的报文传输控制收到STARTDT确认后发总召唤命令让从站把当前全数据区的状态一次性上送。总召唤用ASDU类型标识C_IC_NA100限定词QOI设为20总召唤这个流程是104规约里所有调试的第一步。总召唤之后master会按周期发起遥测轮询和时钟同步。遥测轮询是为了定期刷新测量值时钟同步是为了让从站的事件时标与主站对齐。这些操作说起来简单但里面藏着一个容易踩坑的点发送序号和接收序号的匹配。I帧报文的Cot传送原因、发送序号N(S)、接收序号N(R)必须严格对应。主站发一帧I帧序号就要加一从站回的I帧里的N(R)必须等于主站期望的下一个序号。如果代码里序号管理有瑕疵连接很快就会报“接收序号异常”而被重置。我在master端做了序号校验一旦发现对端返回的N(R)与本地期望不符立即把错误信息呈现出来而不是默默吞掉这对调协议栈的人来说非常重要。3.2 slave端监听、事件上送与变化数据的处理slave端的核心逻辑和master刚好反过来。它不做主动连接而是在指定端口监听等master来建立连接。每个连接到来的master被当成一个独立的客户端slave为它单独维护一套收发状态机。这里有一个工程细节104从站必须支持多个master同时连接但链路层只认先发起STARTDT的那个master。也就是说后来的master可以建立TCP连接但在前面的master还没STOPDT之前它不能收发数据。我实现的时候用了一个简单的许可权标记谁先STARTDT谁获得通信权其他连接挂起等待这是符合规约本意的做法。slave的数据上送是整个模拟器最具可玩性的部分。电力终端上送的遥信分为“在总召时上送”和“变位时主动上送”两种情况。模拟器里给每个遥信点配置了一个可开关的“允许变化上报”标志打开之后你手动把某个遥信点的值从0翻到1slave会立刻组一帧M_SP_NA单点遥信ASDU 1用事件传送原因Cot3发给master同时等待master的确认帧。如果master没回确认按规约要求slave要把这条事件缓存下来等下一次总召唤时再补送一次。我在模拟器里完整实现了这个“变位-上送-确认-补送”的闭环对理解规约的状态转移很有帮助。遥测部分slave支持两种模式一种是周期上送周期可配常见配置是3秒到5秒一次另一种是按扫描周期变化上送模拟器里设定一个阈值只有浮点值变化超过阈值才上送模拟真实遥测中的死区设置。这两种模式可以并存看你要测主站的刷新逻辑还是测死区处理逻辑。3.3 ASDU列表支持哪些类型哪些场景必用模拟器目前支持的ASDU类型覆盖了日常调试最常用的一批我按应用场景整理了一张表方便对照着去用ASDU类型名称场景M_SP_NA1单点遥信开关位置、刀闸位置M_DP_NA3双点遥信断路器位置双判位M_ME_NC13归一化遥测浮点电压、电流、功率C_SC_NA45单点遥控分闸/合闸命令C_DC_NA46双点遥控双点遥控命令C_SE_NC50设定值控制浮点变电站定值整定M_SP_TB30SOE带时标单点遥信故障录波、事件顺序记录C_IC_NA100总召唤全数据区刷新C_CS_NA103时钟同步对时挑两个重点说说。SOE事件顺序记录是最能体现104物特点的功能。它不仅要记录开关变位还要精确记录变位发生的毫秒时刻。模拟器里做了个“触发SOE”按钮点击之后自动生成一条带时标的M_SP_TB事件时标的来源是内置的毫秒时钟。调试主站时你能用这个功能验证SOE解析是否准确、时标是否与主站系统时间对齐。我实际测试时发现有些主站对SOE时标解析用的是UTC有些用本地时间不一致会导致事件时间差8小时这坑主站设计和终端设计都得防着。遥控Command是另一个重头戏。从站收到遥控命令后规范的做法是回一个“确认”但不立即执行然后再回一个“执行结束”的确认帧。这个中间状态就是俗称的“返校”。很多不熟悉规约的人在联调时会被这个逻辑绕晕主站已经收到确认了为什么从站的状态没变原因就是遥控还没执行。模拟器里我加了一个“遥控执行时间延迟”参数默认100ms收到遥控命令后先回确认帧延迟后再回执行结束帧。如果你手动把延迟调到很大就可以清晰地看到这个两段式执行的完整过程。4. 实操过程从启动到联调4.1 环境准备与程序启动程序发布为一个自包含的.NET 8单文件可执行程序不用安装运行时双击就能跑。启动后的界面是一个双面板布局左侧是master模式的操作区右侧是slave模式的操作区中间共享一个报文监视窗口。刚启动时两个模式都是未激活状态。你要先选好当前要扮演什么角色。比如今天要测的是自己写的终端协议栈那就切到master模式填上目标终端的IP和端口点“连接”。如果是要模拟终端等待主站接入那就切到slave模式设定监听端口默认2404点“监听”。初次使用有一点要特别注意确认Windows防火墙没有阻止监听端口。这个坑我踩过不止一次——程序明明显示在监听但外面的主站就是连不上最后发现是系统防火墙弹窗没点允许。如果是自动化测试脚本调用记得在部署时就把入站规则配上。4.2 用master模式测试一个自研从站协议栈我这里演示一个完整流程用模拟器的master模式去测试一个自研嵌入式从站。第一步建立连接。填IP和端口后点“连接”等右上角出现“STARTDT已确认”的状态。如果这个状态一直不出来优先检查TCP是否连通再检查从站有没有正常处理STARTDT。第二步发送总召唤。点“发送总召唤”按钮观察报文监视窗口。正常情况应该是主站发C_IC_NA100总召唤从站回一个总召唤确认Cot7然后从站开始按顺序把遥信、遥测一帧帧上送最后回一个总召唤结束帧Cot10。如果你发现中途少了某类数据或者有报文丢失要么是从站的ASDU组装有bug要么是序号管理出了问题直接把报文抓下来逐帧对。第三步做遥控测试。先在从站属性列表里选一个遥控点比如“遥控点1”然后在master端选择分闸或合闸点“发送”。预期看到两个确认帧依次返回。如果中间等待时间不匹配你设定的“遥控执行延迟”说明从站的执行流程不对或者确认帧的传送原因填错了。第四步做时钟同步。点“时钟同步”按钮主站会发C_CS_NA103从站回确认。这个同步值是一个带毫秒的时间戳验证从站解码后是否正确更新了自己的系统时间。4.3 用slave模式模拟终端给主站调试反过来用slave模式模拟一个终端连到真实调度主站是更日常的调试场景。启动slave监听后先在遥信表里配置一组点位例如“1号开关合位”“1号开关分位”“2号刀闸合位”关联好类型是单点还是双点设置好初始值。遥测表里配几个模拟量比如“线路Uab电压”220.5kV“线路电流”356A。这些都配置完后就可以等主站来连了。主站连上并完成总召唤后你可以手动翻一个遥信点的值观察master侧的显示是否同步更新。遥测部分可以修改模拟值看主站的遥测刷新是否能感知变化。如果主站发了遥控指令slave端会弹出一条遥控请求提示显示命令来自哪个主站、哪个点号、执行的是合还是分你选择“执行”或“拒绝”——这就完全模拟了遥控返校里“允许/不允许执行”的真实决策过程。4.4 模拟异常场景断链、丢包、超时重发真正的好工具不光要在数据正常的条件下好用还要能在异常条件下帮你发现问题。这也是模拟器存在的重要原因之一。我加了几个网络故障注入功能断链、延迟、丢包。用“断链”可以随便中断任意一侧的TCP连接观察对端有没有正确感知到连接断开并触发重连逻辑。这在测试商用的主站、终端通信模块时特别有用因为断链重连本身就是稳定性测试的核心项。“丢包”功能允许你设定一个丢包率比如5%。发送的报文会按概率被丢弃。这时候通信对端的超时重传机制就会被动触发。IEC104的超时参数t1、t2、t3分别是多少个毫秒重传次数是几次这些在模拟器的参数面板里都可以调整。我建议你第一次调试时故意把丢包率调到10%把重传次数设为1这样能快速验证对端协议栈在恶劣网络下会不会崩会不会出现序号错乱。真正在电力系统中很多通信故障排查到最后都躲不开“丢包之后的时序”这关。5. 常见问题与排查技巧实录5.1 握手失败这类问题的排查思路模拟器用得多了你会遇到一些看起来莫名其妙的问题。这里把我遇到的和身边朋友踩过的整理成一张排查表下次碰到可以直接对号入座。现象可能原因排查方法连接建立后没有STARTDT确认对端没有正确实现APCI状态机或链路层未激活抓包看一下对端回的是什么帧U帧确认还是I帧误发总召下发后没有数据返回从站未正确启动数据上送或ASDU类型不支持检查从站是否处于允许上送状态检查总召确认的Cot是否为7报文监视有大量序号错误收发双方的发送序列号被复位过确认程序重启后是否连续建立新TCP连接新连接必须重新STARTDT遥控发了但从站没反应遥控命令类型、因果传输标识错误核对C_SC_NA/C_DC_NA的类型标识检查QOC选择是否有效偶尔丢数据但网络正常接收缓冲区处理粘包时出错检查拆包逻辑是否处理了APDU长度字段特别是长度大于253的情况系统时间漂移时钟同步未生效或时区不一致核对C_CS_NA时标解析确认通信两端在同一时区多主站连接时有乱码多个master抢占传输控制权检查STARTDT/STOPDT逻辑是否正确确认许可权标记只在STARTDT后的连接生效5.2 常见协议实现误区传送原因这些字段千万别填错如果让我只能讲一个点那绝对是“传送原因Cot”。这个字段只有两个字节却决定了整条ASDU的含义。调试过程中我见过大量的帧都是败在这个字段上。以总召唤为例主站发送总召唤命令时Cot6激活从站确认总召唤时Cot7激活确认从站上送数据时Cot20响应总召唤总召唤结束时Cot10激活终止。这几个值一旦填错比如该用7的地方你填成了6主站不会把从站的数据当作总召唤响应来解析表现为“总召后就没了下文”——这是一个非常隐蔽的bug因为TCP层面一切正常但业务数据就是没接上。类似地事件上送的Cot3突发响应周期轮询的Cot5响应站召唤时钟同步用Cot6激活发起Cot7激活确认应答。这些值本身没有记忆成本真正要养成的习惯是拿到一条报文先看Cot再看ASDU类型最后才看数据内容。这个顺序能帮你快速判断一个回复是“命令执行了”还是“正在执行”避免把业务状态搞混。5.3 仿真遥控、SOE时区这些实操中容易忽略的细节最后聊点实际中容易忽略但影响很大的细节。第一个是模拟量质量位。104遥测不仅有数值还有一个品质描述符Quality包含是否有效、是否溢出、是否被取代等标志位。如果从站返回的数据带的是“无效”标志主站处理时会直接不刷新该点。很多调试者第一次用模拟器发了一个数值发现主站上完全不更新第一反应是主站解析出错了——其实是因为模拟器里那个点的质量位默认是“无效”直到你在属性面板把它改成“有效”。这个细节一定要在联调前自查一遍。第二个是SOE时区的坑。我们前面提过SOE时标带毫秒是绝对时间但有些主站按UTC处理有些按本地时间处理。如果你的开发机在中国标准时间下而现场主站部署在UTC环境下不带时区跳转的模拟器很容易让联调双方产生“怎么差8小时”的困惑。我在模拟器里加了显示时区选项默认跟随系统时区调试时注意保持与主站端一致。第三个是连接断开的异常清理。如果模拟器异常退出后马上重启端口可能处于TIME_WAIT状态导致无法立刻监听同一端口。这不算bug是TCP的底层机制。如果遇到“端口被占用”或“地址已在用”的提示等一下再启动或者干脆在代码里启用SO_REUSEADDR选项。这也解释了为什么我一直推荐在调试脚本里加个0.5到1秒的重连间隔——省下很多重复报错的功夫。我在实际开发中用这套工具联调过好几轮最大的体会是一个趁手的模拟器不是帮你省掉测试而是把测试从“碰运气”变成“可复现”。同样的抓包数据输入模型一致、时序一致问题定位速度能快上好几倍。这也是我在模拟器里反复强化报文监视和故障注入功能的原因它们才是日常调试中最常被低估的战斗力。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

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