|
|
|
@ -1,7 +1,6 @@ |
|
|
|
package com.inspect.simulator.service.impl; |
|
|
|
|
|
|
|
|
|
|
|
import cn.hutool.core.util.ObjectUtil; |
|
|
|
import com.alibaba.fastjson.JSON; |
|
|
|
import com.alibaba.fastjson.JSONObject; |
|
|
|
import com.fasterxml.jackson.databind.JsonNode; |
|
|
|
@ -26,21 +25,6 @@ import com.inspect.simulator.tempCount.TempCount; |
|
|
|
import com.inspect.simulator.utils.StringUtils; |
|
|
|
import com.inspect.simulator.utils.redis.RedisService; |
|
|
|
import com.inspect.simulator.utils.sftp.SftpClient; |
|
|
|
import java.awt.*; |
|
|
|
import java.awt.image.BufferedImage; |
|
|
|
import java.io.*; |
|
|
|
import java.net.URI; |
|
|
|
import java.net.URISyntaxException; |
|
|
|
import java.nio.ByteBuffer; |
|
|
|
import java.nio.ByteOrder; |
|
|
|
import java.nio.charset.StandardCharsets; |
|
|
|
import java.nio.file.Paths; |
|
|
|
import java.text.SimpleDateFormat; |
|
|
|
import java.util.*; |
|
|
|
import java.util.List; |
|
|
|
import java.util.concurrent.TimeUnit; |
|
|
|
import javax.annotation.Resource; |
|
|
|
import javax.imageio.ImageIO; |
|
|
|
import okhttp3.*; |
|
|
|
import org.apache.commons.compress.utils.IOUtils; |
|
|
|
import org.apache.commons.csv.CSVFormat; |
|
|
|
@ -56,6 +40,24 @@ import org.springframework.beans.factory.annotation.Value; |
|
|
|
import org.springframework.http.ResponseEntity; |
|
|
|
import org.springframework.stereotype.Service; |
|
|
|
|
|
|
|
import javax.annotation.Resource; |
|
|
|
import javax.imageio.ImageIO; |
|
|
|
import java.awt.*; |
|
|
|
import java.awt.image.BufferedImage; |
|
|
|
import java.io.*; |
|
|
|
import java.net.URI; |
|
|
|
import java.net.URISyntaxException; |
|
|
|
import java.nio.ByteBuffer; |
|
|
|
import java.nio.ByteOrder; |
|
|
|
import java.nio.charset.StandardCharsets; |
|
|
|
import java.nio.file.Files; |
|
|
|
import java.nio.file.Path; |
|
|
|
import java.nio.file.Paths; |
|
|
|
import java.text.SimpleDateFormat; |
|
|
|
import java.util.*; |
|
|
|
import java.util.List; |
|
|
|
import java.util.concurrent.TimeUnit; |
|
|
|
|
|
|
|
/** |
|
|
|
* 海康威视测试服务类 |
|
|
|
*/ |
|
|
|
@ -99,6 +101,116 @@ public class HikVisionServiceImpl implements HikVisionService { |
|
|
|
@Resource |
|
|
|
private ImageAnnotationService annotationService; |
|
|
|
|
|
|
|
//校验 |
|
|
|
public static byte[] locateAndReadInfraredData(byte[] fileData) { |
|
|
|
// 文件末尾标识 |
|
|
|
final byte[] FOOTER_SIGN = { |
|
|
|
(byte) 0x37, (byte) 0x66, (byte) 0x07, (byte) 0x1a, |
|
|
|
(byte) 0x12, (byte) 0x3a, (byte) 0x4c, (byte) 0x9f, |
|
|
|
(byte) 0xa9, (byte) 0x5d, (byte) 0x21, (byte) 0xd2, |
|
|
|
(byte) 0xda, (byte) 0x7d, (byte) 0x26, (byte) 0xbc |
|
|
|
}; |
|
|
|
|
|
|
|
try { |
|
|
|
// 1. 检查数据大小 |
|
|
|
if (fileData.length < FOOTER_SIGN.length + 4) { |
|
|
|
System.err.println("错误:数据太小"); |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
// 2. 读取并验证文件尾标识 |
|
|
|
byte[] footer = new byte[FOOTER_SIGN.length]; |
|
|
|
// 从数组末尾读取FOOTER_SIGN |
|
|
|
System.arraycopy(fileData, fileData.length - FOOTER_SIGN.length, footer, 0, FOOTER_SIGN.length); |
|
|
|
|
|
|
|
if (!Arrays.equals(footer, FOOTER_SIGN)) { |
|
|
|
System.err.println("错误:文件标识不匹配"); |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
// 3. 读取偏移量(小端序) |
|
|
|
byte[] offsetBytes = new byte[4]; |
|
|
|
// 读取FOOTER_SIGN前的4个字节作为偏移量 |
|
|
|
System.arraycopy(fileData, fileData.length - FOOTER_SIGN.length - 4, offsetBytes, 0, 4); |
|
|
|
int dataOffset = ByteBuffer.wrap(offsetBytes) |
|
|
|
.order(ByteOrder.LITTLE_ENDIAN) |
|
|
|
.getInt(); |
|
|
|
|
|
|
|
// 4. 验证偏移量有效性 |
|
|
|
if (dataOffset < 0 || dataOffset >= fileData.length - FOOTER_SIGN.length - 4) { |
|
|
|
System.err.println("错误:无效的偏移量"); |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
// 5. 定位到偏移量处读取数据(直到文件尾标识前) |
|
|
|
int dataLength = fileData.length - FOOTER_SIGN.length - 4 - dataOffset; |
|
|
|
if (dataLength <= 0) { |
|
|
|
System.err.println("错误:数据长度为0"); |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
byte[] infraredData = new byte[dataLength]; |
|
|
|
System.arraycopy(fileData, dataOffset, infraredData, 0, dataLength); |
|
|
|
|
|
|
|
return infraredData; |
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
System.err.println("处理错误: " + e.getMessage()); |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//二维数组转 CSV |
|
|
|
private static String convertMatrixToCsv(float[][] matrix) { |
|
|
|
StringBuilder csvBuilder = new StringBuilder(); |
|
|
|
for (float[] row : matrix) { |
|
|
|
for (int i = 0; i < row.length; i++) { |
|
|
|
csvBuilder.append(row[i]); |
|
|
|
if (i < row.length - 1) { |
|
|
|
csvBuilder.append(","); |
|
|
|
} |
|
|
|
} |
|
|
|
csvBuilder.append("\n"); |
|
|
|
} |
|
|
|
return csvBuilder.toString(); |
|
|
|
} |
|
|
|
|
|
|
|
public static String truncateFileNameToCsv(String originalPath) { |
|
|
|
// 获取文件名部分(最后一个 '/' 之后的内容) |
|
|
|
int lastSlashIndex = originalPath.lastIndexOf('/'); |
|
|
|
if (lastSlashIndex == -1) { |
|
|
|
// 如果没有路径分隔符,直接处理整个字符串 |
|
|
|
return processFileNameToCsv(originalPath); |
|
|
|
} |
|
|
|
|
|
|
|
// 分离路径和文件名 |
|
|
|
String path = originalPath.substring(0, lastSlashIndex + 1); |
|
|
|
String fileName = originalPath.substring(lastSlashIndex + 1); |
|
|
|
|
|
|
|
// 处理文件名部分并强制改为.csv |
|
|
|
String processedFileName = processFileNameToCsv(fileName); |
|
|
|
|
|
|
|
|
|
|
|
return path + processedFileName; |
|
|
|
} |
|
|
|
|
|
|
|
private static String processFileNameToCsv(String fileName) { |
|
|
|
// 去掉文件扩展名(如果有) |
|
|
|
int lastDotIndex = fileName.lastIndexOf('.'); |
|
|
|
if (lastDotIndex != -1) { |
|
|
|
fileName = fileName.substring(0, lastDotIndex); |
|
|
|
} |
|
|
|
|
|
|
|
// 去掉最后一个 '_' 及其后面的部分 |
|
|
|
int lastUnderscoreIndex = fileName.lastIndexOf('_'); |
|
|
|
if (lastUnderscoreIndex != -1) { |
|
|
|
fileName = fileName.substring(0, lastUnderscoreIndex); |
|
|
|
} |
|
|
|
|
|
|
|
// 强制添加.csv后缀 |
|
|
|
return fileName + ".csv"; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public AjaxResult capturePicture(InfraPictureInfo infraPictureInfo) { |
|
|
|
log.info("capturePicture, coordinate:{}", infraPictureInfo); |
|
|
|
@ -281,78 +393,6 @@ public class HikVisionServiceImpl implements HikVisionService { |
|
|
|
return infraredInfo; |
|
|
|
} |
|
|
|
|
|
|
|
//校验 |
|
|
|
public static byte[] locateAndReadInfraredData(byte[] fileData) { |
|
|
|
// 文件末尾标识 |
|
|
|
final byte[] FOOTER_SIGN = { |
|
|
|
(byte) 0x37, (byte) 0x66, (byte) 0x07, (byte) 0x1a, |
|
|
|
(byte) 0x12, (byte) 0x3a, (byte) 0x4c, (byte) 0x9f, |
|
|
|
(byte) 0xa9, (byte) 0x5d, (byte) 0x21, (byte) 0xd2, |
|
|
|
(byte) 0xda, (byte) 0x7d, (byte) 0x26, (byte) 0xbc |
|
|
|
}; |
|
|
|
|
|
|
|
try { |
|
|
|
// 1. 检查数据大小 |
|
|
|
if (fileData.length < FOOTER_SIGN.length + 4) { |
|
|
|
System.err.println("错误:数据太小"); |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
// 2. 读取并验证文件尾标识 |
|
|
|
byte[] footer = new byte[FOOTER_SIGN.length]; |
|
|
|
// 从数组末尾读取FOOTER_SIGN |
|
|
|
System.arraycopy(fileData, fileData.length - FOOTER_SIGN.length, footer, 0, FOOTER_SIGN.length); |
|
|
|
|
|
|
|
if (!Arrays.equals(footer, FOOTER_SIGN)) { |
|
|
|
System.err.println("错误:文件标识不匹配"); |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
// 3. 读取偏移量(小端序) |
|
|
|
byte[] offsetBytes = new byte[4]; |
|
|
|
// 读取FOOTER_SIGN前的4个字节作为偏移量 |
|
|
|
System.arraycopy(fileData, fileData.length - FOOTER_SIGN.length - 4, offsetBytes, 0, 4); |
|
|
|
int dataOffset = ByteBuffer.wrap(offsetBytes) |
|
|
|
.order(ByteOrder.LITTLE_ENDIAN) |
|
|
|
.getInt(); |
|
|
|
|
|
|
|
// 4. 验证偏移量有效性 |
|
|
|
if (dataOffset < 0 || dataOffset >= fileData.length - FOOTER_SIGN.length - 4) { |
|
|
|
System.err.println("错误:无效的偏移量"); |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
// 5. 定位到偏移量处读取数据(直到文件尾标识前) |
|
|
|
int dataLength = fileData.length - FOOTER_SIGN.length - 4 - dataOffset; |
|
|
|
if (dataLength <= 0) { |
|
|
|
System.err.println("错误:数据长度为0"); |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
byte[] infraredData = new byte[dataLength]; |
|
|
|
System.arraycopy(fileData, dataOffset, infraredData, 0, dataLength); |
|
|
|
|
|
|
|
return infraredData; |
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
System.err.println("处理错误: " + e.getMessage()); |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
//二维数组转 CSV |
|
|
|
private static String convertMatrixToCsv(float[][] matrix) { |
|
|
|
StringBuilder csvBuilder = new StringBuilder(); |
|
|
|
for (float[] row : matrix) { |
|
|
|
for (int i = 0; i < row.length; i++) { |
|
|
|
csvBuilder.append(row[i]); |
|
|
|
if (i < row.length - 1) { |
|
|
|
csvBuilder.append(","); |
|
|
|
} |
|
|
|
} |
|
|
|
csvBuilder.append("\n"); |
|
|
|
} |
|
|
|
return csvBuilder.toString(); |
|
|
|
} |
|
|
|
//调用华软接口获取csv文件 |
|
|
|
public float[][] UploadFtpImage(String url, String type, String image) { |
|
|
|
log.info("imageName" + image); |
|
|
|
@ -691,7 +731,8 @@ public class HikVisionServiceImpl implements HikVisionService { |
|
|
|
if (inputStream != null) { |
|
|
|
try { |
|
|
|
inputStream.close(); |
|
|
|
} catch (IOException ignored) {} |
|
|
|
} catch (IOException ignored) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 关闭FTP连接 |
|
|
|
@ -835,12 +876,26 @@ public class HikVisionServiceImpl implements HikVisionService { |
|
|
|
|
|
|
|
if (picData != null) { |
|
|
|
int[] parts = Arrays.stream(picData.split(",")).mapToInt(Integer::parseInt).toArray(); |
|
|
|
// 6个值:分为左上角(x,y) 右下角(x,y) 图片高宽(h,w) |
|
|
|
if (parts.length == 6) { |
|
|
|
coordinates.add(new Coordinate(parts[0], parts[1], parts[2], parts[3], parts[5], parts[4])); |
|
|
|
} else if (parts.length == 10) { |
|
|
|
// 10个值:分为左上角(x,y) 右上角(x,y) 右下角(x,y) 左下角(x,y) 图片宽高(w,h) |
|
|
|
coordinates.add(new Coordinate(parts[0], parts[1], parts[4], parts[5], parts[8], parts[9])); |
|
|
|
|
|
|
|
if (parts.length > 2) { |
|
|
|
int last1 = parts[parts.length - 1]; |
|
|
|
int last2 = parts[parts.length - 2]; |
|
|
|
int width = Math.max(last1, last2); |
|
|
|
int height = Math.min(last1, last2); |
|
|
|
int[] points; |
|
|
|
// 移动设备的矩形框,只有6个值:分为左上角(x,y) 右下角(x,y) 图片高宽(h,w) |
|
|
|
if (parts.length == 6 && last1 > last2) { |
|
|
|
points = new int[]{ |
|
|
|
parts[0], parts[1], |
|
|
|
parts[2], parts[1], |
|
|
|
parts[2], parts[3], |
|
|
|
parts[0], parts[3] |
|
|
|
}; |
|
|
|
} else { |
|
|
|
points = new int[parts.length - 2]; |
|
|
|
System.arraycopy(parts, 0, points, 0, parts.length - 2); |
|
|
|
} |
|
|
|
coordinates.add(new Coordinate(points, width, height)); |
|
|
|
} else { |
|
|
|
log.error("{} 点位画框数据异常,请检查!", basedataPatrolPoint.getPatrolPointId()); |
|
|
|
} |
|
|
|
@ -860,32 +915,42 @@ public class HikVisionServiceImpl implements HikVisionService { |
|
|
|
infraredInfo = readDataHw(imageBytes); |
|
|
|
//绘制框选标识 |
|
|
|
imageAnnotation(infraPictureInfo, infraredInfo); |
|
|
|
|
|
|
|
} catch (IOException e) { |
|
|
|
log.error("error" + e); |
|
|
|
} |
|
|
|
|
|
|
|
} else if (AlgConstants.INFRA_CAMERA_REVERSE.equals(imageType)) { |
|
|
|
//相机反算红外 |
|
|
|
// 相机反算红外:优先DL/T664,其次反算 |
|
|
|
InputStream inputStreamPath = downloadFtp(imagePath); |
|
|
|
if (StringUtils.isNull(inputStreamPath)) { |
|
|
|
log.error("inputStreamPath图片为空"); |
|
|
|
} |
|
|
|
|
|
|
|
//调取实时测温温度 |
|
|
|
//传值,相机 ip |
|
|
|
TemperatureData temperatureData = TemperatureMeasurement(basedataPatrolPoint); |
|
|
|
|
|
|
|
if (temperatureData != null) { |
|
|
|
String minTemperature = temperatureData.getMinTemperature(); |
|
|
|
String maxTemperature = temperatureData.getMaxTemperature(); |
|
|
|
log.info("[INFRARED] 最高温度值:{}", maxTemperature); |
|
|
|
log.info("[INFRARED] 最低温度值:{}", minTemperature); |
|
|
|
float[][] matrix = tempCount.countTemp(inputStreamPath, Double.valueOf(maxTemperature), Double.valueOf(minTemperature));//图,最高值,最低值 |
|
|
|
infraredInfo.setTemperatureMatrix(matrix); |
|
|
|
try { |
|
|
|
byte[] imageBytes = IOUtils.toByteArray(inputStreamPath); |
|
|
|
inputStream.close(); |
|
|
|
// 1.判断是否为DL/T664 |
|
|
|
byte[] infraredData = locateAndReadInfraredData(imageBytes); |
|
|
|
if (infraredData == null) { |
|
|
|
InputStream reusableStream = new ByteArrayInputStream(imageBytes); |
|
|
|
//调取实时测温温度 |
|
|
|
//传值,相机 ip |
|
|
|
TemperatureData temperatureData = TemperatureMeasurement(basedataPatrolPoint); |
|
|
|
|
|
|
|
if (temperatureData != null) { |
|
|
|
String minTemperature = temperatureData.getMinTemperature(); |
|
|
|
String maxTemperature = temperatureData.getMaxTemperature(); |
|
|
|
log.info("[INFRARED] 最高温度值:{}", maxTemperature); |
|
|
|
log.info("[INFRARED] 最低温度值:{}", minTemperature); |
|
|
|
float[][] matrix = tempCount.countTemp(reusableStream, Double.valueOf(maxTemperature), Double.valueOf(minTemperature));//图,最高值,最低值 |
|
|
|
infraredInfo.setTemperatureMatrix(matrix); |
|
|
|
} |
|
|
|
} else { |
|
|
|
infraredInfo = readDataHw(imageBytes); |
|
|
|
} |
|
|
|
imageAnnotation(infraPictureInfo, infraredInfo); |
|
|
|
} catch (Exception e) { |
|
|
|
log.error("error" + e); |
|
|
|
} |
|
|
|
|
|
|
|
imageAnnotation(infraPictureInfo, infraredInfo); |
|
|
|
} else if (AlgConstants.INFRA_1800.equals(imageType)) { |
|
|
|
//ivs1800红外图 |
|
|
|
imageAnnotation(infraPictureInfo, infraredInfo); |
|
|
|
@ -1034,43 +1099,6 @@ public class HikVisionServiceImpl implements HikVisionService { |
|
|
|
return inputStream; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public static String truncateFileNameToCsv(String originalPath) { |
|
|
|
// 获取文件名部分(最后一个 '/' 之后的内容) |
|
|
|
int lastSlashIndex = originalPath.lastIndexOf('/'); |
|
|
|
if (lastSlashIndex == -1) { |
|
|
|
// 如果没有路径分隔符,直接处理整个字符串 |
|
|
|
return processFileNameToCsv(originalPath); |
|
|
|
} |
|
|
|
|
|
|
|
// 分离路径和文件名 |
|
|
|
String path = originalPath.substring(0, lastSlashIndex + 1); |
|
|
|
String fileName = originalPath.substring(lastSlashIndex + 1); |
|
|
|
|
|
|
|
// 处理文件名部分并强制改为.csv |
|
|
|
String processedFileName = processFileNameToCsv(fileName); |
|
|
|
|
|
|
|
|
|
|
|
return path + processedFileName; |
|
|
|
} |
|
|
|
|
|
|
|
private static String processFileNameToCsv(String fileName) { |
|
|
|
// 去掉文件扩展名(如果有) |
|
|
|
int lastDotIndex = fileName.lastIndexOf('.'); |
|
|
|
if (lastDotIndex != -1) { |
|
|
|
fileName = fileName.substring(0, lastDotIndex); |
|
|
|
} |
|
|
|
|
|
|
|
// 去掉最后一个 '_' 及其后面的部分 |
|
|
|
int lastUnderscoreIndex = fileName.lastIndexOf('_'); |
|
|
|
if (lastUnderscoreIndex != -1) { |
|
|
|
fileName = fileName.substring(0, lastUnderscoreIndex); |
|
|
|
} |
|
|
|
|
|
|
|
// 强制添加.csv后缀 |
|
|
|
return fileName + ".csv"; |
|
|
|
} |
|
|
|
|
|
|
|
//解析获取csv文件温度--返回流文件 |
|
|
|
private InputStream getFtpFileAsStream(String csvUrl) throws IOException { |
|
|
|
|
|
|
|
|