Sfoglia il codice sorgente

refactor(environment-data-service): 重构图片消息处理逻辑

- 使用 ThreadLocal 替代全局变量,确保线程安全
- 优化数据解析和包处理逻辑,提高稳定性和效率
- 改进异常处理和日志记录,增强错误诊断能力
- 重构 MonitorDatasynch 类,优化数据同步流程
林仔 11 mesi fa
parent
commit
5a76c0a8ff

+ 136 - 112
environment-data-service/src/main/java/com/zksy/environment/service/impl/PictureMessageServiceImpl.java

@@ -1,11 +1,9 @@
 package com.zksy.environment.service.impl;
 
-import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zksy.environment.domain.po.PictureMessage;
 import com.zksy.environment.mapper.PictureMessageMapper;
 import com.zksy.environment.service.PictureMessageService;
-import com.zksy.environment.utils.DataCheckUtil;
 import com.zksy.environment.utils.DateTimeUtil;
 import com.zksy.environment.utils.HexToImageConverter;
 import com.zksy.service.MinioFileStorageService;
@@ -16,160 +14,186 @@ import org.springframework.stereotype.Service;
 import java.io.File;
 import java.time.LocalDateTime;
 import java.util.*;
+import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-/**
- * <p>
- * 遥测站图片报或中心站查询遥测站图片采集信息 服务实现类
- * </p>
- *
- * @author Yang
- * @since 2024-08-09
- */
 @Slf4j
 @Service
-public class PictureMessageServiceImpl extends ServiceImpl<PictureMessageMapper, PictureMessage> implements PictureMessageService {
-    private final List<String> allDataSegments = new ArrayList<>();
-    private final Object lock = new Object();
-    private HashMap<String,String> map = new HashMap<>();
+public class PictureMessageServiceImpl extends ServiceImpl<PictureMessageMapper, PictureMessage>
+        implements PictureMessageService {
+
+    // 每个传输任务独立存储数据段,用 ThreadLocal 隔离线程状态
+    private final ThreadLocal<List<String>> allDataSegmentsThreadLocal =
+            ThreadLocal.withInitial(ArrayList::new);
+
+    // 新增:使用 ThreadLocal 存储 tempMap,确保同一个图片传输使用同一个 map
+    private final ThreadLocal<Map<String, String>> tempMapThreadLocal =
+            ThreadLocal.withInitial(HashMap::new);
+
     @Autowired
     private MinioFileStorageService minioFileStorageService;
+
     @Override
     public boolean savePictureMessage(String oldDataStr, String dataStr) {
         boolean flag = false;
+        List<String> allDataSegments = allDataSegmentsThreadLocal.get();
+        Map<String, String> tempMap = tempMapThreadLocal.get(); // 从 ThreadLocal 获取 tempMap
+
         try {
             PictureMessage pictureMessage = new PictureMessage();
-            if (dataStr.length() < 8) { // 确保有足够的长度来提取 packageNumberHex
-                log.warn("Data string too short to extract package number: " + dataStr);
+            if (dataStr.length() < 8) {
+                log.warn("数据长度不足,无法解析包序号: {}", dataStr);
+                clearThreadLocal(allDataSegments);
                 return false;
             }
-            // 解析数据包
-            String SYN = dataStr.substring(0, 2);
+
+            // 解析固定头部:总包数、包序号
             String totalHex = dataStr.substring(2, 5);
-            String total = DataCheckUtil.hexToDecimalExample(totalHex);
-            String packageNumberHex = dataStr.substring(5, 8);
-            if (!packageNumberHex.matches("[0-9A-Fa-f]{3}")) {
-                log.warn("Invalid package number hex: " + packageNumberHex);
+            int totalPackets;
+            try {
+                totalPackets = Integer.parseUnsignedInt(totalHex, 16);
+                if (totalPackets <= 0) {
+                    log.error("总包数不能为 0,hex: {}", totalHex);
+                    clearThreadLocal(allDataSegments);
+                    return false;
+                }
+
+                // 根据总包数初始化数据段列表
+                if (allDataSegments.isEmpty()) {
+                    allDataSegments = new ArrayList<>(Collections.nCopies(totalPackets, ""));
+                    allDataSegmentsThreadLocal.set(allDataSegments);
+                }
+            } catch (Exception e) {
+                log.error("总包数解析失败,hex: {}", totalHex, e);
+                clearThreadLocal(allDataSegments);
                 return false;
             }
-            // 将 packageNumberHex 转换为无符号整数
+
+            String packageNumberHex = dataStr.substring(5, 8);
             int packageNumber;
             try {
                 packageNumber = Integer.parseUnsignedInt(packageNumberHex, 16);
             } catch (NumberFormatException e) {
-                log.error("Failed to parse package number hex: " + packageNumberHex, e);
+                log.error("包序号解析失败,hex: {}", packageNumberHex, e);
+                clearThreadLocal(allDataSegments);
                 return false;
             }
 
-            // 处理数据包
+            // 提取当前包的数据段
             String segment;
-            if ("01".equals(packageNumber)) {
-                // 解析初始数据包
-                String serialNumber = dataStr.substring(8, 12);
+            if (packageNumber == 1) {
+                // 首包:清空 tempMap 并解析额外信息(测站地址、时间等)
+                tempMap.clear();
+
                 String dataTime = dataStr.substring(12, 24);
-                String sendingMessageTime = "20" + dataTime;
-                map.put("sendingMessageTime", DateTimeUtil.parseDateTime(sendingMessageTime).toString());
-
-                // 测站地址
-                int stationAddressIndex = dataStr.indexOf("F1F1");
-                if (stationAddressIndex != -1 && stationAddressIndex + 10 < dataStr.length()) {
-                    String result = dataStr.substring(stationAddressIndex + 4, stationAddressIndex + 14);
-                    map.put("measuringStationAddress",result);
-                }
+                LocalDateTime sendingMessageTime = DateTimeUtil.parseDateTime("20" + dataTime);
+                tempMap.put("sendingMessageTime", sendingMessageTime.toString());
 
-                // 测站类型
-                Pattern pattern = Pattern.compile("F0F0", Pattern.CASE_INSENSITIVE);
-                String[] f0F0s = pattern.split(dataStr);
-                String f0F0 = f0F0s[0];
-                if (f0F0.length() >= 2) {
-                    String prefix = f0F0.substring(f0F0.length() - 2);
-                    map.put("measuringStationType",prefix);
+                // 改进测站地址解析逻辑
+                String stationAddressPattern = "F1F1([0-9A-F]{10})";
+                Pattern pattern = Pattern.compile(stationAddressPattern, Pattern.CASE_INSENSITIVE);
+                Matcher matcher = pattern.matcher(dataStr);
+                if (matcher.find()) {
+                    tempMap.put("measuringStationAddress", matcher.group(1));
                 }
 
-                // 观测时间
-                String f0F01 = f0F0s[1];
-                if (f0F01.length() >= 10) {
-                    String digits = f0F01.substring(0, 10);
-                    String observeTime = "20" + digits + "00";
-                    map.put("observeTime",DateTimeUtil.parseDateTime(observeTime).toString());
-                }
+                // 改进测站类型和观测时间解析逻辑
+                String[] f0F0Parts = dataStr.split("F0F0", Pattern.CASE_INSENSITIVE);
+                if (f0F0Parts.length > 1) {
+                    // 测站类型:F0F0之前的最后两个字符
+                    String firstPart = f0F0Parts[0];
+                    if (firstPart.length() >= 2) {
+                        tempMap.put("measuringStationType", firstPart.substring(firstPart.length() - 2));
+                    }
 
-                // 初始化数据段列表
-                synchronized (lock) {
-                    ((ArrayList<String>) allDataSegments).ensureCapacity(Integer.parseInt(total));
+                    // 观测时间:F0F0之后的前10个字符(YYYYMMDDHH格式)
+                    String secondPart = f0F0Parts[1];
+                    if (secondPart.length() >= 10) {
+                        String observeTimeStr = "20" + secondPart.substring(0, 10) + "00";
+                        LocalDateTime observeTime = DateTimeUtil.parseDateTime(observeTimeStr);
+                        tempMap.put("observeTime", observeTime.toString());
+                    }
                 }
 
-                // 对于首个包,直接获取数据段
-                List<String> dataSegments = new ArrayList<>();
-                int startIndex = dataStr.indexOf("F3F3");
-                if (startIndex != -1) {
-                    segment = dataStr.substring(startIndex + "F3F3".length());
-                    dataSegments.add(segment);
-                }
-                segment = String.join("", dataSegments);
+                // 提取首包数据段(基于 "F3F3" 标识)
+                int dataStartIndex = dataStr.indexOf("F3F3");
+                segment = (dataStartIndex != -1)
+                        ? dataStr.substring(dataStartIndex + "F3F3".length())
+                        : "";
             } else {
-                // 对于非首个包,直接获取数据段
+                // 分包:直接截取数据段
                 segment = dataStr.substring(8);
             }
 
-            // 保存数据段
-            synchronized (lock) {
-                int index = packageNumber - 1;
-                // 初始化 allDataSegments 到正确大小,如果还没有的话
-                int totalPackets = Integer.parseInt(total);
-                if (allDataSegments.size() < totalPackets) {
-                    while (allDataSegments.size() < totalPackets) {
-                        allDataSegments.add(null);
-                    }
-                }
+            int index = packageNumber - 1;
+            if (index < 0 || index >= totalPackets) {
+                log.warn("包序号越界,总包数: {}, 包序号: {}", totalPackets, packageNumber);
+                clearThreadLocal(allDataSegments);
+                return false;
+            }
+            allDataSegments.set(index, segment);
 
-                // 确保 index 不会越界
-                if (index >= 0 && index < allDataSegments.size()) {
-                    allDataSegments.set(index, segment);
-                } else {
-                    log.warn("Invalid package number: " + packageNumber + ". Index out of bounds: " + index);
-                    return false;
+            // 检查是否收全所有包
+            if (totalPackets == index + 1) {
+                // 拼接完整数据
+                StringBuilder completeDataBuilder = new StringBuilder();
+                for (String seg : allDataSegments) {
+                    completeDataBuilder.append(Objects.toString(seg, ""));
                 }
+                String completeImageData = completeDataBuilder.toString();
+                log.info("完整图片数据长度: {}", completeImageData.length());
+
+                // 转换为图片并上传
+                File image = HexToImageConverter.hexStringToImage(completeImageData, "output.png");
+                if (image != null) {
+                    String filePath = minioFileStorageService.uploadFileByFile(image, "environment-image");
+                    pictureMessage.setMessage(filePath);
 
-//                System.out.println("packageNumber====="+packageNumber+"total====="+totalPackets);
-                // 检查是否所有数据包都已经收到
-                if (packageNumber == totalPackets) {
-                    // 重新组合所有的数据包
-                    StringBuilder completeImageDataBuilder = new StringBuilder();
-                    for (String segment1 : allDataSegments) {
-                        // 确保 segment 不为 null
-                        if (segment1 != null) {
-                            completeImageDataBuilder.append(segment1);
-                        }
+                    // 填充对象字段
+                    pictureMessage.setId(UUID.randomUUID().toString().replace("-", ""));
+                    pictureMessage.setCreateTime(LocalDateTime.now());
+
+                    // 从tempMap获取值并设置,增加空值检查
+                    if (tempMap.get("sendingMessageTime") != null) {
+                        pictureMessage.setSendingMessageTime(LocalDateTime.parse(tempMap.get("sendingMessageTime")));
+                    }
+                    if (tempMap.get("measuringStationAddress") != null) {
+                        pictureMessage.setMeasuringStationAddress(tempMap.get("measuringStationAddress"));
+                    }
+                    if (tempMap.get("measuringStationType") != null) {
+                        pictureMessage.setMeasuringStationType(tempMap.get("measuringStationType"));
+                    }
+                    if (tempMap.get("observeTime") != null) {
+                        pictureMessage.setObserveTime(LocalDateTime.parse(tempMap.get("observeTime")));
                     }
-                    String completeImageData = completeImageDataBuilder.toString();
-                    System.out.println("图片集合:" + completeImageData);
-                    File image = HexToImageConverter.hexStringToImage(completeImageData, "output.png");
-
-                    if (image != null) {
-                        String filePath = minioFileStorageService.uploadFileByFile(image, "environment-image");
-                        // 设置完整的消息
-                        pictureMessage.setMessage(filePath);
-                        System.out.println("pictureMessage=====" + pictureMessage);
-                        // 设置 ID 和创建时间
-                        pictureMessage.setId(UUID.randomUUID().toString().replace("-", ""));
-                        pictureMessage.setCreateTime(LocalDateTime.now());
-                        pictureMessage.setSendingMessageTime(LocalDateTime.parse(map.get("sendingMessageTime")));
-                        pictureMessage.setMeasuringStationAddress(map.get("measuringStationAddress"));
-                        pictureMessage.setMeasuringStationType(map.get("measuringStationType"));
-                        pictureMessage.setObserveTime(LocalDateTime.parse(map.get("observeTime")));
-                        // 执行保存操作
-                        flag = super.save(pictureMessage);
+
+                    // 保存到数据库
+                    flag = super.save(pictureMessage);
+                    if (flag) {
+                        log.info("图片数据保存成功,文件路径: {}", filePath);
+                    } else {
+                        log.error("数据库保存失败");
                     }
-                    allDataSegments.clear(); // 清空列表以便下次使用
                 }
+
+                // 清理状态,为下次传输做准备
+                clearThreadLocal(allDataSegments);
+                tempMapThreadLocal.remove(); // 移除 tempMap
             }
             return flag;
         } catch (Exception e) {
-//            log.info("==========数据解析错误==========", oldDataStr);
-            e.printStackTrace();
-            allDataSegments.clear(); // 清空列表以便下次使用
+            log.error("图片数据解析失败,原始数据: {}", oldDataStr, e);
+            clearThreadLocal(allDataSegments);
+            tempMapThreadLocal.remove(); // 异常情况下也需要清理
         }
         return flag;
     }
-}
+
+    /**
+     * 清理 ThreadLocal 中的数据,避免内存泄漏和状态污染
+     */
+    private void clearThreadLocal(List<String> list) {
+        list.clear();
+        allDataSegmentsThreadLocal.remove();
+    }
+}

+ 155 - 114
environment-data-service/src/main/java/com/zksy/environment/utils/MonitorDatasynch.java

@@ -11,6 +11,7 @@ import org.springframework.stereotype.Service;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
 
@@ -30,135 +31,175 @@ public class MonitorDatasynch {
 
     private final List<String> data1List = new ArrayList<>();
     private final Object lock = new Object();
+    private AtomicBoolean isWaitingForNextStart = new AtomicBoolean(false);
+    private int maxBufferSize = 10; // 最大缓冲数据条数,防止内存溢出
 
     /**
      * 获取水质同步数据,数据有效性校验,解析
      */
     public String pushMonitorData(ByteBuf dataBuf) {
         String response = null;
-    try {
-        if (dataBuf.isReadable()) {
-            // 读取并转换 ByteBuf 中的数据为16进制字符串
-            byte[] dataByte = new byte[dataBuf.readableBytes()];
-            dataBuf.readBytes(dataByte);
-            // 读取并转换 ByteBuf 中的数据为16进制字符串
-            String oldDataStr = HexToImageConverter.bytesToHex(dataByte);
-
-//            log.info("==========接收到数据:" + oldDataStr + "==========");
-
-            // 获取检验码数据
-            String codeStr = oldDataStr.substring(0, oldDataStr.length() - 4);
-            // 获取数据CRC16检验码
-            String code = "";
-            code = oldDataStr.substring(oldDataStr.length() - 4);
-
-            // 通过数据生成CRC16校验码
-            String nowCode = "";
-            nowCode = DataCheckUtil.getCRC16Result(codeStr);
-
-            //先将两个字符串转换为相同的大小写形式(默认是小写)
-            if (code.equals(nowCode)) {
-                boolean flag = false;
-                // dataType == "32" 遥测站定时报,"33" 遥测站加时报,"34" 遥测站小时报,"36" 查询/报送 JPG 图片信息
-                // 获取报文特征
-                String dataType = oldDataStr.substring(20, 22);
-
-                if (!"2F".equals(dataType)) {
-                    if ("36".equals(dataType)) {
-                        // 获取数据正文
-                        String dataStr = oldDataStr.substring(26, oldDataStr.length() - 6);
-//                        log.info("数据主体正文:" + dataStr);
-                        flag = pictureMessageService.savePictureMessage(oldDataStr, dataStr);
-                        //清除data1List
-                        data1List.clear();
-                    } else {
-                        // 获取数据正文
-                        String dataStr = oldDataStr.substring(32, oldDataStr.length() - 6);
-//                        log.info("数据主体正文:" + dataStr);
-                        flag = stationHourService.saveStationHour(oldDataStr, dataStr);
-                    }
-
-                    if (flag) {
-                        log.info("新增成功", oldDataStr);
-                    } else {
-//                        log.info("新增失败", oldDataStr);
-                    }
-                } else {
-                    log.info("收到2f数据========", oldDataStr);
-                }
-            }else {
-                /**
-                 * 图片数据第一条分段接收的问题进行合并
-                 */
-                //开始符
-                String startCharacter = oldDataStr.substring(0, 4);
-                //结束符
-                // 获取字符串的长度
-                int length = oldDataStr.length();
-
-                // 计算倒数第5位和第6位的位置
-                int pos5 = length - 5;
-                int pos6 = length - 6;
-
-                // 截取倒数第5位到倒数第6位的子字符串
-                String subStr = oldDataStr.substring(pos6, pos5 + 1);
-                if(!"7E7E".equals(startCharacter) || !"17".equals(subStr)) {
-                    // 将不完整数据存入data1List
-                    synchronized (lock) {
-                        data1List.add(oldDataStr);
-                    }
-                    StringBuilder dataBuilder = new StringBuilder();
-                    if (data1List.size() >= 2) {
-                        for (String data : data1List) {
-                            dataBuilder.append(data);
-                        }
-                    }
-                    if (dataBuilder != null && dataBuilder.length() > 6) {
-                        //开始符
-                        String dataBuilderStartCharacter = dataBuilder.substring(0, 4);
-                        // 获取字符串的长度
-                        int dataLength = dataBuilder.length();
-                        // 计算倒数第5位和第6位的位置
-                        int dataPos5 = dataLength - 5;
-                        int dataPos6 = dataLength - 6;
-                        // 截取倒数第5位到倒数第6位的子字符串
-                        String dataSubStr = dataBuilder.substring(dataPos6, dataPos5 + 1);
-                        if (!"7E7E".equals(dataBuilderStartCharacter) && !"17".equals(dataSubStr)) {
-                            return response;
-                        } else {
-                            oldDataStr = dataBuilder.toString();
-                            System.out.println("组合不完整数据dataBuilder======" + dataBuilder);
-                        }
-                    }
-                }
-                // 获取报文特征
-                String dataType = oldDataStr.substring(20, 22);
+        try {
+            if (dataBuf.isReadable()) {
+                // 读取并转换 ByteBuf 中的数据为16进制字符串
+                byte[] dataByte = new byte[dataBuf.readableBytes()];
+                dataBuf.readBytes(dataByte);
+                String oldDataStr = HexToImageConverter.bytesToHex(dataByte);
 
                 // 获取检验码数据
-                String codeStrBuilder = oldDataStr.substring(0, oldDataStr.length() - 4);
+                String codeStr = oldDataStr.substring(0, oldDataStr.length() - 4);
                 // 获取数据CRC16检验码
-                String codeBuilder = oldDataStr.substring(oldDataStr.length() - 4);
+                String code = oldDataStr.substring(oldDataStr.length() - 4);
 
                 // 通过数据生成CRC16校验码
-                String nowCodeBuilder = DataCheckUtil.getCRC16Result(codeStrBuilder);
-                if(codeBuilder.equalsIgnoreCase(nowCodeBuilder)) {
-                    if ("36".equals(dataType)) {
-                        // 获取数据正文
-                        String dataStr = oldDataStr.substring(26, oldDataStr.length() - 6);
-//                        log.info("数据主体正文:" + dataStr);
-                        pictureMessageService.savePictureMessage(oldDataStr, dataStr);
-                        data1List.clear();
-                    }
-                }else{
-                    log.info("==========数据解包校验不正确==========");
+                String nowCode = DataCheckUtil.getCRC16Result(codeStr);
+
+                // 先将两个字符串转换为相同的大小写形式(默认是小写)
+                if (code.equals(nowCode)) {
+                    // 校验通过,正常处理数据
+                    processValidData(oldDataStr);
+                } else {
+                    // 校验不通过,处理图片数据的拼接
+                    handleInvalidData(oldDataStr);
+                }
+            } else {
+                log.info("==========数据长度格式不正确==========");
+            }
+        } catch (Exception e) {
+            log.error("处理数据异常", e);
+        }
+        return response;
+    }
+
+    /**
+     * 处理校验通过的数据
+     */
+    private void processValidData(String oldDataStr) {
+        boolean flag = false;
+        // dataType == "32" 遥测站定时报,"33" 遥测站加时报,"34" 遥测站小时报,"36" 查询/报送 JPG 图片信息
+        // 获取报文特征
+        String dataType = oldDataStr.substring(20, 22);
+
+        if (!"2F".equals(dataType)) {
+            if ("36".equals(dataType)) {
+                // 获取数据正文
+                String dataStr = oldDataStr.substring(26, oldDataStr.length() - 6);
+                flag = pictureMessageService.savePictureMessage(oldDataStr, dataStr);
+                // 清除data1List
+                synchronized (lock) {
+                    data1List.clear();
+                    isWaitingForNextStart.set(false);
+                }
+                if (flag) {
+                    log.info("新增成功", oldDataStr);
+                }
+            } else {
+                // 获取数据正文
+                String dataStr = oldDataStr.substring(32, oldDataStr.length() - 6);
+                flag = stationHourService.saveStationHour(oldDataStr, dataStr);
+                if (flag) {
+                    log.info("新增成功", oldDataStr);
+                } else {
+                    log.info("新增失败", oldDataStr);
                 }
             }
         } else {
-            log.info("==========数据长度格式不正确==========");
+            log.info("收到2f数据========", oldDataStr);
         }
-    }catch (Exception e){
-        e.printStackTrace();
     }
-        return response;
+
+    /**
+     * 处理校验不通过的数据
+     */
+    private void handleInvalidData(String oldDataStr) {
+        // 检查是否是新的数据包开始
+        String startCharacter = oldDataStr.substring(0, 4);
+        String endCharacter = oldDataStr.substring(oldDataStr.length() - 2);
+
+        synchronized (lock) {
+            // 如果检测到新的数据包开始,且之前正在等待拼接
+            if ("7E7E".equals(startCharacter) && isWaitingForNextStart.get()) {
+                // 尝试处理已缓存的数据
+                processBufferedData();
+                // 重置标志
+                isWaitingForNextStart.set(false);
+                data1List.clear();
+            }
+
+            // 如果是dataType=36的第一条数据且校验不通过,或者正在等待拼接
+            String dataType = oldDataStr.substring(20, 22);
+            if ("36".equals(dataType) || isWaitingForNextStart.get()) {
+                // 将数据添加到缓冲区
+                data1List.add(oldDataStr);
+                isWaitingForNextStart.set(true);
+
+                // 限制缓冲区大小,防止内存溢出
+                if (data1List.size() > maxBufferSize) {
+                    log.warn("缓冲区数据过多,移除最早的数据");
+                    data1List.remove(0);
+                }
+
+                // 尝试拼接并校验
+                if (tryToProcessBufferedData()) {
+                    return; // 成功处理,返回
+                }
+
+                // 如果检测到数据包结束但仍未通过校验
+                if ("17".equals(endCharacter) && data1List.size() > 1) {
+                    log.error("图片数据拼接后校验失败,数据可能损坏");
+                    data1List.clear();
+                    isWaitingForNextStart.set(false);
+                }
+            }
+        }
+    }
+
+    /**
+     * 尝试处理已缓存的数据
+     */
+    private boolean tryToProcessBufferedData() {
+        if (data1List.isEmpty()) {
+            return false;
+        }
+
+        StringBuilder dataBuilder = new StringBuilder();
+        for (String data : data1List) {
+            dataBuilder.append(data);
+        }
+
+        String combinedData = dataBuilder.toString();
+        if (combinedData.length() <= 6) {
+            return false;
+        }
+
+        // 校验拼接后的数据
+        String codeStr = combinedData.substring(0, combinedData.length() - 4);
+        String code = combinedData.substring(combinedData.length() - 4);
+        String nowCode = DataCheckUtil.getCRC16Result(codeStr);
+
+        if (code.equalsIgnoreCase(nowCode)) {
+            String dataType = combinedData.substring(20, 22);
+            if ("36".equals(dataType)) {
+                String dataStr = combinedData.substring(26, combinedData.length() - 6);
+                boolean saved = pictureMessageService.savePictureMessage(combinedData, dataStr);
+                if (saved) {
+                    log.info("图片数据拼接成功并保存");
+                    data1List.clear();
+                    isWaitingForNextStart.set(false);
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 处理已缓存的数据(当检测到新的数据包开始时调用)
+     */
+    private void processBufferedData() {
+        if (!tryToProcessBufferedData() && !data1List.isEmpty()) {
+            log.error("读取到下一条7E7E之前,缓存的图片数据一直未能通过校验,校验失败");
+            data1List.clear();
+        }
     }
 }

BIN
output.png


+ 6 - 4
visualization-service/src/main/java/com/zksy/visualization/controller/ParkingManageController.java

@@ -383,6 +383,8 @@ public class ParkingManageController {
                 .host(iccConfigProperty.getHost())
                 .addPathSegments("/evo-apigw/ipms/caraccess/find/hisCount")
                 // 添加查询参数时检查null值并处理日期格式化
+                .addQueryParameter("pageNum", valueOrNull(queryRequest.getPageNum()))
+                .addQueryParameter("pageSize", valueOrNull(queryRequest.getPageSize()))
                 .addQueryParameter("entranceChannelIds", nullToEmpty(queryRequest.getEntranceChannelIds()))
                 .addQueryParameter("exitusChannelIds", nullToEmpty(queryRequest.getExitusChannelIds()))
                 .addQueryParameter("carType", valueOrNull(queryRequest.getCarType()))
@@ -393,10 +395,10 @@ public class ParkingManageController {
                 .addQueryParameter("parkingLotCodes", nullToEmpty(queryRequest.getParkingLotCodes()))
                 .addQueryParameter("enterCarNumLikeStr", nullToEmpty(queryRequest.getEnterCarNumLikeStr()))
                 .addQueryParameter("exitCarNumLikeStr", nullToEmpty(queryRequest.getExitCarNumLikeStr()))
-                .addQueryParameter("enterTimeStrLeft", formatDate(queryRequest.getEnterTimeStrLeft()))
-                .addQueryParameter("enterTimeStrRight", formatDate(queryRequest.getEnterTimeStrRight()))
-                .addQueryParameter("exitTimeStrLeft", formatDate(queryRequest.getExitTimeStrLeft()))
-                .addQueryParameter("exitTimeStrRight", formatDate(queryRequest.getExitTimeStrRight()))
+                .addQueryParameter("enterTimeStrLeft", nullToEmpty(queryRequest.getEnterTimeStrLeft()))
+                .addQueryParameter("enterTimeStrRight", nullToEmpty(queryRequest.getEnterTimeStrRight()))
+                .addQueryParameter("exitTimeStrLeft", nullToEmpty(queryRequest.getExitTimeStrLeft()))
+                .addQueryParameter("exitTimeStrRight", nullToEmpty(queryRequest.getExitTimeStrRight()))
                 .build();
 
         Request request = new Request.Builder()

+ 10 - 4
visualization-service/src/main/java/com/zksy/visualization/domain/request/ImportExportStatisticsRequest.java

@@ -14,6 +14,12 @@ import java.util.Date;
  */
 @Data
 public class ImportExportStatisticsRequest {
+    @ApiModelProperty(value = "页码",example = "1", required = true)
+    private Integer pageNum;
+
+    @ApiModelProperty(value = "分页数据大小,最大1000",example = "1000", required = true)
+    private Integer pageSize;
+
     @ApiModelProperty(value = "入口通道id,多个时用半角逗号隔开; 获取方式一:调道闸通道信息全量查询接口 ,返回字段channelId;获取方式二:调设备列表查询接口 ,返回字段sluiceUnits.channels下的deviceCode、unitSeq、channelSeq,并组装成完整的通道编码,组装格式:deviceCode$14$unitSeq$channelSeq", required = false)
     private String entranceChannelIds;
 
@@ -45,14 +51,14 @@ public class ImportExportStatisticsRequest {
     private String exitCarNumLikeStr;
 
     @ApiModelProperty(value = "进场时间起,格式:yyyy-MM-dd HH:mm:ss", required = false)
-    private Date enterTimeStrLeft;
+    private String enterTimeStrLeft;
 
     @ApiModelProperty(value = "进场时间止,格式:yyyy-MM-dd HH:mm:ss", required = false)
-    private Date enterTimeStrRight;
+    private String enterTimeStrRight;
 
     @ApiModelProperty(value = "出场时间起,格式:yyyy-MM-dd HH:mm:ss", required = false)
-    private Date exitTimeStrLeft;
+    private String exitTimeStrLeft;
 
     @ApiModelProperty(value = "出场时间止,格式:yyyy-MM-dd HH:mm:ss", required = false)
-    private Date exitTimeStrRight;
+    private String exitTimeStrRight;
 }