1. 协议概述:工业自动化的“普通话”
在工业自动化,特别是电力、能源、水利这些关乎国计民生的领域,设备之间要“对话”,光靠眼神和手势可不行,必须有一套严谨、高效、可靠的“普通话”。IEC 60870-5-104协议,也就是我们常说的IEC104协议,就是这套在调度自动化系统中应用最广泛的“普通话”。它定义了控制中心(主站)与变电站、发电厂等远方终端(子站)之间,如何通过网络(通常是TCP/IP)进行实时数据通信。
我第一次接触IEC104是在一个地区电网调度数据网改造项目里。当时现场的设备五花八门,有老旧的串口设备,也有新上的智能终端,通信协议更是七国八制。项目目标是要把所有站点的实时数据(比如开关状态、电流电压值)和事件信息(比如开关变位、保护动作)统一上传到新的地调主站系统。经过多方对比和测试,最终选择了IEC104作为标准通信规约。原因很简单:它是国际电工委员会(IEC)制定的标准,开放性高,兼容性强,几乎成了国内电力行业事实上的厂站通信标准。掌握了它,就相当于拿到了与绝大多数电力自动化设备“对话”的钥匙。
这套协议的核心价值在于,它让不同厂家、不同型号的设备,只要支持IEC104,就能无缝接入同一个监控系统。对于运维人员来说,不用再为每个厂家的私有协议头疼;对于系统集成商来说,项目实施和后期维护的成本大大降低。它主要解决的是实时监控与数据采集(SCADA)系统中的“信息孤岛”问题,实现“四遥”功能:遥测(YC,模拟量,如电压)、遥信(YX,状态量,如开关分合)、遥控(YK,控制命令)、遥调(YT,设定值调整)。接下来,我们就深入这套“普通话”的语法和会话规则。
2. 协议栈与报文结构深度解析
理解IEC104,首先要抛开对普通网络通信(比如HTTP)的认知。它不是简单的请求-响应模式,而是一种基于平衡式传输的通信机制,既有主站主动询问,也允许子站主动上报变化信息。这一切都建立在清晰的协议栈和严谨的报文结构之上。
2.1 四层协议栈模型
IEC104协议栈可以清晰地划分为四层,这借鉴了OSI七层模型和TCP/IP模型的思想,但针对工业实时通信做了优化:
物理层与链路层(L1 & L2):这部分被“隐藏”了。IEC104标准本身不定义物理介质和链路层协议,它默认其承载网络是“可靠的”。在实际应用中,这一层就是标准的以太网(IEEE 802.3)和TCP/IP协议栈。这意味着,IEC104报文是作为TCP协议的应用层数据来传输的。这种设计让它能直接运行在成熟的IP网络之上,利用TCP的可靠连接、数据校验和重传机制,省去了自己实现复杂链路层规约(比如循环冗余校验、超时重发)的麻烦。
传输层(L3 - TCP):使用TCP协议,端口号固定为2404。主站作为TCP客户端,子站作为TCP服务器端。连接建立后,形成一个稳定的、全双工的数据通道。所有IEC104的应用层报文都在这个通道里传输。
应用层(L4 - IEC 60870-5-104):这是协议的核心,定义了报文的组织格式、信息元素、服务原语和传输规则。它又可以分为两个子层:
- 应用服务数据单元(ASDU):这是信息的“内容”或“载荷”,包含了信息对象地址、数据值、品质描述词等具体数据。你可以把它想象成一封信的正文。
- 应用协议控制信息(APCI):这是控制传输的“信封”,包含了启动字符、长度标识以及最关键的控制域(发送序号和接收序号),用于实现报文的确认、流量控制和窗口管理。
注意:很多人会混淆APCI和TCP的关系。APCI的序号确认机制是IEC104应用层为了满足电力系统对报文顺序和确认的特定要求而设计的,它运行在TCP的可靠传输之上,是一种“双重保险”。TCP保证了字节流不错不乱地到达,而APCI则保证了应用层信息单元(I帧)的按序确认和窗口控制。
2.2 APCI控制域:会话的节拍器
APCI是IEC104会话管理的核心,其控制域只有4个字节,却至关重要。它定义了三种类型的帧:
I帧(信息传输帧):携带实际数据(ASDU)的帧。控制域的第1、2字节是发送序号N(S),第3、4字节是接收序号N(R)。子站每发送一个I帧,N(S)加1;主站每正确接收一个I帧,在回复的I帧或S帧中,其N(R)应等于刚接收的I帧的N(S)+1,以此确认。这是一个典型的滑动窗口协议,默认窗口大小为12(可配置),即未经确认的I帧不能超过12个。
S帧(确认帧):专门用于确认接收到的I帧,但不携带数据。其控制域第1、2字节为0,第3、4字节为接收序号N(R)。当主站或子站收到一批I帧,但暂时没有数据要回复时,就发送S帧来确认,告诉对方“我已收到截至N(R)-1的所有帧”。
U帧(控制帧):用于建立和释放连接。常见的有:
- STARTDT(启动数据传输):连接建立后,主站必须发送STARTDT act,子站回复STARTDT con,之后才能开始传输I帧数据。这是一个重要的状态切换点。
- STOPDT(停止数据传输):主站发送STOPDT act,子站回复STOPDT con后,双方停止传输I帧,但TCP连接保持。
- TESTFR(测试帧):链路空闲时,用于保持连接活跃的“心跳”。一方发送TESTFR act,另一方必须回复TESTFR con。
实操心得:在调试时,抓包工具(如Wireshark)里看到大量U帧(TESTFR)交换而几乎没有I帧,通常意味着链路已建立但未进行数据召唤或子站无变化数据。而如果看到发送序号不断增长但接收序号迟迟不更新,很可能出现了报文丢失或处理异常,导致发送方窗口耗尽,通信卡死。这时需要检查网络、子站处理能力或主站确认逻辑。
2.3 ASDU结构:信息的标准化封装
ASDU是数据的载体,结构复杂但规整。一个ASDU由数据单元标识符和一个或多个信息对象组成。
数据单元标识符(6字节):
- 类型标识(1字节):定义ASDU的类型,决定了后续信息体的结构和含义。这是解析报文的“钥匙”。例如:
1:单点遥信(M_SP_NA_1)9:测量值,归一化值(M_ME_NA_1)45:单命令(C_SC_NA_1)100:召唤目录(C_IC_NA_1)
- 可变结构限定词(1字节):最高位表示信息对象的数量是单个还是多个(SQ位)。SQ=0表示每个信息对象有独立的地址;SQ=1表示后续信息对象地址连续,仅第一个对象带地址。低7位表示信息对象的数量。
- 传送原因(2字节):说明数据是因何传送。常见原因有:
3:突发(自发上报)5:被请求(响应总召唤)20:响应站召唤6:激活(命令下发)7:激活确认(命令确认)10:激活终止(命令执行失败)
- 公共地址(2字节):即子站(RTU/FTU)的站地址,范围1-65535。用于在一个物理通道上区分多个逻辑子站。
信息对象:由信息对象地址(3字节)和信息元素集构成。对象地址范围很大(3字节=0x000001~0xFFFFFF),提供了灵活的寻址能力。信息元素集则根据类型标识不同而不同,可能包含状态值、带时标的状态值、归一化测量值、浮点数、短浮点数等。
报文解析示例:假设抓到一个报文,其ASDU部分十六进制为:01 01 03 00 01 00 01 00 00 00 00。
01:类型标识=1,代表单点遥信。01:可变结构限定词。二进制00000001,SQ=0(地址不连续),数量=1。03 00:传送原因=3,突发。01 00:公共地址=1,站址为1。01 00 00:第一个(也是唯一一个)信息对象地址=1。00:信息元素(这里就是一个单点状态),00通常表示分位(OFF),01表示合位(ON)。 所以,这条报文的意思是:站址为1的子站,突发上报了地址为1的遥信点,其状态为分位。
3. 通信过程与核心服务详解
IEC104的通信不是杂乱无章的,它遵循一套标准的“会话流程”。理解这些流程,是进行开发、调试和故障排查的基础。
3.1 链路管理与初始化
通信始于TCP三次握手建立连接。连接建立后,IEC104应用层会话才开始:
- 启动连接:主站(客户端)向子站(服务器,端口2404)发起TCP连接。
- 发送STARTDT:TCP连接成功后,主站发送U帧
STARTDT act。子站必须回复STARTDT con。只有完成这个握手,双方才能开始传输I帧数据。这是一个非常关键的步骤,很多通信不通的问题就出在这里——TCP连上了,但没发STARTDT。 - 总召唤与时钟同步:通常,主站在收到
STARTDT con后,会立即发起两个关键操作:- 总召唤(C_IC_NA_1, 类型标识100):主站发送总召唤命令(激活,传送原因6),子站回复激活确认(传送原因7),然后开始将全站所有遥信、遥测等静态数据,以**被请求(传送原因5)**的形式分批上送。全部送完后,子站发送总召唤终止(传送原因10)。
- 时钟同步(C_CS_NA_1, 类型标识103):主站下发当前时间给子站,使子站时钟与主站同步,保证后续事件时标的一致性。
注意:总召唤会带来网络和数据处理峰值。在子站数据量很大(几千点)时,需要合理设置总召唤周期,并确保主站处理性能足够,避免堵塞。有些系统会在初始化时进行一次全量总召唤,后续只依靠突发和变化数据,并定时(如每小时)进行增量召唤。
3.2 平衡式传输与数据流
初始化完成后,链路进入平衡式传输模式,这是IEC104区别于旧式查询-应答规约的最大特点:
主站侧:
- 可以随时下发控制命令(遥控、遥调,类型标识45/46/48/49等)。命令是“激活-确认-执行”模式:主站发激活(原因6),子站回激活确认(原因7),主站再发执行激活(原因6),子站执行后回执行确认(原因7)。
- 可以周期性或手动发起召唤(如召唤二级数据、召唤目录等)。
- 必须及时对收到的子站I帧进行确认(通过回复I帧或S帧)。
子站侧:
- 突发(自发)上传:当发生任何状态变化(如开关变位、保护动作)或模拟量变化超过死区时,子站应立即组织相应的ASDU(如带时标的单点遥信M_SP_TB_1)上送,传送原因为
3(突发)。这是实现实时事件告警的关键。 - 响应召唤:响应主站的总召唤、分组召唤等,以传送原因
5(被请求)上送数据。 - 循环上送:对于一些重要的测量值,子站也可配置为定时循环上送。
- 突发(自发)上传:当发生任何状态变化(如开关变位、保护动作)或模拟量变化超过死区时,子站应立即组织相应的ASDU(如带时标的单点遥信M_SP_TB_1)上送,传送原因为
实操心得:平衡式传输对子站的软件设计提出了更高要求。子站程序不能只是被动应答,必须实现一个主动上报的“事件驱动”机制。同时,要处理好突发数据与响应数据之间的优先级和队列,通常突发数据的优先级最高,以确保事件响应的实时性。
3.3 关键服务原语拆解
控制命令(遥控)流程: 这是一个“选择-执行”或“直接执行”的双步确认过程,以单点遥控(C_SC_NA_1)为例:
- 步骤1:选择主站发送遥控选择命令,ASDU中
QU(限定词)通常为0x80(选择),传送原因=6(激活)。 - 步骤2:确认子站检查对象地址、操作合法性后,回复遥控确认,ASDU内容与主站命令一致,但传送原因变为7(激活确认)。
- 步骤3:执行主站收到确认后,发送遥控执行命令,
QU变为0x00(执行),传送原因=6(激活)。 - 步骤4:终了子站操作执行机构,成功后回复执行确认,传送原因=7(激活确认)。 这个过程确保了控制操作的可靠性和防误性。调试时,务必用报文工具跟踪这四步是否完整,任何一步缺失都意味着操作失败。
- 步骤1:选择主站发送遥控选择命令,ASDU中
召唤流程:
- 总召唤:如上所述,用于获取全站静态数据。
- 分组召唤(C_CI_NA_1, 类型标识102):召唤某一特定组(如1-16组)的数据,用于分组刷新或查看。
- 召唤目录:用于获取子站的信息对象配置清单。
4. 开发与调试实战指南
无论是开发主站测试工具,还是调试子站设备,掌握一套实用的方法都至关重要。
4.1 开发要点与工具选型
主站/测试工具开发:
- Socket通信:基础是TCP Socket编程。建议使用异步非阻塞IO(如Java NIO, .NET Async, Python asyncio)处理多连接。
- 报文组装与解析:这是核心。建议定义好APCI和ASDU的结构体/类,并编写专门的编解码函数。重点处理:
- 字节序:IEC104规定多字节整数(如序号、地址)采用大端序(Big-Endian),即高字节在前。这在x86小端序主机上需要转换。
- 可变结构:根据SQ位灵活解析信息对象数组。
- 定时器管理:实现t0(连接建立超时)、t1(发送或测试超时)、t2(无数据确认时确认超时)、t3(空闲通道测试周期)等协议规定的定时器。
- 状态机:实现一个清晰的链路状态机(初始化、已连接、已启动、数据传输、停止、断开等),并正确处理各种帧的输入输出。
工具推荐:
- 抓包与分析:Wireshark是绝对的首选。它内置了IEC104协议解析器,可以直接将二进制流解析为可读的字段,极大提升调试效率。过滤语句可以用
tcp.port == 2404。 - 测试工具:
- FreyrSCADA IEC104模块:这是一个非常强大的开源SCADA软件组件,既可以作为主站测试工具去连接子站,也可以配置为从站来模拟设备,供主站连接测试。它的从站功能尤其好用,可以灵活配置信息点、模拟变化、响应命令,是开发主站时不可或缺的“假设备”。
- 商业测试软件:如IEC-104 Explorer、Kepware的Advanced Tag Generator等,功能更全面,但通常收费。
4.2 调试流程与常见问题排查
调试IEC104链路,建议遵循以下步骤,可以快速定位问题:
物理与网络层检查:
- Ping测试:确保IP可达。
- Telnet测试:
telnet <子站IP> 2404,看TCP端口是否开放。 - 检查防火墙、路由器ACL规则是否放行了2404端口。
链路建立阶段:
- 抓包观察TCP三次握手是否成功。
- 握手成功后,查看主站是否发送了
U帧(STARTDT act),子站是否回复了STARTDT con。如果没有,检查主站程序逻辑或子站配置。
数据传输阶段:
- 无数据上送:检查主站是否发送了总召唤命令(C_IC_NA_1)。检查子站是否配置了信息点,以及变化数据是否触发了突发上传逻辑。
- 数据错误:对照规约,用Wireshark逐个字段检查ASDU。常见错误:公共地址不对、信息对象地址超出子站配置范围、类型标识与数据不匹配(如用遥测的类型标识发了遥信的数据)。
- 通信中断或卡顿:观察I帧序号。如果发送序号N(S)持续增加,但接收序号N(R)长时间不变,说明接收方没有确认。可能原因:
- 接收方处理太慢,缓冲区满。
- 网络丢包(虽然TCP会重传,但应用层窗口已满)。
- 接收方程序bug,未正确发送确认(S帧或带N(R)的I帧)。
- 控制命令失败:严格按照“选择-确认-执行-终了”四步流程在抓包中核对。常见问题:子站回复的确认帧中对象地址或QU值与命令不一致;主站未收到确认就发了执行;子站返校(检查)逻辑不通过。
4.3 典型问题速查表
| 问题现象 | 可能原因 | 排查思路 |
|---|---|---|
| TCP连接失败 | 网络不通、子站IP/端口错误、子站服务未启动 | Ping、Telnet、检查子站进程 |
| 连接建立后无数据 | 未发送STARTDT、未发起总召唤、子站点表未配置 | 抓包查看STARTDT交换,检查主站初始化流程 |
| 遥信状态不对 | 信息对象地址映射错误、双点与单点类型混淆 | 核对点表地址,检查ASDU类型标识(1是单点,3是双点) |
| 遥测值异常大或小 | 归一化值(NVA)与标度系数转换错误、浮点数字节序错误 | 检查子站上传的原始值,核对转换公式(实际值=原始值*标度系数) |
| 遥控执行失败 | 四步流程不完整、返校不通过、对象地址不可操作 | 抓包分析四步报文,检查子站返校逻辑和对象属性 |
| 通信时断时续 | 网络不稳定、TCP Keep-Alive未生效、t3超时设置过短 | 检查网络质量,在链路上增加TESTFR“心跳”频率 |
| 子站主动断开 | 接收序号不连续、报文格式错误触发子站保护、配置不一致 | 抓包分析断开前的报文,检查双方配置(如APDU最大长度) |
避坑技巧:
- 字节序是魔鬼:在x86/ARM平台开发,处理多字节整数时,务必使用
htonl/ntohl(C语言)或类似函数进行转换。一个错误的字节序会导致地址、序号全部错乱,现象诡异。 - 窗口大小要匹配:主站和子站的发送/接收窗口大小(k, w参数)建议设置为相同值,通常为12。如果一方发得太快(超过对方窗口),会导致对方丢弃报文,链路停滞。
- 妥善处理TESTFR:在链路空闲时,TESTFR的交换是正常的。如果你的程序在空闲时收到TESTFR act,必须回复TESTFR con,否则对方会认为链路故障而断开。
- 点表是关键:主站和子站的信息点表(对象地址、类型、描述)必须完全一致。这是所有数据正确映射的基础。建议使用Excel或专用工具管理点表,并具备导入导出功能。