ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java实现IEC101/104报文解析与组装:从十六进制到结构化对象

Java实现IEC101/104报文解析与组装:从十六进制到结构化对象 简介本资源面向电力自动化、配网通信及工控协议开发人员围绕DL/T634.5101-2002101规约与DL/T634.5104-2009104规约提供一套基于Java的解析与组装实现可用于实际场景中发送报文的生成、规约内容解析与交互调试适合具备一定Java基础、需要对接电网规约的中高级开发者。压缩包共43个文件以34个Java源码为核心辅以3个XML配置、2份规约实施细则文档、1份规约解析细则表格及README说明等整体约1.79MB目录按解析与交互模块划分结构清晰。已有661人学习下载。读者可从中获得101/104规约报文的完整解析与组装思路、可复用的Java工程代码、配套规约细则参考文档以及报文生成与排错的具体实现路径便于快速理解规约结构并落地到实际项目中。1. 从一份 Java 规约解析包说起104 报文到底怎么拆如果你在配网自动化、变电站后台或者调度主站侧做过对接大概率遇到过这种场景现场装置上送一串十六进制看着像68 0E 00 00 00 00 68 01 03 00 01 00 00 00 00但到底是总召唤还是遥测上送、信息体地址落在哪个区间、为什么主站回了个 S 帧而不是 I 帧全靠翻 DL/T634.5104-2009 的册子加经验猜。这份 iec-master 就是冲着这个痛点来的——一个基于 Java 的 IEC101DL/T634.5101-2002和 IEC104DL/T634.5104-2009报文解析与组装工程能拆也能拼适合做协议联调、报文回放、测试用例生成的人直接拿来用。它不是一个跑起来就完事的成品软件而是一套可读、可改、可嵌进你自己工具链的源码配套还带了广东电网配网自动化的 101/104 实施细则和一份规约解析细则这点对做南网项目的同行比较友好。下面我按「它是什么 → 怎么跑起来 → 怎么解析和组装 → 坑在哪 → 怎么进阶」的顺序拆一遍。2. 工程结构与运行环境先把 iec-master 跑起来2.1 目录里都有什么别急着改代码拿到 iec-master.zip 解压后根目录能看到iec_analysis、iec_interaction两个核心模块加上src、pom.xml、README.md、LICENSE、.gitignore以及docs目录下的三份附件。这是一个典型的 Maven 多模块结构pom.xml在根和子模块各有一份说明父 POM 管依赖版本、子模块管各自职责。从命名推断iec_analysis负责规约的解析与组装逻辑iec_interaction更偏交互/会话层比如链路建立、超时重传这类状态机行为。docs里的三份文档是理解代码行为的钥匙附件 1 和附件 2 是广东电网配网自动化的 101/104 实施细则附件 3 是规约解析细则里面通常写清了信息体地址分配、传送原因取值、时标格式这些现场约定。先读细则再读代码否则你会对某些「为什么这里要这么判」的写法一头雾水。2.2 环境准备与编译Java 项目先确认 JDK 和 Maven 版本。规约解析大量涉及字节操作JDK 8 及以上都能跑但建议用 JDK 8 或 11避免高版本模块化带来的反射限制影响老依赖。# 确认环境 java -version mvn -version # 进入工程根目录先做一次依赖解析和编译 cd iec-master mvn clean compile # 如果只想编译某个子模块 mvn -pl iec_analysis -am clean compilemvn clean compile会拉取父 POM 里声明的依赖常见的是 Netty做 TCP 链路、日志组件和测试框架。-pl iec_analysis -am的意思是只构建iec_analysis模块同时把它依赖的模块一起构建调试单模块时很省时间。如果编译报找不到某个内部模块多半是父 POM 的modules没包含它或者你漏了-am。2.3 打包与产物# 打包跳过测试先跑通 mvn clean package -DskipTests # 产物在各自模块的 target 目录下 ls iec_analysis/target/ ls iec_interaction/target/-DskipTests在首次跑通时很有用规约工程的单元测试有时依赖本地端口或模拟装置环境没配好会卡住。产物一般是 jar如果iec_interaction配了maven-assembly-plugin或spring-boot-maven-plugin可能出可执行 jar。判断方法看pom.xml里的build段有mainClass配置的就是可执行入口。这一步别急着改代码先确认能编译能打包把环境问题和使用问题分开是排查的第一原则。3. 报文解析实战从十六进制到结构化对象3.1 104 报文的分层结构先理清IEC104 的报文分三层理解最外层是 APCI应用规约控制信息也就是68起始符 长度 四个控制域字节往里是 ASDU应用服务数据单元包含类型标识、可变结构限定词、传送原因、公共地址、信息体地址和信息体。101 和 104 的 ASDU 结构基本一致差别主要在链路层——101 走串口有固定的帧格式和校验104 走 TCP用 APCI 做链路控制。解析代码里通常先判起始符和长度再根据控制域第 1 字节的低两位判断是 I 帧、S 帧还是 U 帧。I 帧带 ASDUS 帧只做确认U 帧管链路启停和测试。搞不清这三种帧后面所有解析都是空中楼阁。3.2 用代码把一串报文拆开下面这段是常见的调用形态具体类名以你工程里的为准逻辑是通用的把十六进制字符串转字节数组交给解析器拿到结构化结果。// 十六进制字符串转字节数组规约解析的第一步 public static byte[] hexToBytes(String hex) { hex hex.replaceAll(\\s, ); // 去掉空格现场抓包常带空格 int len hex.length(); byte[] data new byte[len / 2]; for (int i 0; i len; i 2) { // 每两个字符拼一个字节16 进制解析 data[i / 2] (byte) ((Character.digit(hex.charAt(i), 16) 4) Character.digit(hex.charAt(i 1), 16)); } return data; } // 解析入口实际类名参考 iec_analysis 模块 String raw 68 0E 00 00 00 00 68 01 03 00 01 00 00 00 00; byte[] frame hexToBytes(raw); // Iec104Parser parser new Iec104Parser(); // Asdu asdu parser.parse(frame); // System.out.println(asdu.getTypeId()); // 类型标识 // System.out.println(asdu.getCause()); // 传送原因 // System.out.println(asdu.getCommonAddr()); // 公共地址hexToBytes里replaceAll(\\s, )是血泪经验现场从串口工具或抓包软件复制出来的报文经常带空格甚至换行不先清洗长度算出来就是错的解析直接抛越界。Character.digit把单个十六进制字符转成 0-15 的数值左移 4 位拼高位加上低位就是一个字节。解析器返回的 ASDU 对象里typeId告诉你这是遥测如 09/0B/0D、遥信01/03、总召唤64H100还是时钟同步67H103cause是传送原因比如 20 是响应总召唤、03 是突发上送commonAddr是公共地址配网里常用来区分不同终端。3.3 关键字段的含义与取值字段位置典型取值说明起始符第 1 字节0x68104 固定101 变长帧不同APDU 长度第 2 字节0x0E后续字节数不含起始符和长度本身控制域 1第 3 字节低两位判帧型00I 帧01S 帧11U 帧类型标识ASDU 第 1 字节0x64100总召唤0x01单点遥信传送原因ASDU 第 3 字节0x1420响应总召唤公共地址ASDU 第 5-6 字节现场分配区分终端信息体地址信息体前 3 字节现场分配低字节在前这张表建议对着附件 3 的解析细则一起看细则里会写明本项目现场用的地址段划分比如遥信从 1H 开始、遥测从 4001H 开始不同局可能不一样别拿网上的通用表硬套。3.4 解析结果怎么验证解析完别只看打印要做交叉验证。常见做法是拿现场真实抓包用解析器拆一遍再和装置说明书里的点表比对信息体地址对不对、时标是不是 CP56Time2a 七字节、遥测值有没有乘系数。如果解析出来的遥测值和后台显示差一个量级多半是系数没处理规约层只负责原始码值系数是业务层的事。这一步是区分「能解析」和「解析对」的关键很多人卡在这。4. 报文组装把业务数据拼成合规帧4.1 组装比解析更容易翻车解析是把已知字节读出来组装是把业务意图写成字节任何一个字段算错对端直接拒收或误动作。组装的核心是长度计算和字节序。104 的 APDU 长度字段是「从控制域第 1 字节到 ASDU 结束」的字节数不含起始符和长度本身这个「不含」是新手最容易多算或少算一的地方。信息体地址是 3 字节、低字节在前时标是 7 字节 CP56Time2a年月日时分秒毫秒各有各的位域拼错一位时间就飞到 2050 年。4.2 组装一个总召唤帧// 组装总召唤报文I 帧发送序号和接收序号需按链路状态维护 ByteArrayOutputStream bos new ByteArrayOutputStream(); bos.write(0x68); // 起始符 // 长度占位最后回填 int lenPos bos.size(); bos.write(0x00); // 控制域I 帧发送序号 N(S) 左移一位接收序号 N(R) 左移一位 int ns 0; // 发送序号实际由链路状态机维护 int nr 0; // 接收序号 bos.write((ns 1) 0xFF); bos.write((ns 7) 0xFF); bos.write((nr 1) 0xFF); bos.write((nr 7) 0xFF); // ASDU bos.write(0x64); // 类型标识总召唤 bos.write(0x01); // 可变结构限定词SQ0信息体个数1 bos.write(0x06); // 传送原因激活 bos.write(0x00); bos.write(0x01); // 公共地址低字节 bos.write(0x00); // 公共地址高字节 bos.write(0x00); // 信息体地址低 bos.write(0x00); // 信息体地址中 bos.write(0x00); // 信息体地址高 byte[] frame bos.toByteArray(); frame[lenPos] (byte) (frame.length - 2); // 回填长度总长减起始符和长度字节(ns 1) 0xFF和(ns 7) 0xFF是 104 序号的两字节小端表示序号只占 15 位最低位固定为 0 标识 I 帧。frame.length - 2就是 APDU 长度减掉的正是起始符和长度字节本身。传送原因0x06是激活响应总召唤的装置会回0x07激活确认加数据。公共地址和信息体地址按你现场的点表填总召唤的信息体地址一般填 0。这段代码的价值在于把「长度回填」和「序号移位」两个易错点显式写出来了抄的时候重点看这两处。4.3 序号维护与链路状态组装单帧不难难的是连续发送时 N(S) 和 N(R) 的维护。104 规定每发一个 I 帧N(S) 加 1每收到一个 I 帧N(R) 更新为对方 N(S)1 并回 S 帧确认。如果 N(S) 和 N(R) 乱了链路会认为丢帧触发重传甚至复位。iec_interaction模块大概率封装了这套状态机用之前先读它的发送和接收回调确认序号是在哪一层自增的。自己手写的话建议把序号维护单独抽一个类别散在业务代码里否则联调时你会怀疑人生。4.4 101 与 104 组装的差异101 走串口帧格式是固定帧长或可变帧长带校验和与结束符0x16没有 APCI 那套序号机制但有链路地址和帧计数位 FCB。组装 101 帧时校验和是前面所有字节累加取低 8 位结束符固定0x16。如果你只做 104这部分可以先跳过如果现场是 101 串口终端务必对着附件 1 的实施细则确认帧格式广东电网的配网 101 在帧结构上有自己的约定别直接套国标通用格式。5. 避坑与排查联调现场最常见的五个问题5.1 解析报数组越界现象一调用解析就抛ArrayIndexOutOfBoundsException。原因报文长度字段和实际字节数对不上或者十六进制字符串里有空格、换行没清洗。解决解析前先校验frame.length是否等于长度字段加 2字符串先replaceAll(\\s, )再在解析器入口加长度断言不满足直接返回错误码而不是硬解析。5.2 总召唤收不到数据现象主站发了总召唤装置没回遥测。原因公共地址填错或者传送原因用了激活终止而不是激活。解决核对附件 2 里本现场的公共地址分配总召唤的传送原因必须是激活06装置回的是激活确认07加数据。用抓包工具看装置有没有回回了但解析不出就是解析侧问题压根没回就是组装侧或链路问题。5.3 遥测值明显偏大或偏小现象解析出的遥测和后台显示差 10 倍、100 倍。原因规约层给的是原始码值业务层要乘系数系数在点表里。解决解析器只负责还原码值系数处理放到业务映射层别在解析器里硬编码系数否则换个现场就得改代码。归一化值、标度化值、短浮点数的处理方式不同看类型标识区分。5.4 时标解析成乱码时间现象时标字段解析出来是 2050 年或 1970 年。原因CP56Time2a 的位域拼错毫秒占两个字节低 16 位分钟占 6 位小时占 5 位日和月、年各有位宽拼错一位整体就飞。解决单独写一个 CP56Time2a 的编解码工具类用已知时间戳做单元测试编出来再解回去必须一致这是后悔药级别的自测。5.5 连续发送后链路断开现象发了几十帧后链路复位。原因N(S) 和 N(R) 没按规则维护或者没及时回 S 帧确认。解决把序号维护收敛到一个链路状态对象里发送和接收都走它收到 I 帧后及时回 S 帧别攒着。抓包看断开前的序号基本能定位是哪一侧没更新。6. 进阶用法把解析器变成你的测试工具把 iec-master 跑通只是起点真正省时间的是把它改造成自己的报文工具。我一般会做两件事。第一件是加一个批量解析入口把现场抓的一整段报文按行喂进去输出类型标识、传送原因、信息体地址和值的表格联调时对着点表一眼就能看出哪帧不对。第二件是做一个报文生成器把点表读进来按类型批量生成总召唤响应帧用来压测主站或验证装置。这两件事都不难核心是复用iec_analysis里的编解码逻辑别重写。改造方向复用模块关键点批量解析iec_analysis按行清洗十六进制逐帧解析异常不中断报文生成iec_analysis点表驱动注意长度回填和序号维护链路模拟iec_interaction状态机复用注意超时和重传参数协议转换两者结合101 与 104 的 ASDU 可复用链路层要分开验证改造是否正确的办法很朴素拿一份已知正确的报文解析再组装字节级比对必须完全一致。不一致就逐字段对长度、序号、时标是三个高发区。从那以后我每次改完编解码逻辑都强制走一遍「解析-组装-比对」的闭环不跑通不提交。这套习惯帮我挡掉了不少联调现场才暴露的问题。希望这份拆解能帮到你把这份规约工程真正用起来。本文还有配套的精品资源点击获取
返回列表