首页 / 资讯中心 / 文章详情

南瑞继保网络103规约实战:从报文解析到四遥调试

南瑞继保网络103规约实战:从报文解析到四遥调试 ★ FEATURED ARTICLE
简介南瑞继保网络103规约是电力系统通信领域的重要技术资料面向从事变电站自动化、远动通信及调度集控系统设计、调试与维护的工程技术人员。该资料基于IEC 60870-5-103标准结合南瑞继保的技术特点进行优化扩展重点阐述主从通信结构下遥测、遥信、遥控、遥调等四遥功能的实现机制并涉及兼容性扩展、安全性增强、性能优化、故障处理及网络适应性等关键内容。资源包内含1个doc文档大小约3.38MB内容涵盖规约结构、命令帧与应答帧格式、错误处理及数据编码解码等核心知识点便于读者系统理解南瑞继保103规约的具体细节。目前已有1980人学习下载适合需要掌握该规约以解决实际工程问题的技术人员参考也可为二次开发与系统集成提供协议层面的支撑。1. 南瑞继保网络103规约从串口到TCP的远动通信落地拆解如果你手头有一台南瑞继保的继电保护装置或测控单元需要把遥测、遥信、遥控、遥调这“四遥”数据接进后台监控或调度主站那你大概率绕不开103规约。它脱胎于IEC 60870-5-103标准原本是面向继电保护设备的串行通信规范后来被扩展出网络承载方式也就是常说的“网络103”。和标准103相比南瑞继保在功能码、报文格式、初始化流程上做了不少工程化调整这些调整在公开文档里往往语焉不详调试时容易卡在“链路能通但数据对不上”的玄学状态。这份资源把规约的帧结构、交互时序和典型参数配置拆开讲适合刚接触电力远动通信的调试人员也适合需要做协议对接的二次开发工程师。下面从报文结构开始一步步走到能抓包、能解析、能排错的实操层面。2. 网络103的报文结构与链路建立从固定帧到可变帧的完整交互2.1 固定帧与可变帧的格式差异103规约的报文分两类固定帧和可变帧。固定帧长度固定用于链路控制比如请求链路状态、确认、复位可变帧长度随数据内容变化用于传输遥测、遥信等业务数据。理解这两类帧的格式是抓包分析的第一步。固定帧的典型结构如下字段长度说明启动字符1字节固定为0x10控制域1字节包含方向位、帧计数位、功能码地址域1字节从站地址1~255校验和1字节从控制域到地址域的算术和取模256结束字符1字节固定为0x16可变帧在启动字符后多了长度域并且数据区可以承载多个信息对象。控制域的功能码决定了这次交互是“发送/确认”还是“请求/响应”。实际调试中最常见的翻车点是地址域配错——主站发下去的地址和从站实际地址不一致从站直接不响应抓包看到的就是主站反复重发固定帧链路始终建不起来。2.2 链路初始化与复位流程网络103在TCP承载下链路建立分两步TCP连接建立和规约层链路复位。TCP连接由操作系统完成规约层复位则需要主站发送复位帧功能码0x40或请求链路状态帧功能码0x49。从站收到后回复确认帧功能码0x00或链路状态帧链路才算进入可用状态。下面是一段用Python模拟主站发起链路复位的代码基于socket直接发送原始字节import socket import time def build_fixed_frame(control, address): 构造固定帧 control: 控制域字节 address: 从站地址 frame bytearray() frame.append(0x10) # 启动字符 frame.append(control) # 控制域 frame.append(address) # 地址域 checksum (control address) % 256 frame.append(checksum) # 校验和 frame.append(0x16) # 结束字符 return bytes(frame) def reset_link(host, port, slave_addr): 发起链路复位并等待确认 sock socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect((host, port)) # 复位帧控制域0x40表示复位方向位为0主站发起 reset_frame build_fixed_frame(0x40, slave_addr) sock.send(reset_frame) print(f发送复位帧: {reset_frame.hex()}) # 等待从站确认 try: resp sock.recv(1024) print(f收到响应: {resp.hex()}) if resp[1] 0x00: print(链路复位成功从站已确认) elif resp[1] 0x80: print(从站返回链路忙需等待后重试) except socket.timeout: print(超时检查IP、端口、从站地址是否匹配) finally: sock.close() # 示例调用从站地址设为1端口通常为2404 reset_link(192.168.1.100, 2404, 1)这段代码里build_fixed_frame负责拼装固定帧校验和只累加控制域和地址域不包含启动字符和结束字符这是103规约的硬性规定算错校验从站直接丢帧。reset_link里控制域传0x40对应复位功能如果从站回复的控制域是0x80说明从站当前忙需要等一个超时周期再重发。实际工程中我一般会在复位失败后重试三次间隔500毫秒避免网络抖动导致的偶发失败。2.3 可变帧的解析与信息对象定位链路建立后主站开始发送可变帧请求数据。可变帧的结构比固定帧复杂长度域之后是数据区数据区里可以包含一个或多个信息对象。每个信息对象由信息对象地址IOA、信息元素集和时标组成。解析可变帧的关键是正确计算长度域和定位每个信息对象的起始偏移。下面是一个解析可变帧的Python函数重点处理长度域和IOA提取def parse_variable_frame(raw): 解析可变帧返回信息对象列表 raw: 完整帧字节 if raw[0] ! 0x10: raise ValueError(启动字符错误) length raw[1] # 长度域 control raw[2] # 控制域 address raw[3] # 地址域 data_area raw[4:4length-2] # 数据区减去控制域和地址域 checksum raw[4length-2] end raw[4length-1] if end ! 0x16: raise ValueError(结束字符错误) # 计算校验和 calc_sum (control address sum(data_area)) % 256 if calc_sum ! checksum: raise ValueError(f校验和错误: 计算值{calc_sum}, 实际值{checksum}) # 解析信息对象ASDU类型在data_area[0] asdu_type data_area[0] objects [] offset 1 while offset len(data_area): ioa int.from_bytes(data_area[offset:offset3], little) offset 3 # 根据ASDU类型决定信息元素长度这里以遥测为例 if asdu_type 0x09: # 测量值带时标 value int.from_bytes(data_area[offset:offset2], little) offset 2 quality data_area[offset] offset 1 timestamp data_area[offset:offset7] offset 7 objects.append({ioa: ioa, value: value, quality: quality}) else: # 其他类型按实际长度处理 break return {control: control, address: address, asdu_type: asdu_type, objects: objects} # 示例解析一帧遥测数据 frame_hex 100a010109020100000000640000000000000016 result parse_variable_frame(bytes.fromhex(frame_hex)) print(result)这里length字段包含了控制域、地址域和数据区的总长度所以数据区的切片是raw[4:4length-2]。IOA是3字节小端序遥测值2字节小端序品质位1字节时标7字节。实际调试时如果发现解析出的IOA和点表对不上先检查字节序——有些装置用大端有些用小端这个没有统一规定只能拿实际报文比对。我习惯在解析前先打印原始hex对照点表逐个核对IOA确认无误后再批量处理。3. 四遥功能的代码实现遥测、遥信、遥控、遥调的请求与响应3.1 遥测与遥信的周期上送与召唤遥测测量值和遥信状态量是103规约里最常用的两类数据。网络103下从站可以周期上送也可以由主站召唤。周期上送时从站主动发可变帧主站只需解析召唤方式则是主站发请求帧从站回复对应数据。召唤遥测的请求帧ASDU类型通常为0x09测量值或0x0D测量值带时标。下面代码演示主站召唤遥测并解析响应def call_measurement(sock, slave_addr, ioa_list): 召唤指定IOA的遥测值 ioa_list: 信息对象地址列表 # 构造可变帧ASDU类型0x09召唤限定词0x05表示请求指定IOA data bytearray() data.append(0x09) # ASDU类型测量值 data.append(0x01) # 可变结构限定词信息对象数量 data.append(0x05) # 传送原因请求 data.append(slave_addr) # 公共地址 for ioa in ioa_list: data.extend(ioa.to_bytes(3, little)) length len(data) 2 # 加上控制域和地址域 frame bytearray() frame.append(0x10) frame.append(length) frame.append(0x01) # 控制域主站发送帧计数位 frame.append(slave_addr) frame.extend(data) checksum sum(frame[2:]) % 256 frame.append(checksum) frame.append(0x16) sock.send(bytes(frame)) print(f召唤遥测: {bytes(frame).hex()}) resp sock.recv(4096) parsed parse_variable_frame(resp) for obj in parsed[objects]: print(fIOA{obj[ioa]}, 值{obj[value]}, 品质{obj[quality]})data里的可变结构限定词0x01表示后续有1个信息对象传送原因0x05表示这是响应召唤请求。公共地址一般和链路地址一致但有些装置会单独配置调试时要确认。遥信召唤类似ASDU类型换成0x01单点信息或0x03双点信息信息元素只有品质位没有数值。3.2 遥控与遥调的预置与执行遥控和遥调属于控制类操作103规约采用“预置-执行”两阶段流程。主站先发预置帧从站确认预置成功主站再发执行帧从站执行后回复确认。这个机制是为了防止误操作预置阶段可以校验控制参数是否合法。下面代码演示遥控的预置和执行def remote_control(sock, slave_addr, ioa, command): 遥控预置并执行 command: 0x01表示合闸0x02表示分闸 # 预置帧 data bytearray() data.append(0x2E) # ASDU类型带时标的单点命令 data.append(0x01) # 可变结构限定词 data.append(0x05) # 传送原因预置 data.append(slave_addr) data.extend(ioa.to_bytes(3, little)) data.append(command) # 命令值 data.append(0x00) # 品质位 data.extend(b\x00 * 7) # 时标占位 length len(data) 2 frame bytearray([0x10, length, 0x01, slave_addr]) frame.extend(data) frame.append(sum(frame[2:]) % 256) frame.append(0x16) sock.send(bytes(frame)) resp sock.recv(1024) if resp[1] 0x00: print(预置成功发送执行帧) # 执行帧传送原因改为0x06 data[2] 0x06 frame bytearray([0x10, length, 0x01, slave_addr]) frame.extend(data) frame.append(sum(frame[2:]) % 256) frame.append(0x16) sock.send(bytes(frame)) resp sock.recv(1024) if resp[1] 0x00: print(遥控执行成功) else: print(f执行失败响应: {resp.hex()}) else: print(f预置失败响应: {resp.hex()})预置帧的传送原因是0x05执行帧改为0x06其他字段不变。实际工程中遥控失败最常见的原因是预置超时——从站预置成功后等待执行的时间窗口通常只有几秒超时后预置状态自动取消执行帧发过去会被拒绝。我一般会在预置确认后立即发执行帧中间不加额外延时。4. 网络103调试避坑从抓包到参数核对的五条血泪经验4.1 链路能通但数据不刷新现象TCP连接正常复位帧有确认但遥测遥信数据始终不更新抓包看到主站反复发召唤帧从站回复的报文长度正常但数据区全零。原因从站的公共地址配置和主站请求帧里的公共地址不一致。103规约里链路地址和公共地址是两个独立参数链路复位只用链路地址但数据召唤时从站会校验公共地址不匹配就返回空数据。解决抓包对比主站请求帧和从站响应帧的公共地址字段确认两者一致。如果不一致修改主站配置或从站参数重启链路后重新复位。4.2 校验和错误导致丢帧现象抓包看到主站发送的帧格式正确但从站不响应或者从站响应后主站不确认通信间歇性中断。原因校验和计算范围错误。103规约的校验和只累加控制域、地址域和数据区不包含启动字符、长度域和结束字符。有些开发者习惯把长度域也算进去导致校验和偏大。解决用代码里的build_fixed_frame和parse_variable_frame逐帧核对校验和。如果手算麻烦直接在抓包工具里看校验和字段和代码计算结果比对。4.3 遥控预置成功但执行被拒现象预置帧收到确认执行帧发下去从站回复否定确认遥控操作失败。原因预置和执行之间的时间间隔超过了从站设定的超时窗口。不同厂家的超时窗口不一样常见的是5秒到30秒但有些装置默认只有3秒。解决预置确认后立即发执行帧不要在中间插入日志打印、数据库写入等耗时操作。如果业务逻辑必须延时把延时控制在1秒以内。4.4 时标解析偏差现象遥测数据值正确但时标和实际时间差了几小时或者时标字段解析出乱码。原因103规约的时标是7字节前4字节是毫秒级时间戳后3字节是分钟、小时、日等。有些装置用本地时间有些用UTC没有统一规定。另外时标字节序也有大小端差异。解决先确认装置用的是本地时间还是UTC再确认字节序。拿一帧已知时间的报文做基准手动解析时标字段和实际时间比对确认解析逻辑后再批量处理。4.5 多从站并发时的地址冲突现象单个从站通信正常接入多个从站后部分从站数据错乱或者主站收到不属于当前从站的响应。原因多个从站的链路地址或公共地址重复。103规约的地址域只有1字节最多支持255个从站但实际工程中地址规划不规范容易出现重复。解决在主站配置里为每个从站分配唯一地址接入前用抓包工具逐个确认从站地址。如果地址冲突无法避免考虑用不同的TCP端口区分从站。5. 从抓包到点表核对一套可复用的103规约验证流程调试103规约最怕的不是协议复杂而是没有一套固定的验证流程每次遇到问题都从头猜。我现在的习惯是拿到一个新装置先不急着写业务代码而是用下面这套流程走一遍确认链路、报文、点表三层都对得上再进入功能开发。第一步用tcpdump或Wireshark抓包过滤端口2404103规约常用端口但实际项目可能不同以装置手册为准。抓包时先只发复位帧确认从站有确认响应且响应帧的地址域和主站请求一致。这一步能排除IP、端口、地址配置错误。第二步发单点召唤帧只召唤一个遥测IOA解析响应报文。重点核对三个字段ASDU类型是否匹配、IOA是否和点表一致、数值和品质位是否合理。如果IOA对不上检查字节序如果数值明显异常检查信息元素长度是否解析正确。第三步批量召唤所有遥测和遥信IOA和点表逐条比对。这一步最容易发现点表配置错误比如IOA偏移、类型不匹配。我一般会写一个脚本把解析结果和点表CSV做diff输出不一致的条目。第四步做一次完整的遥控预置-执行流程确认控制类操作正常。遥控测试要谨慎确保装置处于调试状态避免误动真实设备。第五步记录整个流程的抓包文件和解析日志作为后续排查的基准。下次遇到通信异常先拿当前抓包和基准对比快速定位是链路问题还是数据问题。这套流程走下来大部分103规约的对接问题都能覆盖。从那以后我每次接入新装置都强制走一遍这五步再也没出现过“链路通了但数据不对”的尴尬。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站