简介:面向电力自动化与变电站通信开发人员,这套资料聚焦南自以太网103规约(基于IEC 60870-5-103)的协议要点与上位机实现,可用于理解规约帧结构、服务类型及透明传输机制,并快速搭建与南自设备通信的应用程序。压缩包内共有2个文件,包含1份zip规约说明文档和1个cpp上位机源码,整体大小788KB;zip部分梳理数据帧构造及控制域、地址域、信息域、校验域等核心概念,cpp源码则演示报文构造、编码解码、序列号管理、错误检测与重传、连接管理及心跳报文等关键逻辑。该规约在变电站自动化、电网监控、配电自动化、电量计量等方向广泛应用,已有1442人学习浏览。适合具备C++基础、需对照实际代码调试规约通信的中高级开发者,可据此完善TCP/IP环境下的链路检测与数据分包处理,缩短项目开发周期。 收到这份项目文件时,同事发我的时候也没多说,就一句“南自以太网103规约及上位机代码.zip,你研究一下”。说实话,做电力自动化这行的人看到这个文件名基本就能猜到里面是什么——一套基于IEC 60870-5-103规约的以太网通信实现,外加一套上位机示例工程。这东西在变电站监控系统、继电保护装置联调、配网自动化终端接入这些场景里,属于典型的“绕不开、但资料特别散”的硬骨头。今天我把这套文件的拆解过程、103规约的核心原理、上位机代码的关键模块,以及我自己踩过的一些坑,一次性整理出来。
先说清楚这篇内容适合谁:如果你是刚接手变电站监控后台开发、保护装置通信调试,或者正在做第三方系统对接保护装置的集成工作,那这篇文章可以直接当一份参考笔记用;如果你只是想了解103规约到底是什么、以太网方式和传统串口方式有什么区别,前两部分也够你建立整体认知了。
1. 项目概述:这套文件到底解决了什么问题
1.1 南自设备与103规约的行业背景
南京南自在电力系统里知名度不需要我多说,它的保护测控装置在变电站里装机量非常大。而103规约,全称是IEC 60870-5-103,是国际电工委员会制定的继电保护设备信息接口配套标准。这规约的核心目的,是让保护装置和变电站层监控系统之间能够互相通信——监控系统能召唤定值、接收遥信遥测、拿故障报告和波形,保护装置也能主动上送事件和扰动数据。
过去很多年,103规约主要跑在串口上,RS-232或者RS-485,一个装置一条线,通信速率通常9600bps或19200bps,速度慢、距离短、接线繁琐。后来变电站自动化系统逐步网络化,以太网接口的装置越来越多,厂商就在原有103规约基础上做了以太网传输的扩展。南自的这套以太网103,本质上就是把原本承载在串口链路层上的103应用层数据,改放到TCP/IP网络上传输。听起来就是个换传输层的操作,但实际落地的时候牵扯到会话管理、报文分帧、多客户端接入、故障文件传输等一系列工程问题。
1.2 压缩包内容合理拆解
我拿到手之后先做了个粗略的结构分析,虽然不同项目里这套文件的具体内容会有差异,但一般就这几类东西:
- 规约说明文档,通常是PDF或者Word,描述了报文格式、ASDU类型、定值区编号、故障文件格式这些。这是最值钱的资料,网上零散的资料根本没法比。
- 上位机示例代码,语言多见于C++、C#或者Delphi,包含通信模块、协议解析模块、界面工程。我这份代码核心是通信和解析,界面比较简单,但足够说明问题。
- 配置文件或者配置工具,用于设置装置的IP、端口、装置地址、定值参数等。
- 可能还有抓包样例、报文日志,方便你对照调试。
这里必须提醒一点:103规约虽然有个国际标准编号,但实际落地的时候,各家厂商都会有自己的扩展和私有定义。南自的装置、南瑞的装置、四方、许继,它们的103实现细节都存在差异。所以这套文件的价值,重点不在“103规约标准”,而在于“南自装置的103实现和对应的上位机对接方案”。你拿着这套东西去接南自的老装置,基本是正路;拿它去接其他厂商设备,只能当参考,不能照搬。
2. 103规约核心原理,为什么要走以太网
2.1 103规约的报文模型
要理解这套代码,先得弄清楚103规约的报文组织。103规约遵循IEC 60870-5系列的基本框架,分物理层、链路层、应用层。应用层的数据单元叫ASDU(应用服务数据单元),由类型标识、可变结构限定词、传送原因、公共地址、信息体地址、信息体元素等字段组成。
举几个常见类型标识,对后续看代码非常有帮助:
| 类型标识 | 含义 | 用途 |
|---|---|---|
| 1 | 带时标的报文 | 事件顺序记录、SOE |
| 2 | 带品质描述的遥测量 | 测量值上送 |
| 5 | 标识报文 | 装置自识别 |
| 6 | 时间同步 | 对时 |
| 7 | 总召唤 | 全数据召唤 |
| 8 | 定值组标识 | 定值区信息 |
| 40 | 带时标的扰动数据 | 故障波形启动 |
| 45 | 扰动数据传输 | 波形数据 |
链路层在串口时代用的是IEC 60870-5-1里定义的FT1.2帧格式,有固定的帧头、控制域、地址域、帧校验和。你可以把它理解成一种带校验的“数据信封”,保证一帧数据在串口线路上传输的可靠性。应用层数据被塞进这个信封里,一次传一个或者多个ASDU。
这里我想多说一句为什么要先搞清楚报文模型,因为上位机代码里90%的逻辑都在处理这些报文的组装和解析。你如果一上来就抠代码细节,很容易被各种数组、长度字段绕晕。先建立“信封+内容”的心智模型,再去看代码,思路会清晰很多。
2.2 从串口到以太网的演变,技术和运维双维度的必然
串口103的问题,搞过现场的人一清二楚。首先是接线,每个装置一根通信线拉到监控屏,装置多了就是一个线缆迷宫;其次是速率,9600波特率传输一个完整的故障波形文件动辄要几分钟,而且串口通信半双工机制决定了一问一答的低效;再就是距离和隔离问题,RS-485虽然能到千米级,但现场干扰大时通信质量会明显下降。
以太网方式就直观多了:装置通过网线或者光纤接到站控层交换机,IP地址一配,一根物理链路承载所有装置的通信。传输速率直接到10M/100M,传送波形文件从几分钟缩短到几秒。更重要的是,TCP协议本身提供了可靠传输、重复报文检测、流量控制能力,应用层不需要再纠结链路层的重发机制。
我实际接触下来的感受是:以太网化的103规约,基本把串口时代最折磨人的几个点全部解决了。但天下没有免费的午餐,以太网也带来新问题——多个客户端同时连接的管理、TCP连接断开重连时的状态恢复、报文粘包和半包处理,这些在串口时代根本不存在。所以这份上位机代码里,通信管理部分的复杂度远高于协议解析部分。
2.3 以太网103的关键设计点
南自这套以太网103,我梳理下来有这几个关键设计:
- 传输层采用TCP协议,装置侧作为服务端监听固定端口,上位机作为客户端主动连接。也有厂家做成上位机做服务端、装置做客户端的模式,但南自这套常见的是前者。
- 链路层的FT1.2帧结构通常被保留,也就是把FT1.2帧整体作为TCP的负载进行传输。这样设计的好处是应用层的ASDU解析逻辑可以完全复用原来的串口代码,改动成本小。
这个保留FT1.2帧结构的做法,我特意要指出来,因为它直接影响了代码结构。你在上位机代码里会看到,接收缓冲区里处理的数据不是裸的ASDU,而是带帧头、帧尾、校验字的东西。有些刚接触的人容易疑惑:既然走TCP已经可靠了,为什么还要帧校验?答案是历史兼容,以及防止TCP数据流被异常打断时能快速重新定位帧边界。
另一个重要设计是TCP端口与会话状态。装置一般监听一个固定端口(比如2404,和IEC 104规约的常用端口一致),支持多个上位机同时连接。每个TCP连接独立维护收发状态,连接断开后装置侧会清空该连接上的上下文。上位机侧则需要实现断线重连、定时心跳(通常用总召唤或时间同步命令来试探链路状态),否则网络抖动一次,链路就静默了。
3. 上位机代码架构与关键模块实现
3.1 整体框架梳理
打开工程之后,第一件事不是看代码,而是先看目录结构和类/模块划分。我这份代码的结构大致是:
- 通信层:封装Socket连接管理、收发线程、接收缓冲区。
- 规约层:处理FT1.2帧的封装与解包,ASDU的组包与解析。
- 业务层:把规约层解析出来的数据映射成逻辑对象——遥信变位、遥测刷新、事件记录、定值项。
- 界面层:用于显示数据和手动操作,发送总召唤、对时、定值修改等命令。
- 公共工具:日志、定时器、字节序转换、CRC校验等。
这个分层思路值得新入行的朋友学习。千万别把Socket收发的代码和ASDU解析揉在一起,不然后期维护绝对崩溃。我见过不少项目把协议解析写在Socket接收回调里,最终代码只能推倒重写。规约层和通信层解耦,你换一种传输方式(比如从TCP改成UDP),上层完全不用动。
3.2 通信层:连接管理与报文收发
通信层的核心任务是可靠地接收完整的FT1.2帧。TCP是字节流协议,没有消息边界,所以上位机必须自己做分帧处理。代码里一般维护一个累积缓冲区,每次收到数据就追加进去,然后循环查找帧头、解析长度字段、判断帧尾和校验,取出一帧完整的数据再交给规约层。
这里有一个几乎所有新手都会踩的坑:直接按固定长度去切数据包。TCP粘包时,一次recv可能收到两帧甚至三帧;TCP半包时,一帧数据被拆成两次甚至多次到达。我写过一个简单的接收循环逻辑,可以给做以太网103的同学参考:
// 伪代码示意:TCP数据接收和FT1.2帧分帧 void OnDataReceived(const char* buffer, int len) { recvBuffer.Append(buffer, len); while (true) { int frameLen = recvBuffer.FindCompleteFrame(); if (frameLen <= 0) break; // 数据不足,等待下一包 std::vector<uint8_t> frame = recvBuffer.Take(frameLen); ProcessFrame(frame); // 交给规约解析层 } }注意这段逻辑里最关键的是FindCompleteFrame,它需要根据FT1.2的帧格式,先定位起始字符(一般是0x68),然后读长度字段,最后用帧校验和确认。遇到校验不对的帧,直接丢掉并重新同步,而不是硬解析。
3.3 规约解析层:ASDU解析、定值/故障文件处理
规约层处理的东西比较杂,但归根到底就是两件事:组包和解析。
组包就是按照103规约的格式,把类型标识、传送原因、公共地址、信息体地址等字段组装成链路帧发送出去。解析就是逆过程,收到帧后拆出ASDU,再根据类型标识分发到不同的处理函数。比如类型标识是1的报文,代表带时标的SOE事件,解析出保护动作类型、动作时间、动作相别等信息,然后推到界面刷新和数据库记录。
定值处理是103规约里比较繁琐的一环。定值区通常有多个区,每个区里包含数十个定值项,值和属性的格式五花八门(整数、浮点数、ASCII字符串)。上位机代码一般会把这些定值的定义抽象成一张表,每个表项包含定值编号、名称、数据类型、单位、格式,这样召唤定值和修改定值都可以通过这张表驱动,而不是为每个定值写死代码。
故障文件和波形数据的处理更特殊。扰动数据通过类型标识40的ASDU启动,装置先告诉上位机“我这里有一份波形文件,大小多少字节”,然后上位机用类型标识45的报文逐块召唤数据。因为数据量大,往往需要配合重传机制和校验机制。这部分代码通常最复杂,也是最容易出Bug的地方。后面我会专门讲一个常见问题。
3.4 界面与数据服务
界面层在示例代码里往往很朴素,无非就是设备列表、遥信状态表、遥测曲线、事件窗口、定值管理窗口。但它的价值在于验证协议层是否正确。我建议调试的时候,优先把事件窗口和原始报文日志窗口打开,一个显示解析后的结果,一个显示收发的原始报文。两边对照着看,定位问题最快。
数据服务这块容易被人忽略。上位机不只是显示数据,还要把SOE事件、故障报告存下来,所以代码里通常有数据库封装或者文件存储模块。我在实际项目中见过只做界面不做存储的上位机,结果现场要追查三个月前的一次保护动作,数据早就没了。做上位机,存储功能一定不能省,哪怕只是按天记录CSV文件,也比没有强。
4. 实操建议:如何把手里的这套代码跑起来
4.1 环境准备与依赖检查
拿到代码后,先别急着编译。我建议按这四步走:
- 确认开发环境。看工程文件是VC++的.sln还是C#的.csproj,或者别的,直接决定了你要不要装对应版本的Visual Studio。老项目大概率是VC6或者VS2008时代写的,新系统编译时会出一堆兼容性问题。
- 检查第三方依赖。有些示例代码用到了第三方串口库、数据库组件、图表控件,这些依赖没装齐,编译必然过不去。
- 找装置或者模拟器。真机联调最理想,但如果没有装置,可以先用支持103规约的模拟器软件顶替,或者用之前抓包保存的报文文件做回放。
- 准备报文分析工具。Wireshark抓包一定要会看,TCP层的交互一目了然。
4.2 配置要点:IP、端口、装置地址
配置是联调的第一步,也是问题高发区。需要注意几类参数:
- 本机IP和装置IP要在同一个网段,掩码、网关要对。这个看似简单,但现场经常因为IP冲突或者掩码错误导致通信失败。
- 端口号要一致。装置监听哪个端口,上位机就得连哪个端口,端口错了就连不上。
- 公共地址(Common Address)必须匹配。103规约里每个ASDU都带公共地址,装置侧和上位机侧对不上,收发的报文都会被丢弃。
- 装置地址、链路地址有时候和公共地址是一个值,有时是两个值,要看文档说明。
配置这块我的建议是:第一次联调时,把装置侧日志、上位机日志、Wireshark抓包三方同时打开。出了问题,先看抓包,确定TCP有没有建立,再看应用层有没有数据交互,最后看解析是否报错。一层一层往下查。
4.3 联调步骤与验证方法
联调建议从最基础的功能开始,逐步增加复杂度:
- 先做TCP连接测试。上位机连装置的端口,观察连接是否建立成功。
- 发送总召唤命令。这是最基础的链路确认手段,装置收到后会上送所有的遥信遥测状态。如果总召唤能成功,说明链路层和应用层的基本功能是通的。
- 测试时间同步。让上位机下发对时命令,然后看装置是否回确认,时间是否更新。这一步能验证双向通信的完整性。
- 测试SOE上送。在装置上模拟一个保护动作或者开入变位,观察上位机事件窗口是否出现带时标的事件记录。
- 最后测试定值召唤和故障文件传输,这两块涉及更多报文交互,放到后面单独验证。
5. 常见问题与排查技巧实录
5.1 TCP连不上,抓包看不到连接建立
这是最基础也是最高频的问题。大部分情况是IP或者端口配置错误。但也有一种容易被忽略的情况:装置侧的TCP连接数满了。有些装置的嵌入式系统对并发连接数有限制,比如只允许4个客户端,如果之前的连接没有正常关闭,新连接就进不来。
排查思路:先ping通装置IP,再用telnet测试端口连通性。如果端口不通,看装置的连接列表,把无效连接清掉再试。上位机侧要做好连接的异常关闭处理,程序退出时主动断开Socket,避免占着连接不释放。
5.2 总召唤有响应,但数据解析不出来
这通常不是链路问题,而是ASDU解析的细节对不上。常见原因有三类:
- 公共地址不匹配,导致报文被过滤掉。这种问题在抓包里能看到完整报文,但代码里没有任何反应。
- 字节序问题。103规约里的多字节整数一般是高字节在前(大端),但有些装置在实现时用了低字节在前。代码里需要提供字节序转换的开关,或者根据报文特征自动识别。
- 类型标识或者传送原因没覆盖全。装置上送了一种代码没处理的报文类型,解析逻辑走到default分支直接丢弃了。
经验之谈:解析类问题,最好的办法是拿一份标准报文或者已知正确的报文日志,逐字节对照。把打印日志做到位,每个ASDU的字段都打出来,对照标准一比,很快就能看出哪里偏了。
5.3 故障波形文件传输总是不完整
这个问题在以太网103里相当典型。故障文件数据量大,TCP传输过程中一旦出现网络抖动,或者发送端和接收端的缓冲区设置不当,就容易丢数据。上位机侧的典型症状是:波形文件能开始传输,但传到一半卡住,或者传完后校验失败。
我当时的排查步骤是:第一步看TCP是否有重传(Wireshark里能看到),如果有,说明网络质量不好或者TCP缓冲区太小;第二步看装置的故障文件分帧逻辑,是不是每次发送的数据块过大,导致TCP层分片后接收端重组出错;第三步看接收端的缓冲区大小,必须比装置最大单帧数据大,否则会截断数据。
后面我采用了一个比较稳妥的方案:接收端动态扩容缓冲区,同时增加数据完整性校验,传完后对文件CRC做验证,失败就自动重新召唤一次。这个方案在现场用了很久,稳定性提升很明显。
5.4 时间同步成功但SOE时间还是不对
SOE时标不对,这问题和规约关系不大,更多是装置侧的时钟源头问题。就算上位机下发了对时命令,如果装置本身没有可靠的时钟源,时间走几天又会漂。所以现场一定要保证装置能正常对时,常见做法是配置GPS/北斗对时,或者依赖于站控层定期广播对时命令。
上位机侧的代码也要注意:SOE解析出来的时间是装置自己记录的,上位机不要做任何时区转换。有些新手看到时间不对,习惯性加个8小时偏移,结果过几天又发现差了8小时,来回折腾。先确认原始报文里的时间值,再决定要不要处理。
| 问题现象 | 可能的根因 | 排查建议 |
|---|---|---|
| TCP连接建立失败 | IP/端口配置错误、连接数满 | 先ping后telnet,检查装置连接列表 |
| 总召唤有响应但无数据 | 公共地址不匹配、字节序错误、类型未处理 | 对照报文逐字节解析 |
| 波形文件传输不完整 | 网络抖动、缓冲区设置不当、分帧逻辑问题 | 抓包看重传,校验文件CRC,失败重召 |
| SOE时间不正确 | 装置时钟源不准、时区处理错误 | 确认原始报文时间值,检查对时源 |
最后说几句实在的
这套“南自以太网103规约及上位机代码.zip”折腾下来,我最深的体会是:在电力自动化这个领域,规约这东西永远是“标准给你画了框架,细节全靠自己趟”。103规约的标准文档你可以从各种渠道拿到,但南自装置具体怎么实现、上位机代码里哪些地方做了兼容性处理,这些只能靠实际工程积累。
如果你正要开始接触这个项目,我的建议是三个字:先抓包。任何文档、任何代码,都不如一份真实的报文日志来得直观。把报文结构吃透了,再看上位机代码,你会发现那些看似复杂的函数都变得很好理解。而如果你是想把这套方案移植到自己的项目里,记住别把通信层、规约层、业务层揉在一起,这个架构原则比任何具体代码都重要。
最后再分享一个小技巧:调试103上位机的时候,在代码里加一个“原始报文备份”功能——把所有收发的报文按时间顺序完整记录到本地文件。现场出了问题,远程把日志要过来,本地回放报文,很多问题不去现场就能定位。这个习惯帮我在无数个远程支持场景里省下了大量时间。
本文还有配套的精品资源,点击获取