|
@@ -1,11 +1,9 @@
|
|
|
package com.zksy.environment.service.impl;
|
|
package com.zksy.environment.service.impl;
|
|
|
|
|
|
|
|
-import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
|
|
|
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
import com.zksy.environment.domain.po.PictureMessage;
|
|
import com.zksy.environment.domain.po.PictureMessage;
|
|
|
import com.zksy.environment.mapper.PictureMessageMapper;
|
|
import com.zksy.environment.mapper.PictureMessageMapper;
|
|
|
import com.zksy.environment.service.PictureMessageService;
|
|
import com.zksy.environment.service.PictureMessageService;
|
|
|
-import com.zksy.environment.utils.DataCheckUtil;
|
|
|
|
|
import com.zksy.environment.utils.DateTimeUtil;
|
|
import com.zksy.environment.utils.DateTimeUtil;
|
|
|
import com.zksy.environment.utils.HexToImageConverter;
|
|
import com.zksy.environment.utils.HexToImageConverter;
|
|
|
import com.zksy.service.MinioFileStorageService;
|
|
import com.zksy.service.MinioFileStorageService;
|
|
@@ -16,160 +14,186 @@ import org.springframework.stereotype.Service;
|
|
|
import java.io.File;
|
|
import java.io.File;
|
|
|
import java.time.LocalDateTime;
|
|
import java.time.LocalDateTime;
|
|
|
import java.util.*;
|
|
import java.util.*;
|
|
|
|
|
+import java.util.regex.Matcher;
|
|
|
import java.util.regex.Pattern;
|
|
import java.util.regex.Pattern;
|
|
|
|
|
|
|
|
-/**
|
|
|
|
|
- * <p>
|
|
|
|
|
- * 遥测站图片报或中心站查询遥测站图片采集信息 服务实现类
|
|
|
|
|
- * </p>
|
|
|
|
|
- *
|
|
|
|
|
- * @author Yang
|
|
|
|
|
- * @since 2024-08-09
|
|
|
|
|
- */
|
|
|
|
|
@Slf4j
|
|
@Slf4j
|
|
|
@Service
|
|
@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
|
|
@Autowired
|
|
|
private MinioFileStorageService minioFileStorageService;
|
|
private MinioFileStorageService minioFileStorageService;
|
|
|
|
|
+
|
|
|
@Override
|
|
@Override
|
|
|
public boolean savePictureMessage(String oldDataStr, String dataStr) {
|
|
public boolean savePictureMessage(String oldDataStr, String dataStr) {
|
|
|
boolean flag = false;
|
|
boolean flag = false;
|
|
|
|
|
+ List<String> allDataSegments = allDataSegmentsThreadLocal.get();
|
|
|
|
|
+ Map<String, String> tempMap = tempMapThreadLocal.get(); // 从 ThreadLocal 获取 tempMap
|
|
|
|
|
+
|
|
|
try {
|
|
try {
|
|
|
PictureMessage pictureMessage = new PictureMessage();
|
|
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;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
- // 解析数据包
|
|
|
|
|
- String SYN = dataStr.substring(0, 2);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 解析固定头部:总包数、包序号
|
|
|
String totalHex = dataStr.substring(2, 5);
|
|
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;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
- // 将 packageNumberHex 转换为无符号整数
|
|
|
|
|
|
|
+
|
|
|
|
|
+ String packageNumberHex = dataStr.substring(5, 8);
|
|
|
int packageNumber;
|
|
int packageNumber;
|
|
|
try {
|
|
try {
|
|
|
packageNumber = Integer.parseUnsignedInt(packageNumberHex, 16);
|
|
packageNumber = Integer.parseUnsignedInt(packageNumberHex, 16);
|
|
|
} catch (NumberFormatException e) {
|
|
} catch (NumberFormatException e) {
|
|
|
- log.error("Failed to parse package number hex: " + packageNumberHex, e);
|
|
|
|
|
|
|
+ log.error("包序号解析失败,hex: {}", packageNumberHex, e);
|
|
|
|
|
+ clearThreadLocal(allDataSegments);
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 处理数据包
|
|
|
|
|
|
|
+ // 提取当前包的数据段
|
|
|
String segment;
|
|
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 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 {
|
|
} else {
|
|
|
- // 对于非首个包,直接获取数据段
|
|
|
|
|
|
|
+ // 分包:直接截取数据段
|
|
|
segment = dataStr.substring(8);
|
|
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;
|
|
return flag;
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
-// log.info("==========数据解析错误==========", oldDataStr);
|
|
|
|
|
- e.printStackTrace();
|
|
|
|
|
- allDataSegments.clear(); // 清空列表以便下次使用
|
|
|
|
|
|
|
+ log.error("图片数据解析失败,原始数据: {}", oldDataStr, e);
|
|
|
|
|
+ clearThreadLocal(allDataSegments);
|
|
|
|
|
+ tempMapThreadLocal.remove(); // 异常情况下也需要清理
|
|
|
}
|
|
}
|
|
|
return flag;
|
|
return flag;
|
|
|
}
|
|
}
|
|
|
-}
|
|
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 清理 ThreadLocal 中的数据,避免内存泄漏和状态污染
|
|
|
|
|
+ */
|
|
|
|
|
+ private void clearThreadLocal(List<String> list) {
|
|
|
|
|
+ list.clear();
|
|
|
|
|
+ allDataSegmentsThreadLocal.remove();
|
|
|
|
|
+ }
|
|
|
|
|
+}
|