新闻详情

新闻详情

首页 / 资讯中心 / 详情

Java实现101/104规约解析:从报文结构到ASDU解码

发布时间:2026/10/2 8:47:11来源:尧图网络
Java实现101/104规约解析:从报文结构到ASDU解码
简介面向电网规约开发与毕业设计场景这套基于Java语言的101DLT634.5101-2002与104DLT634.5104-2009规约解析和组装工具为需要处理电力远动通信的在校学生、高校教师及程序员提供了可直接运行的工程代码。压缩包共64个文件大小约147KB其中54个Java源文件构成核心解析和组装逻辑4个XML文件用于工程配置2个Excel文件为规约解析细则另有MD说明文档与TXT注意事项便于对照学习与二次开发。项目结构清晰基本依据文档即可运行适合课程设计、毕业设计或入门电力规约研究。已有81人学习下载得到初步认可对爱钻研的开发者可深入理解101/104报文格式并扩展功能。建议将项目重命名为英文路径后再运行可减少环境问题。1. 104规约和101规约解析工具调度端的“黑匣子”终于能打开了做过电力远动调试的工程师都体会过这种尴尬站端装置和调度主站之间明明在跑数据可抓下来的报文就是一堆十六进制字节流不知道哪一条是遥测、哪一条是遥控。基于Java语言的101规约和104规约DLT634.5104-2009解析和组装工具就是把这个“黑匣子”打开。它能把串口或TCP链路上的报文完整解析成可读的ASDU结构也能反向组装出总召唤、时钟同步、遥控执行等命令。正常的一次I帧握手、一条YC遥测、一条YK遥控都能在这类工具里变成清晰的字段。适合做远动规约调试、厂站接入测试、虚拟从站模拟的Java开发者和自动化工程师。2. 报文结构拆解从APCI到ASDU解析器依赖的只有这些字节104规约和101规约虽然名字像但传输载体和链路层完全不同。104规约跑在TCP/IP上链路层的“帧边界”由APCI承担101规约跑在串口上链路层有独立的帧格式、地址域和校验域。要想让一套Java代码同时兼容两者必须先把这两层的报文结构拆开抽象出共同的ASDU解码部分。2.1 104规约的APCI和ASDU0x68开头跟长度走DLT634.5104-2009对应的就是IEC 60870-5-104它把传输帧分成I帧信息传输帧、S帧监视帧、U帧控制帧。三种帧都以0x68作为启动字符第二个字节是APDU长度表示后面还有多少字节。这个设计对解析器非常友好读两字节就能确定这一帧的边界剩下的就是按长度截取。帧类型启动字符APDU长度控制域承载内容I帧0x684 ASDU长度2字节各类ASDUS帧0x6842字节确认帧不带ASDUU帧0x6842字节STARTDT/STOPDT/TESTFRI帧的控制域里低字节的低2位是0发送序号N(S)占用前6位高字节的高位是0接收序号N(R)占用后7位。S帧第一个控制字节是0x01U帧则用0x07系列表示启停和测试。解析时如果只关心ASDU就把第3到第6字节的控制域跳过直接读后面的内容。这个“跟长度走”的思路让大部分104报文解析都变成了缓冲区操作不需要做流式状态机。2.2 101规约的链路层报文固定帧长与可变帧长101规约的链路层分两种固定帧长用于链路控制可变帧长用于传输ASDU。固定帧长帧格式为0x10 控制字C 地址域A 校验CS 0x16总共5字节可变帧长帧格式为0x68 长度L 长度L重复 控制字C 地址域A 链路用户数据ASDU 校验CS 0x16。长度L从控制字开始算到校验数据之前结束包含了控制字、地址域和ASDU的长度。校验CS是控制字、地址域、ASDU各字节的算术和取低八位。这是最容易被忽略的地方很多习惯了104规约的工程师直接拿TCP无校验的思路去处理101结果在串口上一直丢帧。101规约的地址域通常只有1字节用来标识变电站内的链路地址。有些地区用2字节扩展解析时需要通过控制字中的A bit判断地址长度。工具里必须同时支持这两种情况否则换一个厂家设备就要改一遍代码。2.3 解析器的通用接口设计先定接口再写实现无论101还是104ASDU的结构是相似的都包含类型标识、可变结构限定词、传送原因、公共地址、信息体。所以第一步先定一个统一解析接口让链路层派生产品去实现。public interface Dlt634Parser { // 输入一段字节流返回解析后的帧对象列表 ListParsedFrame parse(byte[] data); } public class ParsedFrame { private byte[] rawBytes; // 原始帧字节 private int frameType; // 1I帧/S帧/U帧, 2101固定帧, 3101可变帧 private byte[] asduBytes; // ASDU部分可能为空 private int controlField; // 控制字 private int linkAddress; // 101链路地址104无 }接口设计成“输入字节流输出帧列表”而不是“输入一帧输出一帧”是因为串口和TCP都会出现半包、粘包。实现类内部要维护一个缓冲区解析完一帧就把剩余字节继续交给下一次parse。这个接口的好处是上层业务不用关心是从TCP还是串口来的数据只要把收到的字节喂进去拿到的就是已经切割好的帧结构。ASDU解码逻辑可以抽出一个独立的ASDU解码器供101和104两种链路层复用。public class AsduDecoder { public static Asdu parse(byte[] data, int offset) { Asdu asdu new Asdu(); asdu.typeId data[offset] 0xFF; asdu.vsq data[offset 1] 0xFF; asdu.cot data[offset 2] 0xFF; asdu.commonAddress (data[offset 3] 0xFF) | ((data[offset 4] 0xFF) 8); // 根据类型标识继续解析信息体 int objNum asdu.vsq 0x7F; boolean continuous (asdu.vsq 0x80) ! 0; asdu.objects new ArrayList(); int pos offset 5; for (int i 0; i objNum; i) { InformationObject obj new InformationObject(); if (continuous) { obj.infoAddress readInfoAddress(data, pos); pos 3; } else { obj.infoAddress readInfoAddress(data, pos); pos 3; } pos parseValue(data, pos, asdu.typeId, obj); asdu.objects.add(obj); } return asdu; } }这里的关键参数是vsq的高位SQ0表示单个寻址信息体地址要逐个读1表示连续寻址只读第一个信息体地址后面的地址按序号递增。很多解析错误都出在这个bit上尤其是工程上经常遇到厂家把连续寻址的一组遥测拆成多个ASDU数量对不上时最先检查的就是SQ位。3. Java实现104规约解析主循环、ASDU解码与组包发送有了链路层和接口设计接下来就是真正能跑的Java代码。这一章的代码是一个最小可用的104规约解析器包含字节流累积、APDU帧识别、ASDU基本解码和几个常用组包方法。3.1 接收字节流累积与帧边界识别半包粘包处理TCP收到的数据不是按报文边界来的可能一个包里包含两帧半也可能半帧要分三次才到。解析器内部必须维护一个缓冲区用启动字符和长度字段来判断完整帧。public class Iec104Decoder { private final ByteArrayOutputStream buf new ByteArrayOutputStream(); public ListParsedFrame parse(byte[] data) { buf.write(data, 0, data.length); byte[] all buf.toByteArray(); ListParsedFrame frames new ArrayList(); int offset 0; while (offset 2 all.length) { if ((all[offset] 0xFF) ! 0x68) { offset; // 丢弃未对齐的字节 continue; } int len all[offset 1] 0xFF; // 最小APDU长度是4S帧/U帧最大253 if (len 4 || len 253 || offset len 2 all.length) { break; // 长度非法或数据不足等待下一次喂数据 } ParsedFrame frame new ParsedFrame(); frame.rawBytes Arrays.copyOfRange(all, offset, offset len 2); // 控制域偏移2ASDU偏移6 frame.controlField (all[offset 2] 0xFF); if (len 4) { frame.asduBytes Arrays.copyOfRange(all, offset 6, offset len 2); } frames.add(frame); offset len 2; } // 保留未处理完的尾部 buf.reset(); if (offset all.length) { buf.write(all, offset, all.length - offset); } return frames; } }逻辑说明读到0x68后如果第二个字节长度值在4到253之间且缓冲区里已经凑够了整帧就切出来。注意offset len 2里的2是启动字符和长度字节本身。长度字段表示的是控制域加ASDU的总字节数帧总长等于长度2。处理完后把剩余尾部字节写回缓冲区等着下一段数据合并。这个循环可以天然处理粘包和半包。3.2 类型标识与ASDU解码把遥测、遥信、遥控分开ASDU的类型标识决定了信息体的编码方式。常见的类型标识有1单点遥信、3双点遥信、9归一化遥测、11短浮点遥测、45单点遥控、100总召唤命令。解析时先看类型标识再决定每个信息体取多少字节。private static int parseValue(byte[] data, int pos, int typeId, InformationObject obj) { switch (typeId) { case 1: // 单点遥信占用1字节 obj.state (data[pos] 0x01) 1; obj.quality (data[pos] 4) 0x0F; return pos 1; case 9: // 归一化遥测占用2字节带符号整数 obj.value (short) ((data[pos] 0xFF) | ((data[pos 1] 8) 0xFF00)); obj.quality (data[pos 2] 4) 0x0F; return pos 3; case 11: // 短浮点遥测占用3字节 品质描述 int raw (data[pos] 0xFF) | ((data[pos 1] 0xFF) 8) | ((data[pos 2] 0xFF) 16) | ((data[pos 3] 0xFF) 24); obj.value Float.intBitsToFloat(raw); obj.quality (data[pos 4] 4) 0x0F; return pos 5; case 45: // 单点遥控占用1字节品质 1字节命令状态 obj.commandState data[pos]; return pos 1; default: // 遇到不认识的类型先跳过该信息体 throw new UnsupportedOperationException(未实现类型 typeId); } }参数说明归一化遥测的值是带符号16位整数需要按大端组合并且用short接收避免变成无符号数。短浮点遥测在DLT634.5104-2009里严格使用IEEE754单精度大端字节序但实际遇到部分国产装置用小端。我的做法是先按大端解析如果数值一眼不合量纲就加一个字节序切换开关。解析品质描述时高4位是品质低4位是保留位别混在一起读取。3.3 组装104规约报文总召唤、时钟同步、遥控命令解析是读组装是写。调试时最常用的三个组包方法总召唤让从站把全量数据再传一遍时钟同步让站端和主站对时遥控命令用来测试断路器分合。以总召唤为例public static byte[] buildInterrogation(int commonAddress, int sendSeq) { // ASDU100总召唤1个对象6激活公共地址信息体地址0 byte[] asdu new byte[] { (byte) 0x64, // 类型标识100 0x01, // VSQ1个对象 0x06, // COT激活 (byte) (commonAddress 0xFF), (byte) ((commonAddress 8) 0xFF), 0x00, 0x00, 0x00 // 信息体地址0 }; int apduLen 4 asdu.length; ByteBuffer bb ByteBuffer.allocate(2 apduLen); bb.put((byte) 0x68); bb.put((byte) apduLen); // 控制域第一字节低2位为0发送序号左移1位 bb.put((byte) ((sendSeq 0x7F) 1)); // 第二字节接收序号高位清零 bb.put((byte) 0x00); bb.put(asdu); return bb.array(); }这里sendSeq是本次I帧的发送序号范围0到127。控制域第一个字节低2位是0所以序号左移1位。接收序号在上行方向解析时维护下行组包时通常填0即可标准实现中应该回填已经收到的I帧序号。遥控命令的组装类似类型标识45固定用3个信息体字节表示遥控对象的地址再带一个命令字节选择0x81和执行0x01各占一条报文。千万不要把选择和执行塞线程里并发发很多厂站从站只认顺序。4. 101规约串口场景帧校验、地址域和重发机制104规约适合厂站网络环境101规约在老旧串口链路和专用通道上仍然大量运行。Java里用javax.comm或fazecast-jSerialComm都可以。101规约的代码重点在帧校验和可变帧长的字节拼接。4.1 可变帧长的CS校验与地址域可变帧长帧的组装必须把控制字、地址域、ASDU一起计算CS校验。CS不是CRC16而是简单累加。public static byte[] build101VariableFrame(byte control, byte linkAddress, byte[] asdu) { int len 1 1 asdu.length; // 控制字 地址域 ASDU ByteArrayOutputStream out new ByteArrayOutputStream(); out.write(0x68); out.write(len); out.write(len); out.write(control 0xFF); out.write(linkAddress 0xFF); out.write(asdu); // 计算校验和从控制字到ASDU结束 int cs 0; cs (control 0xFF); cs (linkAddress 0xFF); for (byte b : asdu) { cs (b 0xFF); } out.write(cs 0xFF); out.write(0x16); return out.toByteArray(); }参数说明长度len包括控制字、地址域和ASDU但不包括启动符0x68、长度重复字节、CS和结束字符0x16。控制字和地址域都按无符号数加到CS里最后取低8位。有些串口调试助手会显示CS不对原因就是没有按无符号数相加(byte)直接相加会把负数带进累加值。用b 0xFF是最稳妥的写法。4.2 固定帧长帧的解析与链路层确认101固定帧长帧长度是5字节用于请求链路状态、复位等。解析时先校验帧头0x10再取CS最后比0x16。public static boolean parseFixedFrame(byte[] frame) { if (frame.length ! 5) return false; if ((frame[0] 0xFF) ! 0x10) return false; if ((frame[4] 0xFF) ! 0x16) return false; int sum (frame[1] 0xFF) (frame[2] 0xFF); return (sum 0xFF) (frame[3] 0xFF); }固定帧长的控制字只有1字节地址域也只有1字节CS就是这两个字节之和。链路层确认帧往往用固定帧长实现比如主站发了复位命令从站回一个固定帧长确认。解析时不要忽略这个确认否则重发机制会误判。4.3 串口参数与超时策略波特率、停止位和Td101规约串口参数没有统一强制值但国内厂站普遍用波特率9600、数据位8、停止位1、偶校验。下表是常见参数和适用场景参数项常见取值说明波特率9600 / 2400近距离用9600远传通道用2400数据位8大部分站端配置为8停止位1 或 2老设备可能用2新设备用1校验位偶校验少数地区用无校验Td超时500ms3s从站响应超时视通道质量调整Java串口接收需要另开线程读到的字节直接丢给解析器。注意串口流没有消息边界所以解析器必须同时处理半包和粘包。我之前在项目里把解析器的缓冲区放在串口监听线程外部结果读一次开一次缓冲永远等不到完整帧。正确做法是监听线程循环read()把读到的长度不定的字节块交给同一个parse()方法让它内部累积。5. 规约解析和组装中的常见坑与排查5条踩坑记录这部分是真实调试中反复遇到的共性问题。每一条都按“现象 → 原因 → 解决”来写可以直接对照排查。5.1 现象U帧启动握手后从站不进入数据传输态104规约建立TCP连接后主站必须发送U帧STARTDT act控制字0x07 00 00 00从站收到后回STARTDT con0x0B 00 00 00之后才能发I帧。有些从站要求主站先发TESTFR也有从站只接受STARTDT不响应TESTFR。如果主站程序发送STARTDT后立即发总召唤从站还没完成状态迁移报文会被丢弃。解决维护一个连接状态字段只有收到STARTDT con后才允许发送I帧并且用定时器重发STARTDT直到有应答不要一启动就疯狂发数据。5.2 现象总召唤返回的遥测数量对不上原因ASDU里VSQ的SQ位是1表示连续寻址信息体地址只给第一个后面的地址依次加1。很多解析器每读一个信息体都从信息体基址重新解析导致第二个对象地址又和第一个一样最终统计的遥测总数少于实际。解决连续寻址时要在解析循环里维护一个currentAddress每读完一个对象就加1如果遇到信息体地址跳变说明从站用了非连续寻址需要按VSQ的SQ位分别处理。5.3 现象101串口读到乱码校验和看着没问题串口上收到一堆字节按101格式解析半天都对齐不上。原因是101规约的CS校验是算术和不是CRC16而有的设备老固件算的是“帧头到结束符之间的异或和”。这两种算法在数据字节多时结果经常恰好相等导致有时能解析有时乱码。解决先用设备说明书确认校验算法。如果实在没有说明书把捕获的完整帧逐字节分析拿CS反推。我遇到过固件把地址域排除在校验之外的情况改掉之后立刻正常。5.4 现象104控制域序号不连续报文被丢弃主站每发一个I帧发送序号N(S)加1每收一个I帧接收序号N(R)加1。很多初级实现只把固定值写死比如一直发0x00从站收到序号不递增的报文就直接丢弃。解决为每个TCP连接维护独立的sentSeq和recvSeq变量每发一个I帧后就(sentSeq 1) 0x7F并把这两个值分别填到控制域的两个字节里。调试时可以用Wireshark的104过滤器查看序号确认是不是这里出了问题。5.5 现象遥控执行时报“超时”或“命令未确认”遥控过程分为两步先发“选择”命令COT6命令状态0x81从站确认后再发“执行”命令COT6命令状态0x01最后从站返回“激活结束”COT10。如果选择没被确认就发执行或者选择和执行用了同一个I帧序号从站会认为是旧报文直接不回。解决严格按选择-确认-执行-确认的时序每个阶段单独设置超时超时后重发选择而不是继续发执行。很多从站还会检查遥控信息体地址对应的点号是否存在地址填错也会出现这种症状。6. 用模拟报文回放验证工具从抓包到可回归的调试方法解析器写好了如何确认代码没有隐性bug我的习惯是抓一段真实报文保存成十六进制文件然后用Java写一个回放工具把文件里的字节逐条喂给解析器输出成JSON或文本再和抓包工具解析的字段对比。这样每次改动解析器后都能拿同一份报文回归测试。public static void replay(Path hexFile, Iec104Decoder decoder) throws IOException { byte[] all Files.readAllBytes(hexFile); ListParsedFrame frames decoder.parse(all); for (ParsedFrame f : frames) { System.out.printf(frame control0x%02X asduLen%d%n, f.controlField, f.asduBytes.length); if (f.asduBytes.length 0) { Asdu asdu AsduDecoder.parse(f.asduBytes, 0); System.out.println( type asdu.typeId objects asdu.objects.size()); } } }回放文件可以直接从Wireshark导出以十六进制文本形式保存读取时用DatatypeConverter.parseHexBinary或HexFormat.of().parseHex转换。再配合一个JUnit测试把预期帧数、类型标识、遥测值断言进去就能防止后续改代码时引入回归。反向组装工具一样可以测试把组装出来的报文用字节数组解析一遍断言解析结果和入参一致。这个验证方法成本极低但能救大命。我吃过亏改了一个字节序判断后总召唤报文全乱了人眼对比又看不出问题后来就是用回放脚本立刻定位到短浮点解析分支。做完这个回归脚本后换新厂家设备时我也会先抓一段现场报文丢进回放工具里看能否对齐能对齐才继续做后面的联调。希望这些积累能帮你少走几步弯路也祝你的规约工具一次跑通。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

AI日报制作全流程:从信息筛选到知识库的工程实践 2026/10/2 11:04:47

AI日报制作全流程:从信息筛选到知识库的工程实践

1. 一份“AI 日报”到底在记录什么每天早上打开电脑,我的第一件事不是看邮件,而是花二十分钟把过去二十四小时里 AI 圈发生的事过一遍。这个习惯坚持了快三年,从最开始只是自己记备忘录,到后来整理成固定的格式发给团队&#xff0…

阅读更多 →
Python实现多AGV路径规划与调度优化实战解析 2026/10/2 11:04:47

Python实现多AGV路径规划与调度优化实战解析

简介:面向智能物流装备、仓储管理与智能制造领域的科研人员和开发工程师,该资源提供了一份基于Python的多AGV路径规划与调度优化方案,完整复现了“订单拣选系统中多AGV路径规划与调度研究”论文的关键方法。内容以栅格地图环境建模为起点&…

阅读更多 →
Kali Linux root登录配置全攻略:从锁定到解锁的完整说明 2026/10/2 11:04:47

Kali Linux root登录配置全攻略:从锁定到解锁的完整说明

不少朋友刚把Kali装起来,第一件事就是想在登录界面敲个root,结果输完密码直接被打回来,或者在终端里执行个su -,密码怎么输都不对,心里就开始犯嘀咕:是不是我哪里配置错了?其实你大概率没配错&a…

阅读更多 →
AI Agent实战:用命令和日志诊断外接屏HDMI无信号 2026/10/2 11:04:46

AI Agent实战:用命令和日志诊断外接屏HDMI无信号

周一早上 9 点还差几分钟,我照例把笔记本推进底座准备开始干活。外接屏电源灯亮了,黑屏上却只有“HDMI 无信号”几个字。上周它已经闹过一次情绪,当时我花了整个上午逛搜索引擎,“换线、更新驱动、重置显示器、重装系统”挨个试了…

阅读更多 →
多AGV路径规划与调度论文复现:订单分批、模拟退火与A*的Python实现 2026/10/2 11:04:46

多AGV路径规划与调度论文复现:订单分批、模拟退火与A*的Python实现

简介:面向物流自动化与智能仓储领域研究者和Python开发工程师的完整复现资源包,围绕订单拣选系统中多自动导引运输车路径规划与调度问题,提供可运行的Python实现与详细讲解。方案覆盖环境建模与数据预处理、基于节省算法的动态订单分批、模拟…

阅读更多 →
ComfyUI+PS高效AI绘画工作流:从节点搭建到商业落地 2026/10/2 11:04:40

ComfyUI+PS高效AI绘画工作流:从节点搭建到商业落地

1. 为什么是 ComfyUI PS:这套组合解决了我什么痛点1.1 从 WebUI 转向 ComfyUI 的心路历程先说结论:如果你只是想随便玩两下出几张图,WebUI(也就是 Stable Diffusion WebUI)完全够用。但如果你想认认真真把 AI 绘画做成…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉