| 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 ; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  | } |