
简介《南瑞继保网络103规约》是一份面向电力系统自动化领域工程师、调试人员及相关专业学生的协议说明文档围绕IEC 60870-5-103标准的扩展与优化系统讲解远动设备与调度中心之间遥测、遥信、遥控、遥调等“四遥”功能的实现详细覆盖报文结构、主从交互机制、数据编码方式以及命令帧与应答帧的组织形式。资源包内为单个doc文档整体约3.38MB内容还涉及错误处理、网络适应性等工程关注点便于实际项目查阅和对照。目前已有1977人学习下载适合需要掌握网络103规约细节、从事协议二次开发或系统集成工作的电力通信人员。文档结合国内电力系统特殊需求展开对兼容性、安全性、故障处理均有涉及可帮助读者在变电站自动化、调度数据网等场景中快速建立完整认知并为协议调试、异常排查及设备接入提供针对性参考。1. 网络103规约电力远动通信里最绕不开的那层协议做继保装置调试的人几乎都碰过这样的现场主站下发一个遥控指令装置侧报文灯闪了监控后台也显示执行成功可断路器就是没动作。查到最后问题不在二次回路而在报文格式对不上。这份网络103规约资源包就是把这类问题逐帧拆开讲的完整参考。它围绕IEC 60870-5-103标准展开结合某主流继保厂家的工程实现覆盖遥测、遥信、遥控、遥调四类报文的组帧、发送与解析。适合刚接手变电站自动化调试的入门工程师也适合要写规约解析模块或做点表联调的开发人员。2. 链路层与帧结构固定帧长10字节可变帧长最长255先看清规矩再谈解析103规约的传输基础是IEC 60870-5-1定义的FT1.2帧格式。很多人一开始就跳去看ASDU应用服务数据单元结果报文解析总对不上原因就是链路层的帧边界没分清。2.1 FT1.2帧模型与地址域先分清固定帧长和可变帧长FT1.2总共三种帧形态单字符帧、固定帧长、可变帧长。调试中接触最多的是后两种。固定帧长共10个字节结构是启动符68H 帧长度09H 控制域 链路地址 用户数据应用层内容固定2字节 帧校验和 结束符16H。可变帧长则是启动符68H 两个相同的长度字节L L 控制域 链路地址 用户数据可变长 校验和 16H。这里有个现场容易踩的细节可变帧长的长度字节是重复两次的比如68H 10H 10H表示后面从控制域到校验和共16个字节。有些新手写的解析代码只读一个长度字节第二个字节用于校验一致性处理不严谨就会把帧切错位置。链路地址域在103规约里通常对应装置地址不同厂家默认值不一样。我一般会先给装置下发一个总召唤然后在报文里看返回帧的地址域用这个实测值去核对配置表比直接信配置文件里的默认值可靠得多。固定帧长的解析逻辑可以用一段Python快速验证import struct def parse_fixed_frame(raw: bytes): 固定帧长解析68 09 控制域 地址 用户数据 校验 16 输入完整的一帧字节流不含起始符截断 if len(raw) ! 10: return None start raw[0] length raw[1] ctrl raw[2] addr raw[3] user_data raw[4:6] checksum raw[6] end raw[7] # 帧头帧尾合法性校验 if start ! 0x68 or length ! 0x09 or end ! 0x16: return None # FT1.2 校验和是控制域到用户数据的累加和取低字节 calc_sum (ctrl addr user_data[0] user_data[1]) 0xFF if calc_sum ! checksum: return {valid: False, reason: checksum mismatch} return { valid: True, ctrl: hex(ctrl), addr: hex(addr), user_data: user_data.hex(), checksum: hex(checksum), } frame bytes.fromhex(68 09 83 0A 01 00 8D 16) print(parse_fixed_frame(frame))这段代码处理的是10字节固定帧长报文。控制域字段里的bit位决定帧类型主站→从站和从站→主站的控制域含义不一样解析时要结合传输方向看。地址域0x0A如果对不上装置配置的链路地址后续所有响应都匹配不上这是排查的第一站。2.2 主站时序与超时机制总召唤不是一次广播是排队请求103规约是严格的主从问答模式主站发起请求从站响应。总召唤看起来像“广播”实际是主站逐帧下发的单个请求从站按顺序应答。时序上有三组超时参数需要关注工程里通常叫T0、T1、T2T0是链路建立后的最大响应间隔T1是从主站发完一帧到收到从站确认的最大间隔T2是主站连续两帧之间的最小间隔。实际调试中T1设得太短遥信多的装置还没来得及排完队就被主站判超时T2设得太长整轮总召唤耗时拉大影响点检效率。我一般会这样设初值T010sT13sT250ms。装置遥信点数超过500时把T1放宽到5s。这个组合在多数项目中能稳定跑通但也有人把T2设成200ms依然超时——那就不是时间参数问题要从链路质量查起。2.3 厂家扩展与标准差异扩展功能码和一些自定义的服务标准的103规约定义了常用的类型标识号但某主流继保厂家的实现里增加了若干私有功能码用于定值读写、故障录波召唤等扩展服务。这些扩展帧在ASDU里会带厂家私有的类型标识或功能码号段标准协议文档里查不到必须以该厂家的规约手册为准。这份网络103规约资源包的价值就体现在这里资源内包含的报文编码表、功能码号和扩展定义解决的是“标准之外那部分没人讲清楚”的问题。调试时遇到装置上报一个类型标识号解析不了先把报文按16进制抓下来再翻资源包里对应型号的报文定义表大多数情况下能直接对上。3. 应用层与四遥报文总召唤、时钟同步、遥控返校怎么组帧链路层把帧边界定清楚之后真正干活的是应用层的ASDU。四遥功能全部由不同的类型标识号承载组帧、解析、异常处理都围绕这些编号展开。3.1 类型标识号一张表看清遥测、遥信、遥控、总召唤应用层ASDU的第一个字节就是类型标识号调试人员拿到报文先看这个字节就能判断这条帧是干什么的。下表是网络103规约里最常用的一组类型标识号含义数据方向0x01遥信不带时标从站→主站0x03遥信带时标从站→主站0x09遥测归一化值从站→主站0x0B遥测标度化值从站→主站0x0D遥测短浮点从站→主站0x2D (45)遥控选择/执行双向0x64 (100)总召唤双向0x67 (103)时钟同步双向注意0x64和0x67在调试日志里出现频率极高总召唤是每次点检的起点时钟同步是接入调度前的固定动作。这两个帧如果解析出错后续遥信、遥测基本全乱。3.2 总召唤与时钟同步点检流程里的标准动作一次完整的站内点检流程一般是先发时钟同步再发总召唤等装置把所有遥信和遥测上送完再抽几个点做遥控试验。总召唤的ASDU里包含召唤限定词主站发的总召唤通常是0x14全站召唤装置的响应则是先回一个确认帧再按信息体地址顺序上送全部数据。这里有个工程习惯容易被忽略总召唤发起前最好确认链路已经建立可以通过链路测试帧验证。有些人图快装置刚上电就发总召唤装置链路层还没就绪主站侧只能收到噪声。链路测试帧的控制域是0x49返回0x49或0x89都代表链路正常。构造并发送总召唤帧的Python参考写法如下import socket import time def build_total_call_frame(link_addr: int): 构造总召唤可变帧长报文 link_addr: 装置链路地址, 如 0x0A 返回完整帧 bytes ctrl 0x73 # 主站→从站, 启动发送, 要求确认, 功能码总召唤 asdu_type 0x64 # 总召唤 vsq 0x01 # 信息体数量1 cot 0x06 # 传输原因激活 common_addr 0x0A # 公共地址, 与装置配置一致 info_addr 0x00 # 信息体地址, 总召唤用0 call_qualifier 0x14 # 全站召唤 # ASDU: 类型标识 VSQ 传输原因 公共地址 信息体 asdu bytes([ asdu_type, vsq, cot, common_addr, info_addr, call_qualifier ]) # 组装链路层可变帧长部分: ctrl addr asdu payload bytes([ctrl, link_addr]) asdu length len(payload) checksum sum(payload) 0xFF frame bytes([0x68, length, length]) payload bytes([checksum, 0x16]) return frame sock socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect((192.168.1.100, 2404)) frame build_total_call_frame(0x0A) sock.send(frame) resp sock.recv(2048) print(fresp hex: {resp.hex()})这段代码里的ctrl0x73只适用于主站主动发起的总召唤装置主动上报数据的控制域是另一个值。call_qualifier0x14表示全站召唤如果只想召唤某个信息组可以改成对应组号。公共地址common_addr必须和链路地址一致不一致时装置会直接丢弃这条帧且不会回任何错误报文。3.3 遥控选择与返校四遥里最容易出问题的链路遥控的典型流程是选择→返校→执行三步。主站先发一条选择命令控制域特定类型标识45装置收到后校验点号和命令类型合法则返校把收到的命令原样返回主站收到返校后才发执行命令。这个流程里有三个经典故障点第一选择帧里点号写错装置返校时会带一个否定确认主站却锁定了错误的对象第二装置返校后主站必须在规定时间内发执行命令超时或中间插了其他报文执行会被取消第三遥控类型合/分闸编码错误返校帧看起来正常执行后动作方向反了。用代码模拟一个简单的返校响应逻辑def build_select_recall(link_addr: int, point_no: int, operate_type: int): 构建遥控返校帧 point_no: 遥控点号 operate_type: 0分闸, 1合闸 ctrl 0x0B # 从站→主站, 确认帧, 功能码: 响应 asdu_type 0x2D # 遥控 vsq 0x01 cot 0x07 # 激活确认返校 common_addr link_addr # 信息体地址 遥控信息点号 操作类型编码 info_addr_hi (point_no 8) 0xFF info_addr_lo point_no 0xFF operate_flag 0x01 if operate_type 1 else 0x00 qualifier 0x00 # 无附加信息 asdu bytes([ asdu_type, vsq, cot, common_addr, info_addr_hi, info_addr_lo, operate_flag, qualifier ]) payload bytes([ctrl, link_addr]) asdu length len(payload) checksum sum(payload) 0xFF frame bytes([0x68, length, length]) payload bytes([checksum, 0x16]) return framecot0x07是返校专用值主站侧判断遥控流程是否完成主要就是看这个字段。现场如果出现主站显示“返校超时”而报文里明明有返校帧八成是公共地址或点号编码方式不匹配。不同厂家对点号的编码位数有差异有的用双字节低字节在前有的高字节在前必须对照资源包里的点号映射表确认。4. 调试环境与报文巡检从装虚拟主站到用脚本跑一致性核查规约调试最痛苦的是没有主站配合。单装置调试阶段搭一个虚拟主站环境把报文抓下来做自动化检查能省出大量联调时间。4.1 虚拟主站与装置配置先起一套可回放的调试环境虚拟主站的核心工作是三件事建立与装置的TCP连接或串口、按规约格式下发命令、解析并记录返回报文。环境搭建时先在装置侧把网络参数配置好——IP地址、端口号2404是默认端口也有厂家用其他端口、链路地址、公共地址。这些参数不一致时装置链路层直接丢弃报文虚拟主站表现就是“连接正常但无响应”。连上之后第一步永远不是发总召唤而是先做链路测试。这个习惯救过我很多次TCP连接看似建立但装置的应用层还没准备好直接发总召唤会让对方链路层连续回否定帧。链路测试通过后再走时钟同步和总召唤报文就顺了。4.2 报文巡检脚本把日志里的hex变成可统计的表格虚拟主站抓下来的报文是一大堆hex串人工逐条核对效率太低。我习惯用一个简单的Python脚本把日志按帧切分、统计ASDU类型并标出可疑帧脚本如下import re from collections import Counter def parse_frame_log(log_text: str): 解析主站日志, 按帧切分并统计ASDU类型 log_text: 每行一个 hex 报文, 以空格分隔 frames [] pattern re.compile(r68\s[0-9A-Fa-f]{2}\s[0-9A-Fa-f]{2}) for line in log_text.splitlines(): line line.strip() if not line.startswith(68): continue parts line.split() # 可变帧长: 68 L L 控制域 地址 ... 16 if len(parts) 4 and parts[1] parts[2]: length int(parts[1], 16) # 实际数据区长度应等于 length - 2 (控制域和地址) data parts[3:] if len(data) length: frames.append(line) asdu_counter Counter() for f in frames: parts f.split() # 可变帧长: 68 L L ctrl addr userdata... if len(parts) 7: user_data_start 5 user_data parts[user_data_start:] # 用户数据第一个字节即ASDU类型标识 if user_data: asdu_type int(user_data[0], 16) asdu_counter[asdu_type] 1 return len(frames), asdu_counter with open(virtual_master.log, r) as f: log_content f.read() total, stats parse_frame_log(log_content) print(ftotal frames: {total}) for asdu_type, count in sorted(stats.items()): print(fASDU 0x{asdu_type:02X}: {count})这段脚本的核心思路是先用长度字段做帧边界校验再取用户区第一个字节做类型统计。实际使用中发现日志里有些帧会因为TCP分包被拆开脚本会把这些半截帧丢弃统计数量会小于实际值。要拿到完整帧最好在抓包时就按链路层长度做重组而不是事后从日志里硬切。脚本跑出来的统计结果用来做一致性核查把每个ASDU类型的数量与点表里登记的遥信数、遥测数对比差值超过容忍范围就说明有丢帧或漏报。4.3 参数核对表该按装置型号改的五个地方虚拟主站跑不通时多半不是脚本问题而是装置侧参数不对。按下面的顺序核对一遍能解决绝大多数“无响应”和“响应乱序”链路地址装置配置里填的是十进制报文里是十六进制0x0A对应10经常有人填错进制公共地址与链路地址一致是多数厂家的默认行为但部分型号允许两者分开设置不一致就会丢帧端口号2404是常见默认值个别项目改成其他端口主站侧没同步改就连接失败字节序信息体地址、时间戳的高低位顺序各厂家实现有差异解析结果出现天文数字时优先怀疑这里报文超时装置侧的上行超时和主站侧的下行超时是两组独立参数只调一边治不好问题。5. 常见问题排查四个反复出现的坑现象、原因、处理以下是调试网络103规约时反复遇到的四类问题按“现象→原因→解决”的顺序整理每条都来自实际现场。5.1 遥信刷新失败点表没错但总召唤就是不来现象总召唤发出后装置确认帧正常返回但遥信数据迟迟不上送。监控后台显示遥信全部为旧值部分点位显示未知。原因链路层正常但装置侧的信息体地址与主站侧的点表映射不一致。多数情况下是装置侧配置文件里点号做了偏移比如从1开始而主站点表从0开始或者信息体地址编码位数不一致导致装置按自己的地址表上送主站却按另一套映射解析。解决先从装置侧导出实际的点表文件确认信息体地址和点号的对应关系再在主站侧重新映射。调试时不要只看第一个遥信点要把最后几个点也测一下地址位溢出只会影响高位点不容易被发现。5.2 遥控返校超时主站显示成功装置侧没动作现象监控后台显示遥控执行成功装置侧报文灯亮但断路器没有分合闸动作。原因这种问题往往出在“返校”的解析上。主站把装置返回的普通确认帧误认为返校成功实际装置发出的可能是一个否定确认传输原因不是0x07或者遥控选择帧中的操作类型编码与装置规定不符装置拒绝执行但错误码被主站忽略。解决先把报文抓下来定位遥控选择帧和响应帧核对返校帧里的传输原因字段。不是0x07就说明装置没有确认执行。接着检查操作类型编码有的厂家用0x01表示合闸、0x00表示分闸有的正好相反组帧前必须查装置的编码定义表。5.3 链路CRC错误率高串口线缆和接地比协议本身更坑现象串口模式下报文大量校验错误部分帧直接丢失装置偶尔自动重启通信。网络模式下没有这个问题但换成串口就频繁出现。原因FT1.2的帧校验和算法很简单——累加和取低字节本身查错能力有限。串口接线过长、屏蔽层未接地、或者现场有大功率设备启停时干扰信号叠加在数据线上导致误码。协议本身没问题问题出在物理链路的电气质量。解决先量串口线两端电压差确保共地。屏蔽层单端接地不能两端都接。数据线改用屏蔽双绞线网络模式下不存在这个问题所以网络103规约替换串口后会直接“治好”这类故障。如果工程条件上必须留串口建议加光电隔离器。5.4 时钟同步失败对时报文和总召唤报文抢通道现象每次下发时钟同步后紧接着的总召唤数据里有一批遥信带时标的时间戳是乱的而且主站显示对时成功但报警时间不正确。原因主站在短时间内连续下发对时和总召唤装置还在处理对时帧时总召唤已经进来遥信帧里的时标是装置缓存里的旧值。这是典型的时序竞争问题。解决把两个命令间隔开。先发时钟同步确认收到对时确认帧后再发总召唤等待35秒让装置内部完成时标刷新。规范里的流程顺序不是随便定义的按这个顺序走时标错误基本消失。6. 应手技巧报文重放与闭环验证改完点表后的第一道工序调试103规约我最推荐的动作是“报文重放验证”——把抓包工具里录到的一整段主站下发报文保存下来修改链路地址和公共地址后原样发给同型号的装置观察新装置能否复现相同的响应序列。这个方法能快速定位是主站问题还是装置问题也能在批量调试时省去逐台手动操作的重复劳动。具体操作就三步第一用虚拟主站或抓包工具录制一段完整流程链路测试→时钟同步→总召唤→单点遥控第二用脚本或编辑器批量替换报文里的链路地址和公共地址字节第三把修改后的报文一条条发到目标装置上对比两端的响应帧。如果原装置响应正常、目标装置响应异常问题就锁定在装置侧配置差异上。这个技巧我基本每次调试都会用到尤其是多台同型号装置、点表有细微差异的时候。重放验证比逐条手敲命令快得多也方便留档谁改过什么报文、改了什么地址都有据可查。2020年有一次在现场调试因为点表映射错误导致某个间隔的遥控死活调不通查了两天没结果。后来强制自己走了一遍录波重放流程把现场录的报文和厂内测试台的报文逐字节对比才发现是装置程序升级后遥控点号编码从单字节改成了双字节主站侧还按旧协议在发单字节点号。从那以后我每次改完点表或升级完装置程序都强制走一遍“报文重放→闭环验证”的流程先重放旧报文确认兼容性再用新点表发一轮总召唤单点遥控全部通过才算调试完成。这套流程听起来朴素但确实能拦住大多数规约层面的隐藏问题。希望帮到你在变电站现场少走点弯路。本文还有配套的精品资源点击获取