干了这么多年变电站的通信调试,我越来越觉得,很多工程问题不是出在规约本身,而是出在“你以为你懂规约”。就拿南自系保护装置的以太网103来说,不少新手拿着串口103的配置思路去调网络版,结果TCP连接明明建立了,总召唤就是没有响应,现场一夜都别想好过。
实际上,以太网103和串口103的底层逻辑已经变了。它不再是简单的“波特率、奇偶校验、站地址”三件套配置,而是一套由TCP/IP负责传输、FT1.2帧负责组包、ASDU负责业务语义的分层协议栈。这篇内容我从主站和子站的连接建立、报文字节结构、总召唤流程到现场高频坑位完整拆一遍,希望能帮到正在调保护装置接口的自动化工程师,也让刚想入门的同学知道这类规约到底是怎么跑起来的。
1. 103规约为什么会被搬上以太网:它本来不是写给网线用的
1.1 103的“串口基因”
IEC 60870-5-103规约诞生的年代,变电站站控层的主流物理通道还是RS-232和RS-485,链路层用FT1.2帧格式,主站与子站就是典型的一主多从或者点对点连接。这条规约的主要任务是让监控后台能拿到保护装置的遥信、遥测、SOE事件、故障录波信息,同时下发遥控和定值操作命令。由于它面向的是保护设备,所以对报文中的“时间标签”和“事件顺序”要求特别严格,这也是103和电力系统里很多其他规约最大的区别之一。
有意思的是,103虽然定位在“保护设备通信”,但它的链路层沿用了IEC 60870-5-101最早的一套框架。这就导致大家在串口时代调试103时,核心精力都放在串口参数上:波特率对不对、校验位是什么、从站地址是否越界、主站轮询周期合不合理。当时的工程经验是“地址对了一切都好说”,这句话放到今天的以太网103场景里,只对了一半。
1.2 站控层以太网化之后,为什么还要沿用103
新一代变电站站控层早就全面以太网化了,保护装置也普遍配备百兆或千兆电口。理论上大家可以直接上IEC 61850,但问题在于大量的存量装置、第三方监控后台、保信子站,短时间内不可能全部迁移。为了让老设备和新网络共生,最现实的办法就是做一套“以太网版的103”:
- 保留原103协议的应用层语义和ASDU格式,降低软件改动量。
- 将链路层的传输通道从串口替换为TCP/IP。
- 去除串口链路特有的物理层握手和奇偶校验机制。
换句话讲,你就把原来串口线上跑的字节流,原封不动地塞进一个TCP连接里。代码改起来简单,但这背后引发了一个很隐蔽的问题:串口和网口对“帧边界”的定义不同。串口上一帧数据从启动字符到结束字符有明确的停止位和空闲间隔,TCP则是一个无边界的字节流,帧边界必须靠应用层自己识别。这也是后面很多通信故障的真正源头。
1.3 南自系设备的以太网103实现面貌
南自系(包括早期南自院的PSL系列保护、后来整合的各类测控装置)在以太网103的实现上,基本思路是:保护装置作为TCP服务端,在某个固定端口上监听;监控后台或保信子站作为TCP客户端主动建链。连接建立后,装置侧会周期发送链路测试帧或者等待主站发起总召唤,应用层报文依然维持103那套ASDU结构。
但这里要特别提醒:以太网103至今没有一个像IEC 61850那样严格的国际标准来约束厂商细节。同一个“以太网103”的名字下,不同厂家的端口号、控制域定义、公共地址编码方式,甚至是否保留校验和都可能不同。所以做现场调试之前,第一件事不是打开配置工具乱填IP,而是找厂家要对应版本的规约说明,搞清楚它到底属于“完整FT1.2帧包裹”还是“只传ASDU裸数据”。
这部分决定了后续所有调试思路的方向。我见过太多人先入为主觉得“规约都差不多”,结果把一个TCP粘包问题误判成公共地址错误,折腾一晚上。
2. 建链与保活:主站和子站是怎么把TCP会话维持住的
2.1 谁是Server,谁是Client,方向错了连不上
以太网103最常见的组网方式,是保护装置固定监听一个服务端口,主站侧软件(监控后台、保信子站、规约转换器)主动去连它。也就是说,每个保护装置是一个TCP Server,IP地址是唯一的,砖头一样放在那里等人来连。
不过我也见过反过来的场景。有些厂商的通信管理机或者保信子站希望自己作为Server,让下面的保护装置按预设的IP和端口主动上连。这种方案的好处是主站侧不需要维护庞大的设备接入状态,装置挂了重新上电还会主动重连,但也带来了配置和管理上的复杂性。
现场调试的第一步永远是确认连接方向。你可以用netstat或者wireshark抓包观察,看SYN包到底是从主站发出还是从子站发出。很多“连不上”的问题,不是IP不通,也不是网线没插好,而是两端都在等对方先说话。
2.2 连接建立后的心跳机制:有连接不等于活着
TCP连接建立起来之后,两边并不会立刻开始传业务数据。主站一般会先发一个链路测试帧(有的实现是总召唤,有的实现是控制域里特定功能码的测试命令),试探子站是否在线。子站收到后返回一个确认帧。这个频率通常在5秒到30秒之间,不同厂家的缺省值不一样。
如果子站连续多次没有响应,主站会判定链路中断,然后进入重连状态,不断尝试重新发起TCP连接。这里有个经验值:主站侧的超时时间一般设置为发送周期的2到3倍,太短容易被网络抖动误伤,太长又会拖慢故障发现速度。我之前处理过一次现场频繁掉线的问题,最后发现是子站程序里有一个看门狗,每隔30秒才发一次链路测试帧,而主站侧8秒没收到数据就判断链路断了,两边节奏完全不在一个频道上。步调不一致导致的“假死”局面,非抓包根本看不出来。
2.3 端口、公共地址和设备IP之间的关系
很多新手会把“端口”和“公共地址”搞混。端口是TCP传输层的概念,它决定数据包交给哪一个应用程序处理;公共地址在103规约里是应用层的概念,出现在ASDU字段中,用来标识不同的子站或装置。IP负责在网络层找设备,端口负责在设备上找服务进程,公共地址负责在装置内部确认“这批数据是给谁的”。
一个保护装置可能配置了多个网络口,每个网络口上跑着不同的规约服务,对应不同的TCP端口。比如用于监控后台的以太网103监听在2404,用于保信子站的扩展103监听在另一个高端口。端口号不同,应用层解析逻辑完全不一样。所以在配置主站时,不仅要把子站的IP填对,还要把端口填对,同时把ASDU里的公共地址设成和装置一致。三者缺一不可。
从实际工程看,很多老装置的公共地址支持范围是1到254,和串口时代的地址范围一脉相承。但网络化之后,IP本身就承担了寻址功能,公共地址渐渐变成一个“合法性校验项”,不再承担路由选择。有的实现甚至不检查它,只要TCP连接在,ASDU里的公共地址随便填也能通信。这种兼容性的差异,恰恰是调试中最坑的地方。
3. 报文结构拆解:从0x68到ASDU,一帧数据到底怎么组装出来的
3.1 典型的可变长帧格式长什么样
以太网103虽然在物理层改成了TCP/IP,但很多实现依然沿用了FT1.2风格的可变长帧骨架。一个典型的报文是这样组成的:
- 启动字符 0x68
- 长度标识符L、L
- 启动字符 0x68(重复)
- 控制域C
- 公共地址A
- 应用服务数据单元ASDU
- 校验和(部分以太网实现会省略)
- 结束字符 0x16
长度标识符L表示从控制域开始到ASDU结束的字节总数,通常占两个相同字节。这是为了在串口帧中出现连续0x68时也能正确识别帧边界。到了以太网上,由于TCP是流式传输,粘包和半包问题比串口严重得多,接收方必须靠长度L来精确截取一帧,否则解析就会错位。这部分是实现主站解析软件时的核心逻辑,很多人调试“为什么数据乱码”到最后,发现根本不是编码问题,而是按固定偏移量读帧,没等长度字段就硬切,一帧错,帧帧错。
3.2 控制域里的门道:谁在说话,这一帧是干嘛的
控制域在103链路层里是最容易忽略、但又最关键的一个字节。它的位定义从高到低包括方向位DIR、启动标志位PRM、帧计数位FCB/FCV,以及一组功能码。方向位为0表示主站到子站方向,为1表示子站到主站方向。PRM为1表示这一帧是启动侧的请求,为0表示是从动侧的应答。
常见的功能码逻辑:
- 主站发送用户数据(带请求确认)时,功能码通常为3。
- 子站正确接收并校验无误后,回应答帧,功能码为0或1。
- 子站主动上报事件数据时,功能码为8或9之类,表示“这是响应数据”。
这里不打算让大家死记硬背每一个二进制位,因为在以太网103实现里,控制域的细节实在太杂,有的厂家的FCB位随帧交替从0变成1,有的则完全固定不变化。真正有效的调试办法,是找到一套抓包工具,先把主站侧发出的控制域和子站侧返回的控制域各抓几组,对照着看方向位和功能码的变化规律。只要回包控制域的PRM位为0,就能确认它是子站的应答,再结合ASDU的内容判断这一帧的业务类型。
3.3 ASDU字段:真正的业务数据在这
应用服务数据单元(ASDU)是整个报文的灵魂,它承载着103规约的具体业务语义。ASDU一般由以下几个字段构成:
- 类型标识:用1个字节表示数据类别,比如总召唤、单点遥信、带时标的双点遥信、测量值、时钟同步、遥控命令等。
- 可变结构限定词(VSQ):表示当前ASDU里包含的信息对象个数,以及这些信息对象是连续排列还是离散排列。
- 传送原因(COT):表示这一帧的触发原因,最典型的是6(激活)、7(激活确认)、10(激活终止)、3(突发事件)等。
- 公共地址:用于标识子站地址。
- 信息体地址:表示具体的数据点地址,比如某个遥信点号、某个遥测点号。
- 信息体数据:根据类型标识的不同,可能是单点状态值、双点状态值、浮点测量值、时标等。
这里面的难点在于,103规约的ASDU很多是从IEC 60870-5-101/104体系里继承过来的,类型标识在不同厂家的产品中可能有复用和扩展。例如带时标的单点信息在101体系里常见的类型标识为30,不少以太网103实现也沿用了这个编号。但不要默认所有厂家都一致,还是要回到装置的规约手册里去核对。
3.4 实例拆解:一帧总召唤请求的字节级读法
下面我们拿一帧典型的“主站发起总召唤”报文来做个逐字节分解,这里的例子按通用FT1.2帧加常见的101/104公共ASDU风格来展示:
68 0B 0B 68 53 01 64 01 06 00 01 00 00 00 00 30 16
逐字节解释:
- 68:启动字符。
- 0B 0B:从控制域到ASDU结束一共11个字节。
- 68:第二个启动字符。
- 53:控制域,方向位为0表示主站发送,PRM为1,功能码为3,代表发送用户数据并期望确认。
- 01:公共地址,对应子站地址1。
- 64:类型标识,十进制100,总召唤命令。
- 01:VSQ,表示只有一个信息对象。
- 06 00:传送原因,低字节在前,表示“激活”。
- 01 00:公共地址,低字节在前,和前面控制域后的地址口径一致。
- 00 00 00:信息体地址,全0表示对所有信息点进行总召唤。
- 30:校验和。
- 16:结束字符。
这一帧看着简单,但它在整个通信流程中非常重要。子站收到这一帧之后,如果校验通过、公共地址匹配,才会进入总召唤的数据上送流程。如果这里任何一个字段不匹配,子站就可能悄无声息地把这帧丢掉,主站侧的表现就是“我发了总召唤,它不回应”。
4. 从总召唤到遥控:主站与子站之间的典型交互流程
4.1 主站启动后的第一件事:先建立上下文
TCP连接建立之后,主站不会立刻收数据,而是先做设备上线初始化。大多数主站程序会先发一条链路复位或者链路测试命令,再发送总召唤。这么做的目的,是让子站进入一个已知的初始状态,清空之前可能残留的缓存或者中间态。
有些主站还会在总召唤之前先做一次时钟同步,确保事件时标的基准一致。因为103规约对SOE事件的时标要求很严格,时钟不同步会导致故障分析时的顺序错乱,后期排查会很头疼。
初始化顺序在不同厂家产品里略有差异,但基本的逻辑是:链路确认、时钟同步、总召唤、等待全数据、之后进入周期刷新和事件监听。
4.2 总召唤的完整交互过程
总召唤不是一个一来一回的过程,而是三个阶段:
- 主站发送总召唤请求,传送原因置为“激活”(6)。
- 子站校验通过后,先回一帧“激活确认”(7),表示我收到并接受总召唤。
- 子站接着把当前所有信息点数据按地址顺序逐一上送,这些数据帧的传送原因是“响应总召唤”(20)。
- 所有数据发送完毕后,子站再发一帧“激活终止”(10),告诉主站“所有数据都发完了”。
这里要特别注意第4步。主站软件通常是以收到“激活终止”来判断一轮总召唤是否完成的。如果子站数据很多,上送过程可能持续几秒甚至更长;如果中途网络丢包导致“激活终止”丢了,主站会认为总召唤尚未完成,直接超时。这也是103以太网通信里最常见的故障现象之一。
4.3 周期遥测和突发变位:两种完全不同的数据路径
总召唤完成之后,系统进入正常运行状态。遥测数据一般按照主站配置的周期进行周期上送,例如2秒或5秒一次。这部分数据的传送原因通常是“周期”,个别实现也直接使用循环数据类ASDU。
遥信变位和SOE事件则属于突发路径。子站检测到开关变位,会主动向主站推送带时标的事件报文,传送原因一般置为“突发”。这类报文的优先级高于周期遥测,如果多个事件同时发生,子站会按内部优先级排队上送。
这里有一个工程经验:如果子站在同一时刻产生了大量SOE事件,以太网103实现又采用的是独立TCP连接,突发流量可能会挤压正常的总召唤通道。我曾经在故障录波调阅时发现,主站那边数据刷得飞快,但总召唤始终完不成,最后查出是子站把事件上送和总召唤响应都放在同一条连接上,又没有做优先级调度,大量事件把总召唤的数据帧挤在后面了。
4.4 时钟同步和遥控命令的特殊性
时钟同步在103规约里不是随便发一帧就完事的。主站发送时钟同步命令后,子站不仅要把本地时钟设置过去,还要记录命令的发出时间,以便计算通道延时。部分实现中,子站还会在确认帧里带回时间戳,让主站进一步校准。
遥控命令则更严格。常规做法是分选程和执行两步:
- 主站下发“遥控预置”(选择)命令,传送原因置为激活。
- 子站校验该点位是否可遥控、是否匹配,然后返回“激活确认”。
- 主站在规定时间内下发“遥控执行”命令,子站输出执行,返回执行确认。
- 如果超时未收到执行命令,预置状态自动撤销。
如果子站返回的是“未知类型标识”或“未知信息体地址”,说明主站下发的遥控点号和装置内部定义不一致,或者是公共地址不匹配。这种问题通常在调试阶段就能暴露,但有些工程在系统未送电时只测试了遥信遥测,等真正需要遥控断路器的时候才发现点位没映射对,那就会非常被动。
4.5 多主站访问时的地址隔离
保护装置一般不只被一个主站连接,尤其是保信子站和监控后台可能同时需要访问。多个主站同时连接同一台装置时,如果装置在TCP层只允许一条连接,后来的主站会被拒之门外;如果允许多条连接,则需要考虑总线召唤是否互相干扰。
有些装置内部对每个TCP连接维护独立的总召唤状态,有些则是全局唯一的。如果是全局唯一状态,一个主站发起总召唤,另一个主站也在等总召唤完成,就会有一方一直等不到数据。我在现场碰到过这种场景:后台监控先发起总召唤,保信主站随后也发起总召唤,结果其中一侧老是不出数据。后来我们把两侧的轮询周期错开,问题才消失。
5. 现场最常踩的五个坑:从现象到排查思路
5.1 TCP连接一直建立不起来
现象:主站日志显示到子站的连接超时,TCP重传一直持续。
排查链路:
- 先ping子站IP,不通就是物理链路问题,换网线、查交换机端口、查VLAN。
- 通了但还是连不上,就用telnet测试子站端口是否开放。
- 端口不通时登录子站后台,查规约服务进程是否启用,监听端口是否和主站配置一致。
- 如果子站和主站之间隔了防火墙,还要确认放行策略。
这个坑一般不是难在技术,而是难在沟通。变电站调试时,装置物理端口和逻辑端口由不同专业人员管理,主站侧配的是2404,装置侧开的却是2405,两边都觉得自己没错。
5.2 TCP能建立,但总召唤发出去毫无回应
这是最隐蔽的一类问题。连接建立成功,说明网络层没问题,但子站对应用层报文“不感冒”,主要原因有三个:
- ASDU里的公共地址和装置配置不一致,子站把帧校验后直接丢弃。
- 类型标识或传送原因不被子站支持,或者使用的是厂家私有的扩展类型。
- 控制域里的PRM、FCB位判断异常,子站不知道这是发给自己的请求。
排查方法很简单,用抓包工具把主站发出的那帧总召唤请求解析出来,和子站规约说明书里的定义逐字节比对。不要靠猜,直接看字节最靠谱。
5.3 数据大部分正常,但总是隔一段时间掉线又自恢复
现象:监控画面数据中断几十秒,然后又恢复正常,循环反复。
这类问题多数不在规约逻辑,而在TCP层面的连接管理。常见原因:
- 子站在一段时间内没有收到任何应用层报文,主动关闭了连接。
- 主站侧虽然认为连接还活着,但中间的网络设备(比如交换机端口、串口服务器)因为空闲超时把连接回收了。
- 子站程序有内存缓冲溢出,长时间运行后进程卡死,看门狗复位后连接重来。
遇到这种情况,光调规约参数是没用的。先看两端日志里的断开时间点,再往前倒推网络空闲时间,把心跳周期调小或者缩短链路测试帧间隔,一般来说能解决大部分“隔段时间掉线”的诡异问题。
5.4 遥控命令发出后子站无动作,但遥信遥测都正常
现象:主站下发遥控预置,界面提示成功,但装置没有任何出口动作。
这类问题通常不是网络层,而是应用层映射。需要检查四件事:
- 主站配置里遥控对象对应的信息体地址是否和装置定义一致。
- 遥控选择的控制模式是单点还是双点,是否和装置侧匹配。
- 装置侧是否配置了遥控出口软压板,或者硬压板未投入。
- 多主站场景下,另一个主站是否有闭锁控制功能。
曾经有个现场,主站和装置在纸面上点表完全一致,遥控预置也返回了确认,但出口始终不动作,最后发现装置内还有一个“就地/远方”切换把手,当时处于就地位置。这种问题跟规约一点关系没有,但通信调试人员往往要陪到最后一刻。
5.5 抓包时数据正常,不在抓包时就有问题
这个现象听起来很玄学,但原理很简单:抓包工具本身影响了通信时序。wireshark或tcpdump在抓包时会给系统带来额外的CPU和内存负载,一些处理能力较弱的装置,或者主站侧规约解析程序线程比较脆弱的场景,本身已经处于临界状态,抓包只是压垮它的最后一根稻草。
另一个可能原因是在抓包过程中,主站侧的接收缓冲被暂时撑大,粘包半包问题被掩盖,误以为是网络原因导致的数据丢失。如果排除完所有逻辑和配置问题后,只在抓包时正常,那就要关注代码层面是否有缓冲区溢出、线程同步和内存竞争的问题。
写在最后的一点经验
这些年在103和104规约上踩过的坑,让我养成一个习惯:不论厂家兼容性有多好,配置完第一件事永远是抓一包原始报文,从头到尾按字节读一遍。不要急着点“连接”,不要急着看画面,先把链路层的帧和ASDU字段读懂,再去调业务逻辑。
还有个小技巧:如果现场允许,尽量在主站和子站之间加一台镜像交换机,把长期抓包变成常态。很多规约排查的难点不是问题本身,而是“问题发生时你恰好没看到”。有了连续抓包数据,很多偶发掉线、总召唤超时的问题,都能在事后翻报文时找到决定性的证据。做电力通信调试这行,耐心比技术更贵重,规约本身不复杂,复杂的是它周围那一圈千奇百怪的现实条件。