| New file | 
|  |  |  | 
|---|
|  |  |  | package com.dy.common.softUpgrade.parse; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | import cn.hutool.core.util.HexUtil; | 
|---|
|  |  |  | import com.dy.common.util.ByteUtil; | 
|---|
|  |  |  | import com.dy.common.util.CRC16; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | import java.io.*; | 
|---|
|  |  |  | import java.util.ArrayList; | 
|---|
|  |  |  | import java.util.List; | 
|---|
|  |  |  | import java.util.stream.Stream; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * @Author: liurunyu | 
|---|
|  |  |  | * @Date: 2024/11/2 8:51 | 
|---|
|  |  |  | * @Description | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | public class HexFileParse { | 
|---|
|  |  |  |  | 
|---|
|  |  |  | private static final int hexFileMinLine = 4 ; | 
|---|
|  |  |  | private static final int lineHeadEndIndex = 9 ; | 
|---|
|  |  |  | private static final int lineValidCharCount = 43 ;//每行有效字符(冒号与十六进制)个数,例如“:1096C000DC96010010020020203E00000441000052”,但倒数第3行不受限制 | 
|---|
|  |  |  | private static final int bytesSplitUnit512 = 512 ; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public static void main(String[] args) throws Exception{ | 
|---|
|  |  |  | boolean fromBytes = false ; | 
|---|
|  |  |  | HexFileParse obj = new HexFileParse() ; | 
|---|
|  |  |  | if(!fromBytes){ | 
|---|
|  |  |  | String filePath = "D:\\hexFile\\YuanMu_20250116_01.hex"; | 
|---|
|  |  |  | //File file = new File(filePath) ; | 
|---|
|  |  |  | HexFileVo vo =  obj.doParse(filePath) ; | 
|---|
|  |  |  | System.out.println(vo.toString()); | 
|---|
|  |  |  | }else{ | 
|---|
|  |  |  | String filePath = "D:\\hexFile\\YuanMu_20250116_01.hex"; | 
|---|
|  |  |  | File file = new File(filePath) ; | 
|---|
|  |  |  | byte[] fileContent = new byte[(int)file.length()] ; | 
|---|
|  |  |  | FileInputStream fileInputStream = new FileInputStream(file) ; | 
|---|
|  |  |  | fileInputStream.read(fileContent) ; | 
|---|
|  |  |  | HexFileVo vo =  obj.doParse(fileContent) ; | 
|---|
|  |  |  | System.out.println(vo.toString()); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * 解析Hex文件 | 
|---|
|  |  |  | * @param path | 
|---|
|  |  |  | * @return | 
|---|
|  |  |  | * @throws Exception | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | public HexFileVo doParse(String path) throws Exception { | 
|---|
|  |  |  | File file = new File(path) ; | 
|---|
|  |  |  | return this.doParse(file) ; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * 解析Hex文件 | 
|---|
|  |  |  | * @param file | 
|---|
|  |  |  | * @return | 
|---|
|  |  |  | * @throws Exception | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | public HexFileVo doParse(File file) throws Exception{ | 
|---|
|  |  |  | if(file == null){ | 
|---|
|  |  |  | throw new Exception("Hex文件对象为null") ; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | if(!file.exists()){ | 
|---|
|  |  |  | throw new Exception("Hex文件不存在") ; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | Stream<String> linesStream = new BufferedReader(new FileReader(file)).lines() ; | 
|---|
|  |  |  | List<String> lineList =  linesStream.toList() ; | 
|---|
|  |  |  | HexFileVo vo = new HexFileVo() ; | 
|---|
|  |  |  | this.checkAndCalculate(vo, lineList); | 
|---|
|  |  |  | this.parse(vo, lineList); | 
|---|
|  |  |  | this.splitBytesByUnit512(vo); | 
|---|
|  |  |  | this.calculateCrc(vo); | 
|---|
|  |  |  | return vo ; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * 解析Hex文件 | 
|---|
|  |  |  | * @param fileContent | 
|---|
|  |  |  | * @return | 
|---|
|  |  |  | * @throws Exception | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | public HexFileVo doParse(byte[] fileContent) throws Exception{ | 
|---|
|  |  |  | if(fileContent == null || fileContent.length == 0){ | 
|---|
|  |  |  | throw new Exception("Hex文件对象为null") ; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ByteArrayInputStream byteInputStream = new ByteArrayInputStream(fileContent); | 
|---|
|  |  |  | Stream<String> linesStream = new BufferedReader(new InputStreamReader(byteInputStream)).lines() ; | 
|---|
|  |  |  | List<String> lineList =  linesStream.toList() ; | 
|---|
|  |  |  | HexFileVo vo = new HexFileVo() ; | 
|---|
|  |  |  | this.checkAndCalculate(vo, lineList); | 
|---|
|  |  |  | this.parse(vo, lineList); | 
|---|
|  |  |  | this.splitBytesByUnit512(vo); | 
|---|
|  |  |  | this.calculateCrc(vo); | 
|---|
|  |  |  | return vo ; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * 合法性检查,并计算数据包数 | 
|---|
|  |  |  | * @param vo | 
|---|
|  |  |  | * @param lineList | 
|---|
|  |  |  | * @throws Exception | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | private void checkAndCalculate(HexFileVo vo, List<String> lineList) throws Exception{ | 
|---|
|  |  |  | if(lineList == null){ | 
|---|
|  |  |  | throw new Exception("Hex文件内容为空") ; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | vo.totalLines = lineList.size() ; | 
|---|
|  |  |  | if(vo.totalLines <= 0){ | 
|---|
|  |  |  | throw new Exception("Hex文件内容为空") ; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | if(vo.totalLines < hexFileMinLine){ | 
|---|
|  |  |  | throw new Exception("Hex文件内容行数" + vo.totalLines + "不正确(最小行数" + hexFileMinLine +")") ; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | vo.calculateBytes = ((((vo.totalLines - 4) * 32) / 1024) | 
|---|
|  |  |  | + ((((vo.totalLines - 4) * 32) % 1024) > 0?1 : 0)) | 
|---|
|  |  |  | * 512 ; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * 解析Hex文件内容,组合成一个大数据包 | 
|---|
|  |  |  | * @param vo | 
|---|
|  |  |  | * @param lineList | 
|---|
|  |  |  | * @throws Exception | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | private void parse(HexFileVo vo, List<String> lineList) throws Exception{ | 
|---|
|  |  |  | int counter = 0 ; | 
|---|
|  |  |  | for(String line : lineList){ | 
|---|
|  |  |  | if(counter == 0){ | 
|---|
|  |  |  | // 文件头 | 
|---|
|  |  |  | }else if(counter == vo.totalLines - 2){ | 
|---|
|  |  |  | // 文件尾倒数第2行 | 
|---|
|  |  |  | }else if(counter == vo.totalLines - 1){ | 
|---|
|  |  |  | // 文件尾倒数第1行 | 
|---|
|  |  |  | }else{ | 
|---|
|  |  |  | // 文件内容 | 
|---|
|  |  |  | if(line == null){ | 
|---|
|  |  |  | vo.errors.add("第" + counter + "行内容为空") ; | 
|---|
|  |  |  | }else{ | 
|---|
|  |  |  | line = line.trim() ; | 
|---|
|  |  |  | if(line.length() == 0){ | 
|---|
|  |  |  | vo.errors.add("第" + counter + "行内容为空") ; | 
|---|
|  |  |  | }else{ | 
|---|
|  |  |  | if(!line.startsWith(":")){ | 
|---|
|  |  |  | vo.errors.add("第" + counter + "行内容不是以:开头") ; | 
|---|
|  |  |  | }else{ | 
|---|
|  |  |  | if(counter != vo.totalLines - 3 && line.length() != lineValidCharCount){ | 
|---|
|  |  |  | //无效数据,舍弃本行数据 | 
|---|
|  |  |  | }else{ | 
|---|
|  |  |  | line = line.substring(lineHeadEndIndex, line.length() - 2) ; | 
|---|
|  |  |  | if(vo.bytes.length == 0){ | 
|---|
|  |  |  | vo.bytes = HexUtil.decodeHex(line) ; | 
|---|
|  |  |  | }else{ | 
|---|
|  |  |  | byte[] bytes = HexUtil.decodeHex(line) ; | 
|---|
|  |  |  | vo.bytes = ByteUtil.bytesMerge(vo.bytes, bytes) ; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | counter ++ ; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * 以512为单位分割数据包 | 
|---|
|  |  |  | * @param bytes | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | public List<byte[]> splitBytesByUnit512(byte[] bytes){ | 
|---|
|  |  |  | List<byte[]> listByte512 = new ArrayList<>(); | 
|---|
|  |  |  | if(bytes != null && bytes.length > 0){ | 
|---|
|  |  |  | int index = 0 ; | 
|---|
|  |  |  | while (true){ | 
|---|
|  |  |  | if(index < bytes.length){ | 
|---|
|  |  |  | byte[] bs = ByteUtil.bytesSplit(bytes, index, bytesSplitUnit512) ; | 
|---|
|  |  |  | listByte512.add(bs) ; | 
|---|
|  |  |  | }else{ | 
|---|
|  |  |  | break ; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | index += bytesSplitUnit512; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return listByte512 ; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * 以512为单位分割数据包 | 
|---|
|  |  |  | * @param vo | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | private void splitBytesByUnit512(HexFileVo vo){ | 
|---|
|  |  |  | if(vo.bytes != null && vo.bytes.length > 0){ | 
|---|
|  |  |  | vo.realBytes += vo.bytes.length ; | 
|---|
|  |  |  | int index = 0 ; | 
|---|
|  |  |  | while (true){ | 
|---|
|  |  |  | if(index < vo.bytes.length){ | 
|---|
|  |  |  | byte[] bytes = ByteUtil.bytesSplit(vo.bytes, index, bytesSplitUnit512) ; | 
|---|
|  |  |  | vo.listByte512.add(bytes) ; | 
|---|
|  |  |  | vo.totalBytes512 += bytes.length ; | 
|---|
|  |  |  | }else{ | 
|---|
|  |  |  | break ; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | index += bytesSplitUnit512; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * 计算CRC16 | 
|---|
|  |  |  | * @param vo | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | private void calculateCrc(HexFileVo vo){ | 
|---|
|  |  |  | int crc = 0xffff ; | 
|---|
|  |  |  | if(vo.listByte512 != null && vo.listByte512.size() > 0){ | 
|---|
|  |  |  | CRC16 crc16 = new CRC16() ; | 
|---|
|  |  |  | for(byte[] bs : vo.listByte512){ | 
|---|
|  |  |  | crc =  crc16.CRC16_table(bs.length, bs, crc) ; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | vo.bytesCrc16 = crc ; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | } | 
|---|