@@ -22,7 +22,7 @@ public class GeneratorCodeConfig { | |||
private static final String URL = "jdbc:mysql://47.98.125.47:3306/nd-yw-road?serverTimezone=Asia/Shanghai&characterEncoding=utf8&allowPublicKeyRetrieval=true&useSSL=false"; | |||
private static final String USER_NAME = "root"; | |||
private static final String PASSWORD = "NingdaKeji123!"; | |||
private static final String PASSWORD = "Ndkj@1104"; | |||
private static void generate(String author, String packageName, String path, String... tableNames) { | |||
FastAutoGenerator.create(URL, USER_NAME, PASSWORD) | |||
@@ -59,7 +59,7 @@ public class GeneratorCodeConfig { | |||
public static void main(String[] args) { | |||
//generate("PoffyZhang", "car.monitor",PATH_ZPF, "nd_vehicle_security_monitor"); | |||
//generate("WendyYang", "irs",PATH_YYD, "nd_drivers_license"); | |||
generate("CMM", "test",PATH_CMM, "nd_road_behavior_analysis"); | |||
generate("CMM", "test",PATH_CMM, "nd_radar_original_data"); | |||
} | |||
} |
@@ -88,7 +88,14 @@ public class GpsFullDataPullTask { | |||
log.error("下载路径请求失败!"); | |||
return; | |||
} | |||
JSONObject object = JSONObject.parseObject(response); | |||
log.info("Response from server: {}", response); | |||
JSONObject object = null; | |||
try { | |||
object = JSONObject.parseObject(response); | |||
} catch (Exception e) { | |||
log.error("数据拉取失败! {}",e.getMessage()); | |||
return; | |||
} | |||
log.info("Response from server: {}", object); | |||
String downloadUrl = object.getString("data"); | |||
if (StringUtils.isBlank(downloadUrl)){ | |||
@@ -99,8 +99,15 @@ public class GpsRealTimeDataPullTask { | |||
log.error("下载路径请求失败!"); | |||
return; | |||
} | |||
JSONObject object = JSONObject.parseObject(response); | |||
System.out.println("Response from server: " + response); | |||
log.info("Response from server: {}", response); | |||
JSONObject object = null; | |||
try { | |||
object = JSONObject.parseObject(response); | |||
} catch (Exception e) { | |||
log.error("数据拉取失败! {}",e.getMessage()); | |||
return; | |||
} | |||
log.info("Response from server: {}", object); | |||
String downloadUrl = object.getString("data"); | |||
if (downloadUrl == null){ | |||
log.error("下载路径请求失败!"); | |||
@@ -3,13 +3,24 @@ package com.ningdatech.carapi.radar.client; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.DataInputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.net.Socket; | |||
import java.nio.charset.StandardCharsets; | |||
import java.time.LocalDateTime; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.Objects; | |||
import java.util.concurrent.Executors; | |||
import java.util.concurrent.ScheduledExecutorService; | |||
import java.util.concurrent.TimeUnit; | |||
import cn.hutool.socket.SocketUtil; | |||
import com.google.common.collect.Lists; | |||
import com.ningdatech.carapi.radar.helper.RadarDataHelper; | |||
import com.ningdatech.carapi.radar.model.entity.RadarData; | |||
import com.ningdatech.carapi.radar.service.IRadarDataService; | |||
import lombok.RequiredArgsConstructor; | |||
import lombok.extern.slf4j.Slf4j; | |||
/** | |||
@@ -22,50 +33,63 @@ public class RadarHandler implements Runnable{ | |||
private final Socket clientSocket; | |||
private final IRadarDataService radarDataService; | |||
private final String radarIp; | |||
private final RadarDataHelper radarDataHelper; | |||
public RadarHandler(Socket socket, IRadarDataService radarDataService, String radarIp) { | |||
public RadarHandler(Socket socket, IRadarDataService radarDataService, String radarIp, RadarDataHelper radarDataHelper) { | |||
this.clientSocket = socket; | |||
this.radarDataService = radarDataService; | |||
this.radarIp = radarIp; | |||
this.radarDataHelper = radarDataHelper; | |||
} | |||
@Override | |||
public void run() { | |||
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); | |||
// 获取输入流以读取雷达发送的数据 | |||
try { | |||
DataInputStream dataInputStream = new DataInputStream(clientSocket.getInputStream()); | |||
// 从接入数据 开始 持续接收1分钟的数据 接收完成后 关闭连接 | |||
// 设置1分钟后关闭连接 | |||
executor.schedule(() -> { | |||
try { | |||
clientSocket.close(); | |||
} catch (IOException e) { | |||
log.error("Error closing client socket: ", e); | |||
} | |||
}, 1, TimeUnit.MINUTES); | |||
InputStream dataInputStream = clientSocket.getInputStream(); | |||
// 开启保活选项 | |||
clientSocket.setKeepAlive(true); | |||
byte[] allData = readAllBytesFromDataInputStream(dataInputStream); | |||
// 处理从雷达接收到的数据 | |||
if (allData.length > 0) { | |||
String dataString = new String(allData, StandardCharsets.UTF_8); | |||
byte[] buffer = new byte[1024]; | |||
int bytesRead; | |||
// 循环读取数据,直到Socket被关闭 | |||
while (!clientSocket.isClosed() && (bytesRead = dataInputStream.read(buffer)) != -1) { | |||
// 处理读取到的数据 | |||
String dataString = new String(buffer, 0, bytesRead, StandardCharsets.UTF_8); | |||
log.info("Received: {}", dataString); | |||
RadarData radarData = new RadarData(); | |||
radarData.setRadarIp(radarIp); | |||
radarData.setData(dataString); | |||
radarData.setArmIp(radarIp); | |||
// 解析数据 | |||
radarData.setCreateOn(LocalDateTime.now()); | |||
radarData.setUpdateOn(LocalDateTime.now()); | |||
radarDataService.save(radarData); | |||
} | |||
// 关闭当前周期的连接 | |||
clientSocket.close(); | |||
} catch (IOException e) { | |||
throw new RuntimeException(e); | |||
}finally { | |||
// 关闭ScheduledExecutorService | |||
executor.shutdown(); | |||
try { | |||
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) { | |||
executor.shutdownNow(); | |||
} | |||
} catch (InterruptedException e) { | |||
executor.shutdownNow(); | |||
} | |||
} | |||
} | |||
private byte[] readAllBytesFromDataInputStream(DataInputStream dis) throws IOException { | |||
// 使用 ByteArrayOutputStream 来收集所有读取的字节 | |||
try (ByteArrayOutputStream buffer = new ByteArrayOutputStream()) { | |||
// 使用一个合适的缓冲区大小 | |||
byte[] data = new byte[4096]; | |||
int nRead; | |||
while ((nRead = dis.read(data, 0, data.length)) != -1) { | |||
buffer.write(data, 0, nRead); | |||
} | |||
buffer.flush(); | |||
return buffer.toByteArray(); | |||
} | |||
private void processData(String dataString, RadarData radarData) { | |||
// 校验包头、包尾 | |||
} | |||
} |
@@ -0,0 +1,68 @@ | |||
package com.ningdatech.carapi.radar.constant; | |||
import java.util.Objects; | |||
import org.apache.commons.lang3.StringUtils; | |||
import io.swagger.annotations.ApiModel; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Getter; | |||
import lombok.NoArgsConstructor; | |||
/** | |||
* @return | |||
* @author CMM | |||
* @since 2022/12/20 14:10 | |||
*/ | |||
@Getter | |||
@AllArgsConstructor | |||
@NoArgsConstructor | |||
@ApiModel(value = "RadarCarTypeEnum", description = "雷达车辆类型-枚举") | |||
public enum RadarCarTypeEnum { | |||
/** | |||
* 自行车情况 | |||
*/ | |||
NON_MOTOR(1, "非机动车"), | |||
CAR(2, "小型车辆"), | |||
BUS(3, "中大型车辆"), | |||
UNKNOWN(4, "未知"); | |||
private Integer code; | |||
private String desc; | |||
public String getDesc() { | |||
return desc; | |||
} | |||
public void setDesc(String desc) { | |||
this.desc = desc; | |||
} | |||
public static String getDescByCode(Integer code) { | |||
if(Objects.isNull(code)){ | |||
return StringUtils.EMPTY; | |||
} | |||
for (RadarCarTypeEnum t : RadarCarTypeEnum.values()) { | |||
if (code.equals(t.getCode())) { | |||
return t.desc; | |||
} | |||
} | |||
return StringUtils.EMPTY; | |||
} | |||
public static Integer getCodeByDesc(String desc) { | |||
if(StringUtils.isBlank(desc)){ | |||
return null; | |||
} | |||
for (RadarCarTypeEnum t : RadarCarTypeEnum.values()) { | |||
if (desc.equals(t.getDesc())) { | |||
return t.code; | |||
} | |||
} | |||
return null; | |||
} | |||
public boolean eq(String val) { | |||
return this.name().equals(val); | |||
} | |||
} |
@@ -0,0 +1,66 @@ | |||
package com.ningdatech.carapi.radar.constant; | |||
import java.util.Objects; | |||
import org.apache.commons.lang3.StringUtils; | |||
import io.swagger.annotations.ApiModel; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Getter; | |||
import lombok.NoArgsConstructor; | |||
/** | |||
* @return | |||
* @author CMM | |||
* @since 2022/12/20 14:10 | |||
*/ | |||
@Getter | |||
@AllArgsConstructor | |||
@NoArgsConstructor | |||
@ApiModel(value = "RadarDataTypeEnum", description = "雷达数据类型-枚举") | |||
public enum RadarDataTypeEnum { | |||
/** | |||
* 自行车情况 | |||
*/ | |||
OBJECT_DATA(1, "目标数据"), | |||
TRAFFIC_DATA(2, "流量数据"); | |||
private Integer code; | |||
private String desc; | |||
public String getDesc() { | |||
return desc; | |||
} | |||
public void setDesc(String desc) { | |||
this.desc = desc; | |||
} | |||
public static String getDescByCode(Integer code) { | |||
if(Objects.isNull(code)){ | |||
return StringUtils.EMPTY; | |||
} | |||
for (RadarDataTypeEnum t : RadarDataTypeEnum.values()) { | |||
if (code.equals(t.getCode())) { | |||
return t.desc; | |||
} | |||
} | |||
return StringUtils.EMPTY; | |||
} | |||
public static Integer getCodeByDesc(String desc) { | |||
if(StringUtils.isBlank(desc)){ | |||
return null; | |||
} | |||
for (RadarDataTypeEnum t : RadarDataTypeEnum.values()) { | |||
if (desc.equals(t.getDesc())) { | |||
return t.code; | |||
} | |||
} | |||
return null; | |||
} | |||
public boolean eq(String val) { | |||
return this.name().equals(val); | |||
} | |||
} |
@@ -1,17 +1,20 @@ | |||
package com.ningdatech.carapi.radar.controller; | |||
import com.ningdatech.carapi.radar.manage.RadarManage; | |||
import com.ningdatech.carapi.radar.model.vo.RadarObjectDataVO; | |||
import com.ningdatech.carapi.radar.model.vo.RadarTrafficFlowDataVO; | |||
import com.ningdatech.carapi.road.model.req.RoadMonitorReq; | |||
import org.springframework.validation.annotation.Validated; | |||
import org.springframework.web.bind.annotation.GetMapping; | |||
import org.springframework.web.bind.annotation.RequestMapping; | |||
import org.springframework.web.bind.annotation.RestController; | |||
import com.ningdatech.carapi.common.util.OssUtils; | |||
import io.swagger.annotations.ApiOperation; | |||
import lombok.RequiredArgsConstructor; | |||
import lombok.extern.slf4j.Slf4j; | |||
import java.util.List; | |||
/** | |||
* <p> | |||
* 前端控制器 | |||
@@ -29,9 +32,33 @@ public class RadarController { | |||
private final RadarManage radarManage; | |||
@ApiOperation(value = "雷达监听连接测试", notes = "雷达监听连接测试") | |||
@GetMapping("/get-radar-data") | |||
public String getRadarData() { | |||
return radarManage.getRadarData(); | |||
@ApiOperation(value = "客户端-雷达监听连接测试", notes = "客户端-雷达监听连接测试") | |||
@GetMapping("/get-radar-data-as-client") | |||
public String getRadarDataAsClient() { | |||
return radarManage.getRadarDataAsClient(); | |||
} | |||
@ApiOperation(value = "服务端-雷达监听连接测试", notes = "服务端-雷达监听连接测试") | |||
@GetMapping("/get-radar-data-as-server") | |||
public String getRadarDataAsServer() { | |||
return radarManage.getRadarDataAsServer(); | |||
} | |||
@ApiOperation(value = "工作台雷达交通流量数据", notes = "工作台雷达交通流量数据") | |||
@GetMapping("/workbench/radar-traffic-flow-data") | |||
public RadarTrafficFlowDataVO workbenchRadarTrafficFlowData(RoadMonitorReq req) { | |||
return radarManage.workbenchRadarTrafficFlowData(req); | |||
} | |||
@ApiOperation(value = "道路监控驾驶舱雷达交通流量数据", notes = "道路监控驾驶舱雷达交通流量数据") | |||
@GetMapping("/radar-traffic-flow-data") | |||
public RadarTrafficFlowDataVO radarTrafficFlowData(RoadMonitorReq req) { | |||
return radarManage.radarTrafficFlowData(req); | |||
} | |||
@ApiOperation(value = "道路监控驾驶舱雷达目标轨迹数据", notes = "道路监控驾驶舱雷达目标轨迹数据") | |||
@GetMapping("/radar-object-data") | |||
public List<RadarObjectDataVO> radarObjectData(RoadMonitorReq req) { | |||
return radarManage.radarObjectData(req); | |||
} | |||
} |
@@ -0,0 +1,482 @@ | |||
package com.ningdatech.carapi.radar.helper; | |||
import com.google.common.collect.Lists; | |||
import com.ningdatech.carapi.radar.model.vo.RadarDataVO; | |||
import lombok.RequiredArgsConstructor; | |||
import org.springframework.stereotype.Component; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
/** | |||
* @author CMM | |||
* @since 2024/11/08 10:15 | |||
*/ | |||
@Component | |||
@RequiredArgsConstructor | |||
public class RadarDataHelper { | |||
// 将16进制字符串转换为字节数组 | |||
public byte[] hexStringToByteArray(String s) { | |||
// 确保字符串长度为偶数 | |||
if (s.length() % 2 != 0) { | |||
// 如果长度为奇数,移除最后一个字符 | |||
s = s.substring(0, s.length() - 1); | |||
} | |||
int len = s.length(); | |||
byte[] data = new byte[len / 2]; | |||
for (int i = 0; i < len; i += 2) { | |||
// 如果输入的字符不是16进制字符,跳过 | |||
if (!Character.isDigit(s.charAt(i)) && !Character.isLetter(s.charAt(i))) { | |||
continue; | |||
} | |||
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) | |||
+ Character.digit(s.charAt(i+1), 16)); | |||
} | |||
return data; | |||
} | |||
// 以16进制字节数组解析数据帧并提取指定位置指定字节数的数据 | |||
public byte[] extractHexData(String hexFrame, int dataOffset, int dataLength) { | |||
byte[] frameBytes = hexStringToByteArray(hexFrame); | |||
// 检查数据长度是否超出帧的范围 | |||
if (dataOffset + dataLength > frameBytes.length) { | |||
throw new IllegalArgumentException("Data length exceeds frame boundary"); | |||
} | |||
// 提取数据 | |||
byte[] extractedData = new byte[dataLength]; | |||
System.arraycopy(frameBytes, dataOffset, extractedData, 0, dataLength); | |||
return extractedData; | |||
} | |||
// 在字节数组中查找特定模式 | |||
public int findPattern(byte[] array, byte[] pattern) { | |||
for (int i = 0; i < array.length - pattern.length + 1; i++) { | |||
boolean found = true; | |||
for (int j = 0; j < pattern.length; j++) { | |||
if (array[i + j] != pattern[j]) { | |||
found = false; | |||
break; | |||
} | |||
} | |||
if (found) { | |||
return i; | |||
} | |||
} | |||
return -1; | |||
} | |||
// 将字节数组转换为16进制字符串 | |||
public String byteArrayToHexString(byte[] bytes) { | |||
StringBuilder sb = new StringBuilder(); | |||
for (byte b : bytes) { | |||
// 过滤掉EF00 | |||
if (b == (byte) 0xEF || b == (byte) 0x00){ | |||
continue; | |||
} | |||
sb.append(String.format("%02X", b)); | |||
} | |||
return sb.toString(); | |||
} | |||
// 将字节数组转换为16进制字符串 | |||
public String byteArrayToHexStringAll(byte[] bytes) { | |||
StringBuilder sb = new StringBuilder(); | |||
for (byte b : bytes) { | |||
sb.append(String.format("%02X", b)); | |||
} | |||
return sb.toString(); | |||
} | |||
public void processRadarData(String data) { | |||
String head = "55aa55bb"; | |||
String tail = "55cc55dd"; | |||
// 解析上面的数据帧data 包头是head 包尾是tail 从包头到包尾分段取出数据 放入一个字符串列表中 等待解析 | |||
List<String> segments = Lists.newArrayList(); | |||
int headIndex = data.indexOf(head); | |||
while (headIndex != -1) { | |||
int tailIndex = data.indexOf(tail, headIndex + head.length()); | |||
if (tailIndex != -1) { | |||
// 提取从包头到包尾的数据段 | |||
String segment = data.substring(headIndex + head.length(), tailIndex); | |||
segments.add(segment); | |||
// 移动到下一个包头的位置 | |||
headIndex = data.indexOf(head, tailIndex + tail.length()); | |||
} else { | |||
// 没有找到包尾,结束循环 | |||
break; | |||
} | |||
} | |||
// 遍历这些数据帧 解析其中携带的数据 | |||
for (String segment : segments) { | |||
System.out.println(segment); | |||
//// 获取数据长度 2字节 4字符 判断数据包长度 | |||
byte[] dataLengthData = extractHexData(segment, 0, 2); | |||
String dataLengthHexString = byteArrayToHexStringAll(dataLengthData); | |||
// 将16进制字符串转换为字节数组 | |||
byte[] lengthBytes = new byte[2]; | |||
// 高8位 | |||
lengthBytes[0] = (byte) Integer.parseInt(dataLengthHexString.substring(0, 2), 16); | |||
// 低8位 | |||
lengthBytes[1] = (byte) Integer.parseInt(dataLengthHexString.substring(2, 4), 16); | |||
// 将字节数组转换为16位整数,注意网络字节序是大端序 | |||
int byteLength = ((lengthBytes[0] & 0xFF) << 8) | (lengthBytes[1] & 0xFF); | |||
// 打印结果 | |||
System.out.println("数据长度: " + byteLength); | |||
// 获取数据帧类型 1字节 2字符 判断是目标轨迹数据还是交通流量数据 | |||
byte[] objectTypeData = extractHexData(segment, 2, 1); | |||
String objectTypeHexString = byteArrayToHexString(objectTypeData); | |||
System.out.println("数据帧类型:" + objectTypeHexString); | |||
// 获取设备编号 20字节 40字符 判断是哪个雷达的发送的数据 | |||
byte[] deviceIdData = extractHexData(segment, 4, 16); | |||
StringBuilder builder = new StringBuilder(); | |||
for (int i = 0; i < deviceIdData.length; i++) { | |||
byte b = deviceIdData[i]; | |||
// 16进制数 "32" | |||
String hexString = String.format("%02X", b); | |||
// 将16进制字符串转换为十进制整数 | |||
int decimalValue = Integer.parseInt(hexString, 16); | |||
// 将十进制整数转换为对应的ASCII字符 | |||
char asciiChar = (char) decimalValue; | |||
// 如果不为0则添加到字符串中 | |||
if (decimalValue != 0) { | |||
builder.append(asciiChar); | |||
} | |||
} | |||
System.out.println("设备编号:" + builder); | |||
byte[] timestampData = extractHexData(segment, 24, 8); | |||
String timestampHexString = byteArrayToHexString(timestampData); | |||
System.out.println("时间戳:" + timestampHexString); | |||
String localDateTime = getLocalDateTime(segment, timestampData); | |||
System.out.println("时间戳:" + localDateTime); | |||
// 获取数据 | |||
// 用获取到的数据长度 减去数据长度(2字节)、数据帧类型(1字节)、校验和(1字节)、数据编号)、时间戳(8字节)、包头包尾(8字节) | |||
int dataLength = byteLength - 2 - 1 - 1 - 20 - 8; | |||
segment = segment.substring(64); | |||
System.out.println("总数据:" + segment); | |||
// 如果是目标轨迹数据 | |||
if ("01".equals(objectTypeHexString)) { | |||
if (dataLength >= 36) { | |||
byte[] dataBytes = hexStringToByteArray(segment); | |||
// 36个字节为一组数据 进行分组解析 | |||
for (int i = 0; i < dataBytes.length; i += 36) { | |||
byte[] dataSegment = Arrays.copyOfRange(dataBytes, i, i + 36); | |||
String dataSegmentHexString = byteArrayToHexStringAll(dataSegment); | |||
System.out.println("数据:" + dataSegmentHexString); | |||
// 获取目标所属车道 | |||
byte[] targetLaneData = extractHexData(dataSegmentHexString, 2, 1); | |||
String targetLaneHexString = byteArrayToHexStringAll(targetLaneData); | |||
// 将16进制数转换为10进制数 | |||
int targetLane = Integer.parseInt(targetLaneHexString, 16); | |||
System.out.println("目标所属车道:" + targetLane); | |||
// 获取目标类型 | |||
byte[] carTypeData = extractHexData(dataSegmentHexString, 3, 1); | |||
String carTypeHexString = byteArrayToHexStringAll(carTypeData); | |||
System.out.println("目标类型:" + carTypeHexString); | |||
// 获取目标速度 | |||
byte[] targetSpeedData = extractHexData(dataSegmentHexString, 20, 2); | |||
String targetSpeedHexString = byteArrayToHexStringAll(targetSpeedData); | |||
double targetSpeed = Integer.parseInt(targetSpeedHexString, 16) * 0.01; | |||
System.out.println("目标速度:" + targetSpeed); | |||
// 获取目标经度 | |||
byte[] targetLongitudeData = extractHexData(dataSegmentHexString, 28, 4); | |||
String targetLongitudeHexString = byteArrayToHexStringAll(targetLongitudeData); | |||
// 将十六进制字符串转换为long类型的数字 | |||
long targetLongitudeLongValue = Long.parseLong(targetLongitudeHexString, 16); | |||
// 将long类型的数字转换为double类型的数字 | |||
double targetLongitude = (double) targetLongitudeLongValue / 10000000; | |||
System.out.println("目标经度:" + targetLongitude); | |||
// 获取目标纬度 | |||
byte[] targetLatitudeData = extractHexData(dataSegmentHexString, 32, 4); | |||
String targetLatitudeHexString = byteArrayToHexStringAll(targetLatitudeData); | |||
// 将十六进制字符串转换为long类型的数字 | |||
long targetLatitudeLongValue = Long.parseLong(targetLatitudeHexString, 16); | |||
// 将long类型的数字转换为double类型的数字 | |||
double targetLatitude = (double) targetLatitudeLongValue / 10000000; | |||
System.out.println("目标纬度:" + targetLatitude); | |||
} | |||
} | |||
} | |||
// 如果是交通流量数据 | |||
else if ("03".equals(objectTypeHexString)) { | |||
if (dataLength >= 22) { | |||
byte[] dataBytes = hexStringToByteArray(segment); | |||
// 22个字节为一组数据 进行分组解析 | |||
for (int i = 0; i < dataBytes.length; i += 22) { | |||
byte[] dataSegment = Arrays.copyOfRange(dataBytes, i, i + 22); | |||
String dataSegmentHexString = byteArrayToHexStringAll(dataSegment); | |||
System.out.println("数据:" + dataSegmentHexString); | |||
// 获取统计周期 | |||
byte[] statisticsCycleData = extractHexData(dataSegmentHexString, 0, 2); | |||
String statisticsCycleHexString = byteArrayToHexStringAll(statisticsCycleData); | |||
// 将16进制数转换为10进制数 | |||
int statisticsCycle = Integer.parseInt(statisticsCycleHexString, 16); | |||
System.out.println("统计周期:" + statisticsCycle); | |||
// 获取实时车流量 | |||
byte[] realTimeTrafficFlowData = extractHexData(dataSegmentHexString, 12, 2); | |||
String realTimeTrafficFlowHexString = byteArrayToHexStringAll(realTimeTrafficFlowData); | |||
// 将16进制数转换为10进制数 | |||
int realTimeTrafficFlow = Integer.parseInt(realTimeTrafficFlowHexString, 16); | |||
System.out.println("总流量:" + realTimeTrafficFlow); | |||
// 获取平均车速 | |||
byte[] averageSpeedData = extractHexData(dataSegmentHexString, 14, 2); | |||
String averageSpeedHexString = byteArrayToHexStringAll(averageSpeedData); | |||
double averageSpeed = Integer.parseInt(averageSpeedHexString, 16) * 0.1; | |||
System.out.println("平均速度:" + averageSpeed); | |||
// 获取最大排队长度 | |||
byte[] maxQueueLengthData = extractHexData(dataSegmentHexString, 20, 2); | |||
String maxQueueLengthHexString = byteArrayToHexStringAll(maxQueueLengthData); | |||
// 将16进制数转换为10进制数 | |||
int maxQueueLength = Integer.parseInt(maxQueueLengthHexString, 16); | |||
System.out.println("最大排队长度:" + maxQueueLength); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
public List<RadarDataVO> getRadarDataList(String data) { | |||
List<RadarDataVO> vos = Lists.newArrayList(); | |||
String head = "55aa55bb"; | |||
String tail = "55cc55dd"; | |||
// 解析上面的数据帧data 包头是head 包尾是tail 从包头到包尾分段取出数据 放入一个字符串列表中 等待解析 | |||
List<String> segments = Lists.newArrayList(); | |||
int headIndex = data.indexOf(head); | |||
while (headIndex != -1) { | |||
int tailIndex = data.indexOf(tail, headIndex + head.length()); | |||
if (tailIndex != -1) { | |||
// 提取从包头到包尾的数据段 | |||
String segment = data.substring(headIndex + head.length(), tailIndex); | |||
segments.add(segment); | |||
// 移动到下一个包头的位置 | |||
headIndex = data.indexOf(head, tailIndex + tail.length()); | |||
} else { | |||
// 没有找到包尾,结束循环 | |||
break; | |||
} | |||
} | |||
// 遍历这些数据帧 解析其中携带的数据 | |||
for (String segment : segments) { | |||
RadarDataVO vo = new RadarDataVO(); | |||
//// 获取数据长度 2字节 4字符 判断数据包长度 | |||
byte[] dataLengthData = extractHexData(segment, 0, 2); | |||
String dataLengthHexString = byteArrayToHexStringAll(dataLengthData); | |||
// 将16进制字符串转换为字节数组 | |||
byte[] lengthBytes = new byte[2]; | |||
// 高8位 | |||
lengthBytes[0] = (byte) Integer.parseInt(dataLengthHexString.substring(0, 2), 16); | |||
// 低8位 | |||
lengthBytes[1] = (byte) Integer.parseInt(dataLengthHexString.substring(2, 4), 16); | |||
// 将字节数组转换为16位整数,注意网络字节序是大端序 | |||
int byteLength = ((lengthBytes[0] & 0xFF) << 8) | (lengthBytes[1] & 0xFF); | |||
// 获取数据帧类型 1字节 2字符 判断是目标轨迹数据还是交通流量数据 | |||
byte[] objectTypeData = extractHexData(segment, 2, 1); | |||
String objectTypeHexString = byteArrayToHexString(objectTypeData); | |||
// 获取设备编号 20字节 40字符 判断是哪个雷达的发送的数据 | |||
byte[] deviceIdData = extractHexData(segment, 4, 16); | |||
StringBuilder builder = new StringBuilder(); | |||
for (int i = 0; i < deviceIdData.length; i++) { | |||
byte b = deviceIdData[i]; | |||
// 16进制数 "32" | |||
String hexString = String.format("%02X", b); | |||
// 将16进制字符串转换为十进制整数 | |||
int decimalValue = Integer.parseInt(hexString, 16); | |||
// 将十进制整数转换为对应的ASCII字符 | |||
char asciiChar = (char) decimalValue; | |||
// 如果不为0则添加到字符串中 | |||
if (decimalValue != 0) { | |||
builder.append(asciiChar); | |||
} | |||
} | |||
vo.setDeviceId(builder.toString()); | |||
byte[] timestampData = extractHexData(segment, 24, 8); | |||
String timestampHexString = byteArrayToHexString(timestampData); | |||
System.out.println("时间戳:" + timestampHexString); | |||
String localDateTime = getLocalDateTime(segment, timestampData); | |||
vo.setTimeStamp(localDateTime); | |||
// 获取数据 | |||
// 用获取到的数据长度 减去数据长度(2字节)、数据帧类型(1字节)、校验和(1字节)、数据编号)、时间戳(8字节)、包头包尾(8字节) | |||
int dataLength = byteLength - 2 - 1 - 1 - 20 - 8; | |||
segment = segment.substring(64); | |||
// 如果是目标轨迹数据 | |||
if ("01".equals(objectTypeHexString)) { | |||
vo.setDataType("1"); | |||
if (dataLength >= 36) { | |||
byte[] dataBytes = hexStringToByteArray(segment); | |||
// 36个字节为一组数据 进行分组解析 | |||
for (int i = 0; i < dataBytes.length; i += 36) { | |||
byte[] dataSegment = Arrays.copyOfRange(dataBytes, i, i + 36); | |||
String dataSegmentHexString = byteArrayToHexStringAll(dataSegment); | |||
// 获取目标所属车道 | |||
byte[] targetLaneData = extractHexData(dataSegmentHexString, 2, 1); | |||
String targetLaneHexString = byteArrayToHexStringAll(targetLaneData); | |||
// 将16进制数转换为10进制数 | |||
int targetLane = Integer.parseInt(targetLaneHexString, 16); | |||
vo.setTargetLane(String.valueOf(targetLane)); | |||
// 获取目标类型 | |||
byte[] carTypeData = extractHexData(dataSegmentHexString, 3, 1); | |||
String carTypeHexString = byteArrayToHexStringAll(carTypeData); | |||
vo.setCarType(carTypeHexString); | |||
// 获取目标速度 | |||
byte[] targetSpeedData = extractHexData(dataSegmentHexString, 20, 2); | |||
String targetSpeedHexString = byteArrayToHexStringAll(targetSpeedData); | |||
double targetSpeed = Integer.parseInt(targetSpeedHexString, 16) * 0.01; | |||
vo.setTargetSpeed(String.valueOf(targetSpeed)); | |||
// 获取目标经度 | |||
byte[] targetLongitudeData = extractHexData(dataSegmentHexString, 28, 4); | |||
String targetLongitudeHexString = byteArrayToHexStringAll(targetLongitudeData); | |||
// 将十六进制字符串转换为long类型的数字 | |||
long targetLongitudeLongValue = Long.parseLong(targetLongitudeHexString, 16); | |||
// 将long类型的数字转换为double类型的数字 | |||
double targetLongitude = (double) targetLongitudeLongValue / 10000000; | |||
vo.setTargetLongitude(String.valueOf(targetLongitude)); | |||
// 获取目标纬度 | |||
byte[] targetLatitudeData = extractHexData(dataSegmentHexString, 32, 4); | |||
String targetLatitudeHexString = byteArrayToHexStringAll(targetLatitudeData); | |||
// 将十六进制字符串转换为long类型的数字 | |||
long targetLatitudeLongValue = Long.parseLong(targetLatitudeHexString, 16); | |||
// 将long类型的数字转换为double类型的数字 | |||
double targetLatitude = (double) targetLatitudeLongValue / 10000000; | |||
vo.setTargetLatitude(String.valueOf(targetLatitude)); | |||
vos.add(vo); | |||
} | |||
} | |||
} | |||
// 如果是交通流量数据 | |||
else if ("03".equals(objectTypeHexString)) { | |||
vo.setDataType("3"); | |||
if (dataLength >= 22) { | |||
byte[] dataBytes = hexStringToByteArray(segment); | |||
// 22个字节为一组数据 进行分组解析 | |||
for (int i = 0; i < dataBytes.length; i += 22) { | |||
byte[] dataSegment = Arrays.copyOfRange(dataBytes, i, i + 22); | |||
String dataSegmentHexString = byteArrayToHexStringAll(dataSegment); | |||
// 获取实时车流量 | |||
byte[] realTimeTrafficFlowData = extractHexData(dataSegmentHexString, 12, 2); | |||
String realTimeTrafficFlowHexString = byteArrayToHexStringAll(realTimeTrafficFlowData); | |||
// 将16进制数转换为10进制数 | |||
int realTimeTrafficFlow = Integer.parseInt(realTimeTrafficFlowHexString, 16); | |||
vo.setRealTimeTrafficFlow(String.valueOf(realTimeTrafficFlow)); | |||
// 获取平均车速 | |||
byte[] averageSpeedData = extractHexData(dataSegmentHexString, 14, 2); | |||
String averageSpeedHexString = byteArrayToHexStringAll(averageSpeedData); | |||
double averageSpeed = Integer.parseInt(averageSpeedHexString, 16) * 0.1; | |||
vo.setAverageCarSpeed(String.valueOf(averageSpeed)); | |||
// 获取最大排队长度 | |||
byte[] maxQueueLengthData = extractHexData(dataSegmentHexString, 20, 2); | |||
String maxQueueLengthHexString = byteArrayToHexStringAll(maxQueueLengthData); | |||
// 将16进制数转换为10进制数 | |||
int maxQueueLength = Integer.parseInt(maxQueueLengthHexString, 16); | |||
vo.setMaxQueueLength(String.valueOf(maxQueueLength)); | |||
vos.add(vo); | |||
} | |||
} | |||
} | |||
} | |||
return vos; | |||
} | |||
private String getLocalDateTime(String segment, byte[] timestampData) { | |||
// 第一个字节 -> year | |||
byte year = timestampData[0]; | |||
// 将16进制字节转换为整数 | |||
int yearInt = Integer.parseInt(String.format("%02X", year), 16); | |||
// 创建位掩码来提取第0到7位 | |||
int yearMask = 0xFF; | |||
// 使用位掩码提取第0到第7位 | |||
int extractedYearBits = yearInt & yearMask; | |||
// 数据 0~255 对应 2000~2255 year 获取实际的年份 | |||
int yearValue = extractedYearBits + 2000; | |||
System.out.println("年份:" + yearValue); | |||
// 第二个字节 -> month | |||
byte month = timestampData[1]; | |||
// 将16进制字节转换为整数 | |||
int monthInt = Integer.parseInt(String.format("%02X", month), 16); | |||
// 创建位掩码来提取第0到3位 | |||
// 1~12 对应 1~12 month | |||
int monthMask = 0x0F; | |||
// 使用位掩码提取第0到第3位 | |||
int monthValue = monthInt & monthMask; | |||
System.out.println("月份:" + monthValue); | |||
// 第三个字节 -> day | |||
byte day = timestampData[2]; | |||
// 将16进制字节转换为整数 | |||
int dayInt = Integer.parseInt(String.format("%02X", day), 16); | |||
// 创建位掩码来提取第0到4位 | |||
// 数据 1~31 对应 1~31 day | |||
int dayMask = 0x1F; | |||
// 使用位掩码提取第0到第4位 | |||
// 数据 1~31 对应 1~31 day | |||
int dayValue = dayInt & dayMask; | |||
System.out.println("天:" + dayValue); | |||
// 第四个字节 -> hour | |||
byte hour = timestampData[3]; | |||
// 将16进制字节转换为整数 | |||
int hourInt = Integer.parseInt(String.format("%02X", hour), 16); | |||
// 创建位掩码来提取第0到4位 | |||
int hourMask = 0x1F; | |||
// 使用位掩码提取第0到第4位 | |||
// 数据 0~31 对应 0~23 hour | |||
int hourValue = hourInt & hourMask; | |||
System.out.println("小时:" + hourValue); | |||
// 第五个字节 -> minute | |||
byte minute = timestampData[4]; | |||
// 将16进制字节转换为整数 | |||
int minuteInt = Integer.parseInt(String.format("%02X", minute), 16); | |||
// 创建位掩码来提取第0到5位 | |||
int minuteMask = 0x3F; | |||
// 使用位掩码提取第0到第5位 | |||
// 数据 0~63 对应 0~59 minute | |||
int minuteValue = minuteInt & minuteMask; | |||
System.out.println("分钟:" + minuteValue); | |||
// 第六个字节 -> second | |||
byte second = timestampData[5]; | |||
// 将16进制字节转换为整数 | |||
int secondInt = Integer.parseInt(String.format("%02X", second), 16); | |||
// 创建位掩码来提取第0到5位 | |||
int secondMask = 0x3F; | |||
// 使用位掩码提取第0到第5位 | |||
// 数据 0~63 对应 0~59 second | |||
int secondValue = secondInt & secondMask; | |||
System.out.println("秒:" + secondValue); | |||
// 第7、8个字节-> millisecond | |||
// 把第7和第8个字节拼接成16进制字符串 再转换为整数 | |||
byte[] millisecondData = extractHexData(segment, 30, 2); | |||
String millisecondHexString = byteArrayToHexString(millisecondData); | |||
int millisecondValue = Integer.parseInt(millisecondHexString, 16); | |||
// 创建位掩码来提取第0到9位 | |||
int millisecondMask = 0x3FF; | |||
// 使用位掩码提取第0到第9位 | |||
millisecondValue = millisecondValue & millisecondMask; | |||
// 数据 0~999 对应 0~999 毫秒 | |||
System.out.println("毫秒:" + millisecondValue); | |||
// 拼接成LocalDateTime 类型的数据 | |||
String localDateTime = yearValue + "-" + monthValue + "-" + dayValue + " " + hourValue + ":" + minuteValue + ":" + secondValue; | |||
return localDateTime; | |||
} | |||
} |
@@ -1,16 +1,37 @@ | |||
package com.ningdatech.carapi.radar.manage; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.DataInputStream; | |||
import java.io.IOException; | |||
import java.net.InetAddress; | |||
import java.io.InputStream; | |||
import java.math.BigDecimal; | |||
import java.net.InetSocketAddress; | |||
import java.net.ServerSocket; | |||
import java.net.Socket; | |||
import java.nio.charset.StandardCharsets; | |||
import java.time.LocalDateTime; | |||
import java.util.*; | |||
import java.util.concurrent.Executors; | |||
import java.util.concurrent.ScheduledExecutorService; | |||
import java.util.concurrent.TimeUnit; | |||
import java.util.stream.Collectors; | |||
import org.apache.log4j.net.SocketServer; | |||
import cn.hutool.socket.SocketUtil; | |||
import com.ningdatech.carapi.radar.helper.RadarDataHelper; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.springframework.beans.BeanUtils; | |||
import org.springframework.stereotype.Component; | |||
import com.baomidou.mybatisplus.core.toolkit.Wrappers; | |||
import com.google.common.collect.Lists; | |||
import com.ningdatech.basic.exception.BizException; | |||
import com.ningdatech.carapi.radar.constant.RadarCarTypeEnum; | |||
import com.ningdatech.carapi.radar.constant.RadarDataTypeEnum; | |||
import com.ningdatech.carapi.radar.model.entity.RadarData; | |||
import com.ningdatech.carapi.radar.model.vo.RadarObjectDataVO; | |||
import com.ningdatech.carapi.radar.model.vo.RadarTrafficFlowDataVO; | |||
import com.ningdatech.carapi.radar.service.IRadarDataService; | |||
import com.ningdatech.carapi.road.model.req.RoadMonitorReq; | |||
import cn.hutool.core.collection.CollUtil; | |||
import lombok.RequiredArgsConstructor; | |||
import lombok.extern.slf4j.Slf4j; | |||
@@ -25,47 +46,219 @@ import lombok.extern.slf4j.Slf4j; | |||
@RequiredArgsConstructor | |||
public class RadarManage { | |||
public String getRadarData() { | |||
try { | |||
// 192.168.6.42 | |||
ServerSocket serverSocket = new ServerSocket(13000); | |||
// 设置为0表示无限等待,可以根据需要设置超时时间 | |||
serverSocket.setSoTimeout(6000); | |||
System.out.println("ServerSocket started on port: " + 13000); | |||
log.info("ServerSocket started on port: {}", 13000); | |||
Socket clientSocket = serverSocket.accept(); | |||
System.out.println("Connect Successfully!"); | |||
log.info("Connect Successfully!"); | |||
DataInputStream dataInputStream = new DataInputStream(clientSocket.getInputStream()); | |||
// 开启保活选项 | |||
clientSocket.setKeepAlive(true); | |||
byte[] allData; | |||
while (!clientSocket.isClosed()) { | |||
// 使用 ByteArrayOutputStream 来收集所有读取的字节 | |||
try (ByteArrayOutputStream buffer = new ByteArrayOutputStream()) { | |||
// 使用一个合适的缓冲区大小 | |||
byte[] data = new byte[4096]; | |||
int nRead; | |||
while ((nRead = dataInputStream.read(data, 0, data.length)) != -1 && buffer.size() < 4096) { | |||
buffer.write(data, 0, nRead); | |||
} | |||
buffer.flush(); | |||
allData = buffer.toByteArray(); | |||
private final IRadarDataService radarDataService; | |||
private final RadarDataHelper radarDataHelper; | |||
public String getRadarDataAsClient() { | |||
// 盒子ip地址:192.168.6.42 --> 192.168.6.40(黄山隧道义乌来向雷达)、192.168.6.43(黄山隧道义乌去向雷达) | |||
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); | |||
// 默认结果 | |||
String result = "接收失败!"; | |||
String ip = "192.168.6.42"; | |||
// String ip = "192.168.2.249"; | |||
try (Socket socket = SocketUtil.connect(ip, 13000)) { | |||
log.info("Client connected to server on port: {}", 13000); | |||
InputStream inputStream = socket.getInputStream(); | |||
if (Objects.nonNull(inputStream)) { | |||
// 读取输入流中的1024个字节 | |||
byte[] buffer = new byte[1024]; | |||
if (inputStream.available() > 0) { | |||
int len = inputStream.read(buffer); | |||
// 处理读取到的数据 | |||
byte[] data = Arrays.copyOf(buffer, len); | |||
log.info("Received: {}", Arrays.toString(data)); | |||
log.info("Connect Successfully!"); | |||
}else { | |||
return "流数据为空!"; | |||
} | |||
// 处理从雷达接收到的数据 | |||
if (allData.length > 0) { | |||
String dataString = new String(allData, StandardCharsets.UTF_8); | |||
System.out.println("Received: " + dataString); | |||
log.info("Received: {}", dataString); | |||
break; | |||
}else{ | |||
return result; | |||
} | |||
// 从接入数据 开始 持续接收1分钟的数据 接收完成后 关闭连接 | |||
// 设置1分钟后关闭连接 | |||
executor.schedule(() -> { | |||
try { | |||
socket.close(); | |||
} catch (IOException e) { | |||
log.error("Error closing client socket: ", e); | |||
} | |||
}, 1, TimeUnit.MINUTES); | |||
// 开启保活选项 | |||
socket.setKeepAlive(true); | |||
byte[] buffer = new byte[1024]; | |||
int bytesRead; | |||
// 循环读取数据,直到Socket被关闭 | |||
while (!socket.isClosed() && (bytesRead = inputStream.read(buffer)) != -1) { | |||
// 处理读取到的数据 | |||
String dataString = new String(buffer, 0, bytesRead, StandardCharsets.UTF_8); | |||
log.info("Received: {}", dataString); | |||
radarDataHelper.processRadarData(dataString); | |||
} | |||
// 关闭当前周期的连接 | |||
clientSocket.close(); | |||
// 如果循环正常结束,则修改结果 | |||
result = "接收成功!"; | |||
} catch (IOException e) { | |||
return e.getMessage(); | |||
}finally { | |||
// 关闭ScheduledExecutorService | |||
executor.shutdown(); | |||
try { | |||
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) { | |||
executor.shutdownNow(); | |||
} | |||
} catch (InterruptedException e) { | |||
executor.shutdownNow(); | |||
} | |||
} | |||
// 返回最终结果 | |||
return result; | |||
} | |||
public String getRadarDataAsServer() { | |||
// 盒子ip地址:192.168.6.42 --> 192.168.6.40(黄山隧道义乌来向雷达)、192.168.6.43(黄山隧道义乌去向雷达) | |||
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); | |||
// 默认结果 | |||
String result = "接收失败!"; | |||
String ip = "192.168.6.42"; | |||
//String ip = "192.168.2.249"; | |||
try (ServerSocket serverSocket = new ServerSocket()) { | |||
// 设置超时时间 | |||
serverSocket.setSoTimeout(60000); | |||
InetSocketAddress address = new InetSocketAddress(ip, 13000); | |||
//InetSocketAddress address = new InetSocketAddress("192.168.2.254", 13000); | |||
serverSocket.bind(address); | |||
// 等待客户端连接 | |||
System.out.println("等待客户端连接..."); | |||
Socket socket = serverSocket.accept(); | |||
System.out.println("客户端已连接!"); | |||
// 读取客户端推送的数据 | |||
InputStream inputStream = socket.getInputStream(); | |||
byte[] buffer = new byte[1024]; | |||
int len; | |||
while ((len = inputStream.read(buffer)) != -1) { | |||
// 处理接收到的数据 | |||
String data = new String(buffer, 0, len); | |||
System.out.println("接收到数据:" + data); | |||
} | |||
result = "接收成功!"; | |||
} catch (Exception e) { | |||
log.error("Error closing server socket: ", e); | |||
} | |||
// 返回最终结果 | |||
return result; | |||
} | |||
public RadarTrafficFlowDataVO radarTrafficFlowData(RoadMonitorReq req) { | |||
RadarTrafficFlowDataVO vo = new RadarTrafficFlowDataVO(); | |||
String region = req.getRegion(); | |||
// 雷达区域-ip对应关系 | |||
Map<String, String> radarRegionIpMap = getRadarRegionIpMap(); | |||
// 判断查询的是哪个设备的数据 | |||
String radarIp = radarRegionIpMap.getOrDefault(region, null); | |||
if (StringUtils.isBlank(radarIp)){ | |||
throw new BizException("查询区域数据不存在!"); | |||
} | |||
// 从雷达数据信息表中获取查询区域最新的一条流量数据 | |||
RadarData radarData = radarDataService.getOne(Wrappers.lambdaQuery(RadarData.class) | |||
.eq(RadarData::getRadarIp, radarIp) | |||
.eq(RadarData::getDataType, RadarDataTypeEnum.TRAFFIC_DATA.getCode()) | |||
.orderByDesc(RadarData::getUpdateOn) | |||
.last("limit 1")); | |||
if (Objects.nonNull(radarData)) { | |||
BeanUtils.copyProperties(radarData, vo); | |||
} | |||
return vo; | |||
} | |||
private static Map<String, String> getRadarRegionIpMap() { | |||
Map<String, String> radarRegionIpMap = new HashMap<>(); | |||
// 黄山隧道东阳去向 | |||
radarRegionIpMap.put("1", "192.168.6.45"); | |||
// 黄山隧道东阳来向 | |||
radarRegionIpMap.put("2", "192.168.6.47"); | |||
// 黄山隧道义乌去向 | |||
radarRegionIpMap.put("3", "192.168.6.43"); | |||
// 黄山隧道义乌来向 | |||
radarRegionIpMap.put("4", "192.168.6.40"); | |||
// 何里隧道兰溪方向 | |||
radarRegionIpMap.put("5", "192.168.10.63"); | |||
// 何里隧道义乌方向 | |||
radarRegionIpMap.put("6", "192.168.10.60"); | |||
return radarRegionIpMap; | |||
} | |||
public List<RadarObjectDataVO> radarObjectData(RoadMonitorReq req) { | |||
String region = req.getRegion(); | |||
// 雷达区域-ip对应关系 | |||
Map<String, String> radarRegionIpMap = getRadarRegionIpMap(); | |||
// 判断查询的是哪个设备的数据 | |||
String radarIp = radarRegionIpMap.getOrDefault(region, null); | |||
if (StringUtils.isBlank(radarIp)){ | |||
throw new BizException("查询区域数据不存在!"); | |||
} | |||
return null; | |||
// 从雷达数据信息表中获取查询区域最近1分钟的目标数据 | |||
LocalDateTime startTime = LocalDateTime.now().minusMinutes(1); | |||
List<RadarData> radarDataList = radarDataService.list(Wrappers.lambdaQuery(RadarData.class) | |||
.eq(RadarData::getRadarIp, radarIp) | |||
.eq(RadarData::getDataType, RadarDataTypeEnum.OBJECT_DATA.getCode()) | |||
.ge(RadarData::getUpdateOn, startTime) | |||
.orderByDesc(RadarData::getUpdateOn)); | |||
// 如果查询不到数据,则返回雷达数据表中最新的100条目标轨迹数据 | |||
if (CollUtil.isEmpty(radarDataList)){ | |||
radarDataList = radarDataService.list(Wrappers.lambdaQuery(RadarData.class) | |||
.eq(RadarData::getRadarIp, radarIp) | |||
.eq(RadarData::getDataType, RadarDataTypeEnum.OBJECT_DATA.getCode()) | |||
.orderByDesc(RadarData::getUpdateOn) | |||
.last("limit 100")); | |||
} | |||
return radarDataList.stream().map(d -> { | |||
RadarObjectDataVO vo = new RadarObjectDataVO(); | |||
// TODO 待接入雷达数据后 转换为中文 当前以原始编号返回 | |||
vo.setTargetLane(d.getTargetLane()); | |||
vo.setCarType(RadarCarTypeEnum.getDescByCode(d.getCarType())); | |||
vo.setTargetSpeed(d.getTargetSpeed()); | |||
vo.setTargetLatitude(d.getTargetLatitude()); | |||
vo.setTargetLongitude(d.getTargetLongitude()); | |||
vo.setUpdateOn(d.getUpdateOn()); | |||
return vo; | |||
}).collect(Collectors.toList()); | |||
} | |||
public RadarTrafficFlowDataVO workbenchRadarTrafficFlowData(RoadMonitorReq req) { | |||
RadarTrafficFlowDataVO vo = new RadarTrafficFlowDataVO(); | |||
// 雷达区域-ip对应关系 | |||
List<RadarData> radarDataList = getRadarDataList(); | |||
if (CollUtil.isEmpty(radarDataList)){ | |||
vo.setRealTimeTrafficFlow("0"); | |||
return vo; | |||
} | |||
// 计算实时流量的和 | |||
List<String> list = radarDataList.stream().map(RadarData::getRealTimeTrafficFlow).collect(Collectors.toList()); | |||
List<BigDecimal> decimals = list.stream().map(d -> BigDecimal.valueOf(Double.parseDouble(d))).collect(Collectors.toList()); | |||
BigDecimal total = decimals.stream().reduce(BigDecimal.ZERO, BigDecimal::add).stripTrailingZeros(); | |||
vo.setRealTimeTrafficFlow(String.valueOf(total)); | |||
return vo; | |||
} | |||
private List<RadarData> getRadarDataList() { | |||
Map<String, String> radarRegionIpMap = getRadarRegionIpMap(); | |||
List<String> radarIpList = new ArrayList<>(radarRegionIpMap.values()); | |||
// 从雷达数据信息表中获取每个区域最新的一条流量数据 | |||
List<RadarData> radarDataList = Lists.newArrayList(); | |||
radarIpList.forEach(radarIp -> { | |||
RadarData radarData = radarDataService.getOne(Wrappers.lambdaQuery(RadarData.class) | |||
.eq(RadarData::getRadarIp, radarIp) | |||
.eq(RadarData::getDataType, RadarDataTypeEnum.TRAFFIC_DATA.getCode()) | |||
.orderByDesc(RadarData::getUpdateOn) | |||
.last("limit 1")); | |||
if (Objects.nonNull(radarData)) { | |||
radarDataList.add(radarData); | |||
} | |||
}); | |||
return radarDataList; | |||
} | |||
} |
@@ -0,0 +1,16 @@ | |||
package com.ningdatech.carapi.radar.mapper; | |||
import com.ningdatech.carapi.radar.model.entity.RadarOriginalData; | |||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; | |||
/** | |||
* <p> | |||
* Mapper 接口 | |||
* </p> | |||
* | |||
* @author CMM | |||
* @since 2024-11-13 | |||
*/ | |||
public interface RadarOriginalDataMapper extends BaseMapper<RadarOriginalData> { | |||
} |
@@ -0,0 +1,5 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | |||
<mapper namespace="com.ningdatech.carapi.radar.mapper.RadarOriginalDataMapper"> | |||
</mapper> |
@@ -28,15 +28,46 @@ public class RadarData implements Serializable { | |||
@TableId(value = "id", type = IdType.AUTO) | |||
private Long id; | |||
@ApiModelProperty("雷达推送的数据包") | |||
private String data; | |||
@ApiModelProperty("推送数据的盒子ip") | |||
private String armIp; | |||
@ApiModelProperty("推送数据的雷达ip") | |||
private String radarIp; | |||
@ApiModelProperty("数据类型 1 目标数据、2 流量数据") | |||
private Integer dataType; | |||
@ApiModelProperty("创建时间") | |||
private LocalDateTime createOn; | |||
@ApiModelProperty("更新时间") | |||
private LocalDateTime updateOn; | |||
//---------- 目标轨迹数据 ----------// | |||
@ApiModelProperty("目标所属车道") | |||
private String targetLane; | |||
@ApiModelProperty("车辆类型") | |||
private Integer carType; | |||
@ApiModelProperty("目标速度") | |||
private String targetSpeed; | |||
@ApiModelProperty("目标经度") | |||
private String targetLongitude; | |||
@ApiModelProperty("目标纬度") | |||
private String targetLatitude; | |||
//---------- 交通流量数据 ----------// | |||
@ApiModelProperty("实时车流量") | |||
private String realTimeTrafficFlow; | |||
@ApiModelProperty("平均车速") | |||
private String averageCarSpeed; | |||
@ApiModelProperty("最大排队长度") | |||
private String maxQueueLength; | |||
} |
@@ -0,0 +1,45 @@ | |||
package com.ningdatech.carapi.radar.model.entity; | |||
import com.baomidou.mybatisplus.annotation.IdType; | |||
import com.baomidou.mybatisplus.annotation.TableId; | |||
import com.baomidou.mybatisplus.annotation.TableName; | |||
import java.io.Serializable; | |||
import java.time.LocalDateTime; | |||
import io.swagger.annotations.ApiModel; | |||
import io.swagger.annotations.ApiModelProperty; | |||
import lombok.Data; | |||
/** | |||
* <p> | |||
* | |||
* </p> | |||
* | |||
* @author CMM | |||
* @since 2024-11-13 | |||
*/ | |||
@TableName("nd_radar_original_data") | |||
@ApiModel(value = "RadarOriginalData对象", description = "") | |||
@Data | |||
public class RadarOriginalData implements Serializable { | |||
private static final long serialVersionUID = 1L; | |||
@ApiModelProperty("主键") | |||
@TableId(value = "id", type = IdType.AUTO) | |||
private Long id; | |||
@ApiModelProperty("盒子推送的雷达数据") | |||
private String data; | |||
@ApiModelProperty("盒子对应的ip") | |||
private String armIp; | |||
@ApiModelProperty("数据类型") | |||
private String dataType; | |||
@ApiModelProperty("创建时间") | |||
private LocalDateTime createOn; | |||
@ApiModelProperty("更新时间") | |||
private LocalDateTime updateOn; | |||
} |
@@ -0,0 +1,53 @@ | |||
package com.ningdatech.carapi.radar.model.vo; | |||
import java.time.LocalDateTime; | |||
import io.swagger.annotations.ApiModel; | |||
import io.swagger.annotations.ApiModelProperty; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Data; | |||
import lombok.NoArgsConstructor; | |||
/** | |||
* @author CMM | |||
* @since 2024/10/23 10:49 | |||
*/ | |||
@Data | |||
@ApiModel(description = "道路监控驾驶舱雷达流量数据-VO") | |||
@NoArgsConstructor | |||
@AllArgsConstructor | |||
public class RadarDataVO { | |||
@ApiModelProperty("设备编号") | |||
private String deviceId; | |||
@ApiModelProperty("上报时间") | |||
private String timeStamp; | |||
@ApiModelProperty("数据类型") | |||
private String dataType; | |||
@ApiModelProperty("目标所属车道") | |||
private String targetLane; | |||
@ApiModelProperty("车辆类型") | |||
private String carType; | |||
@ApiModelProperty("目标速度") | |||
private String targetSpeed; | |||
@ApiModelProperty("目标经度") | |||
private String targetLongitude; | |||
@ApiModelProperty("目标纬度") | |||
private String targetLatitude; | |||
@ApiModelProperty("实时车流量") | |||
private String realTimeTrafficFlow; | |||
@ApiModelProperty("平均车速") | |||
private String averageCarSpeed; | |||
@ApiModelProperty("最大排队长度") | |||
private String maxQueueLength; | |||
} |
@@ -0,0 +1,38 @@ | |||
package com.ningdatech.carapi.radar.model.vo; | |||
import java.time.LocalDateTime; | |||
import io.swagger.annotations.ApiModel; | |||
import io.swagger.annotations.ApiModelProperty; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Data; | |||
import lombok.NoArgsConstructor; | |||
/** | |||
* @author CMM | |||
* @since 2024/10/23 10:49 | |||
*/ | |||
@Data | |||
@ApiModel(description = "道路监控驾驶舱雷达流量数据-VO") | |||
@NoArgsConstructor | |||
@AllArgsConstructor | |||
public class RadarObjectDataVO { | |||
@ApiModelProperty("目标所属车道") | |||
private String targetLane; | |||
@ApiModelProperty("车辆类型") | |||
private String carType; | |||
@ApiModelProperty("目标速度") | |||
private String targetSpeed; | |||
@ApiModelProperty("目标经度") | |||
private String targetLongitude; | |||
@ApiModelProperty("目标纬度") | |||
private String targetLatitude; | |||
@ApiModelProperty("上报时间") | |||
private LocalDateTime updateOn; | |||
} |
@@ -0,0 +1,32 @@ | |||
package com.ningdatech.carapi.radar.model.vo; | |||
import java.time.LocalDateTime; | |||
import io.swagger.annotations.ApiModel; | |||
import io.swagger.annotations.ApiModelProperty; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Data; | |||
import lombok.NoArgsConstructor; | |||
/** | |||
* @author CMM | |||
* @since 2024/10/23 10:49 | |||
*/ | |||
@Data | |||
@ApiModel(description = "道路监控驾驶舱雷达流量数据-VO") | |||
@NoArgsConstructor | |||
@AllArgsConstructor | |||
public class RadarTrafficFlowDataVO { | |||
@ApiModelProperty("实时车流量") | |||
private String realTimeTrafficFlow; | |||
@ApiModelProperty("平均车速") | |||
private String averageCarSpeed; | |||
@ApiModelProperty("最大排队长度") | |||
private String maxQueueLength; | |||
@ApiModelProperty("上报时间") | |||
private LocalDateTime updateOn; | |||
} |
@@ -21,31 +21,19 @@ public class RadarDataTaskProperties { | |||
*/ | |||
private Boolean enable; | |||
// 雷达设备的IP地址 | |||
// 黄山隧道义乌来向 | |||
private String hsYwComeRadarIp; | |||
// 黄山隧道义乌去向 | |||
private String hsYwGoRadarIp; | |||
// 黄山隧道东阳来向 | |||
private String hsDyComeRadarIp; | |||
// 黄山隧道东阳去向 | |||
private String hsDyGoRadarIp; | |||
// 何里隧道义乌方向 | |||
private String hlYwRadarIp; | |||
// 何里隧道兰溪方向 | |||
private String hlLxRadarIp; | |||
// 雷达盒子设备的IP地址 | |||
// 黄山隧道-义乌方向 | |||
private String hsYwRadarArmIp; | |||
// 黄山隧道-东阳方向 | |||
private String hsDyRadarArmIp; | |||
// 何里隧道 | |||
private String hlRadarArmIp; | |||
// 雷达设备监听的端口号 | |||
// 黄山隧道义乌来向 | |||
private Integer hsYwComeRadarPort; | |||
// 黄山隧道义乌去向 | |||
private Integer hsYwGoRadarPort; | |||
// 黄山隧道东阳来向 | |||
private Integer hsDyComeRadarPort; | |||
// 黄山隧道东阳去向 | |||
private Integer hsDyGoRadarPort; | |||
// 何里隧道义乌方向 | |||
private Integer hlYwRadarPort; | |||
// 何里隧道兰溪方向 | |||
private Integer hlLxRadarPort; | |||
// 雷达盒子监听的端口号 | |||
// 黄山隧道-义乌方向 | |||
private Integer hsYwRadarArmPort; | |||
// 黄山隧道-东阳方向 | |||
private Integer hsDyRadarArmPort; | |||
// 何里隧道 | |||
private Integer hlRadarArmPort; | |||
} |
@@ -1,18 +1,20 @@ | |||
package com.ningdatech.carapi.radar.server; | |||
import com.ningdatech.carapi.radar.client.RadarHandler; | |||
import com.ningdatech.carapi.radar.model.dto.RadarInfoDTO; | |||
import com.ningdatech.carapi.radar.service.IRadarDataService; | |||
import lombok.RequiredArgsConstructor; | |||
import lombok.extern.slf4j.Slf4j; | |||
import java.io.IOException; | |||
import java.net.*; | |||
import java.net.Socket; | |||
import java.util.List; | |||
import java.util.Objects; | |||
import java.util.concurrent.ExecutorService; | |||
import java.util.concurrent.Executors; | |||
import com.ningdatech.carapi.radar.client.RadarHandler; | |||
import com.ningdatech.carapi.radar.helper.RadarDataHelper; | |||
import com.ningdatech.carapi.radar.model.dto.RadarInfoDTO; | |||
import com.ningdatech.carapi.radar.service.IRadarDataService; | |||
import cn.hutool.socket.SocketUtil; | |||
import lombok.extern.slf4j.Slf4j; | |||
/** | |||
* @author CMM | |||
* @author CMM | |||
@@ -26,35 +28,32 @@ public class MultiRadarServer { | |||
private final IRadarDataService radarDataService; | |||
private final ExecutorService executorService; | |||
private final RadarDataHelper radarDataHelper; | |||
public MultiRadarServer(IRadarDataService radarDataService,List<RadarInfoDTO> radarInfoList) { | |||
public MultiRadarServer(IRadarDataService radarDataService, List<RadarInfoDTO> radarInfoList, RadarDataHelper radarDataHelper) { | |||
this.radarDataService = radarDataService; | |||
this.executorService = Executors.newFixedThreadPool(radarInfoList.size()); | |||
this.radarDataHelper = radarDataHelper; | |||
} | |||
public void startServers(List<RadarInfoDTO> radarInfoList) { | |||
log.info("Radar server started. Waiting for connections..."); | |||
for (RadarInfoDTO radarInfo : radarInfoList) { | |||
if (Objects.nonNull(radarInfo.getRadarIp()) && Objects.nonNull(radarInfo.getRadarPort())) { | |||
// 每个设备创建一个线程 建立一个ServerSocket | |||
// 每个设备创建一个线程 建立一个Socket | |||
executorService.execute(() -> { | |||
try (ServerSocket serverSocket = new ServerSocket(radarInfo.getRadarPort(), 600, InetAddress.getByName(radarInfo.getRadarIp()))){ | |||
// 设置为0表示无限等待,可以根据需要设置超时时间 | |||
serverSocket.setSoTimeout(0); | |||
try (Socket socket = SocketUtil.connect(radarInfo.getRadarIp(), radarInfo.getRadarPort(),60000)){ | |||
log.info("ServerSocket started on port: {}", radarInfo.getRadarPort()); | |||
while (!Thread.currentThread().isInterrupted()) { | |||
Socket clientSocket = serverSocket.accept(); | |||
//log.info("Connected to a radar on port: {}", radarInfo.getRadarPort()); | |||
RadarHandler task = new RadarHandler(clientSocket, radarDataService, radarInfo.getRadarIp()); | |||
log.info("Connected to a radar on port: {}", radarInfo.getRadarPort()); | |||
RadarHandler task = new RadarHandler(socket, radarDataService, radarInfo.getRadarIp(),radarDataHelper); | |||
Thread thread = new Thread(task); | |||
thread.start(); | |||
} | |||
} catch (IOException e) { | |||
log.error("Error occurred while accepting client connection: ", e); | |||
log.error("Error occurred while client connection: ", e); | |||
} | |||
}); | |||
} | |||
} | |||
} | |||
@@ -0,0 +1,16 @@ | |||
package com.ningdatech.carapi.radar.service; | |||
import com.ningdatech.carapi.radar.model.entity.RadarOriginalData; | |||
import com.baomidou.mybatisplus.extension.service.IService; | |||
/** | |||
* <p> | |||
* 服务类 | |||
* </p> | |||
* | |||
* @author CMM | |||
* @since 2024-11-13 | |||
*/ | |||
public interface IRadarOriginalDataService extends IService<RadarOriginalData> { | |||
} |
@@ -0,0 +1,20 @@ | |||
package com.ningdatech.carapi.radar.service.impl; | |||
import com.ningdatech.carapi.radar.model.entity.RadarOriginalData; | |||
import com.ningdatech.carapi.radar.mapper.RadarOriginalDataMapper; | |||
import com.ningdatech.carapi.radar.service.IRadarOriginalDataService; | |||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | |||
import org.springframework.stereotype.Service; | |||
/** | |||
* <p> | |||
* 服务实现类 | |||
* </p> | |||
* | |||
* @author CMM | |||
* @since 2024-11-13 | |||
*/ | |||
@Service | |||
public class RadarOriginalDataServiceImpl extends ServiceImpl<RadarOriginalDataMapper, RadarOriginalData> implements IRadarOriginalDataService { | |||
} |
@@ -2,11 +2,10 @@ package com.ningdatech.carapi.radar.task; | |||
import java.util.List; | |||
import javax.annotation.PostConstruct; | |||
import org.apache.commons.compress.utils.Lists; | |||
import org.springframework.stereotype.Component; | |||
import com.ningdatech.carapi.radar.helper.RadarDataHelper; | |||
import com.ningdatech.carapi.radar.model.dto.RadarInfoDTO; | |||
import com.ningdatech.carapi.radar.properties.RadarDataTaskProperties; | |||
import com.ningdatech.carapi.radar.server.MultiRadarServer; | |||
@@ -27,15 +26,16 @@ public class RadarDataTask { | |||
private final RadarDataTaskProperties properties; | |||
private final IRadarDataService radarDataService; | |||
private final RadarDataHelper radarDataHelper; | |||
@PostConstruct | |||
public void initTask() { | |||
if (!properties.getEnable()) { | |||
log.warn("雷达数据同步已关闭……"); | |||
return; | |||
} | |||
//initRadarDataTaskByStart(); | |||
} | |||
//@PostConstruct | |||
//public void initTask() { | |||
// if (!properties.getEnable()) { | |||
// log.warn("雷达数据同步已关闭……"); | |||
// return; | |||
// } | |||
// //initRadarDataTaskByStart(); | |||
//} | |||
/** | |||
* 项目重启之后重新初始化雷达接收定时任务 | |||
@@ -43,13 +43,11 @@ public class RadarDataTask { | |||
private void initRadarDataTaskByStart() { | |||
log.info("雷达数据接收任务已启动……"); | |||
List<RadarInfoDTO> radarInfoList = Lists.newArrayList(); | |||
radarInfoList.add(new RadarInfoDTO(properties.getHsYwComeRadarIp(),properties.getHsYwComeRadarPort())); | |||
radarInfoList.add(new RadarInfoDTO(properties.getHsYwGoRadarIp(),properties.getHsYwGoRadarPort())); | |||
radarInfoList.add(new RadarInfoDTO(properties.getHsDyComeRadarIp(),properties.getHsDyComeRadarPort())); | |||
radarInfoList.add(new RadarInfoDTO(properties.getHsDyGoRadarIp(),properties.getHsDyGoRadarPort())); | |||
radarInfoList.add(new RadarInfoDTO(properties.getHlYwRadarIp(),properties.getHlYwRadarPort())); | |||
radarInfoList.add(new RadarInfoDTO(properties.getHlLxRadarIp(),properties.getHlLxRadarPort())); | |||
MultiRadarServer server = new MultiRadarServer(radarDataService,radarInfoList); | |||
// 每个盒子开启一个线程 | |||
radarInfoList.add(new RadarInfoDTO(properties.getHsYwRadarArmIp(),properties.getHsYwRadarArmPort())); | |||
radarInfoList.add(new RadarInfoDTO(properties.getHsDyRadarArmIp(),properties.getHsDyRadarArmPort())); | |||
radarInfoList.add(new RadarInfoDTO(properties.getHlRadarArmIp(),properties.getHlRadarArmPort())); | |||
MultiRadarServer server = new MultiRadarServer(radarDataService,radarInfoList,radarDataHelper); | |||
server.startServers(radarInfoList); | |||
} | |||
} |
@@ -0,0 +1,240 @@ | |||
package com.ningdatech.carapi.radar.task; | |||
import java.sql.Connection; | |||
import java.sql.DriverManager; | |||
import java.sql.Statement; | |||
import java.time.LocalDateTime; | |||
import java.util.*; | |||
import java.util.stream.Collectors; | |||
import cn.hutool.core.collection.CollUtil; | |||
import com.google.common.collect.Lists; | |||
import com.ningdatech.carapi.radar.helper.RadarDataHelper; | |||
import com.ningdatech.carapi.radar.model.entity.RadarData; | |||
import com.ningdatech.carapi.radar.model.entity.RadarOriginalData; | |||
import com.ningdatech.carapi.radar.model.vo.RadarDataVO; | |||
import com.ningdatech.carapi.radar.service.IRadarDataService; | |||
import com.ningdatech.carapi.radar.service.IRadarOriginalDataService; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.springframework.beans.factory.annotation.Value; | |||
import org.springframework.scheduling.annotation.Scheduled; | |||
import org.springframework.stereotype.Component; | |||
import com.baomidou.mybatisplus.core.toolkit.Wrappers; | |||
import com.ningdatech.carapi.gps.manage.GpsDataPullManage; | |||
import cn.hutool.core.date.StopWatch; | |||
import lombok.RequiredArgsConstructor; | |||
import lombok.extern.slf4j.Slf4j; | |||
/** | |||
* @author CMM | |||
* 雷达实时数据更新定时任务 | |||
* @since 2024/10/12 10:39 | |||
*/ | |||
@Component | |||
@Slf4j | |||
@RequiredArgsConstructor | |||
public class RadarRealTimeDataUpdateTask { | |||
@Value("${task.switch.is-open}") | |||
private boolean flag; | |||
@Value("${task.gps-data-pull.domain}") | |||
private String domain; | |||
@Value("${task.gps-data-pull.real-time-data-url}") | |||
private String realTimeDataUrl; | |||
@Value("${task.gps-data-pull.key}") | |||
private String key; | |||
private final GpsDataPullManage gpsDataPullManage; | |||
private final IRadarOriginalDataService radarOriginalDataService; | |||
private final RadarDataHelper radarDataHelper; | |||
private final IRadarDataService radarDataService; | |||
@Value("${spring.datasource.url}") | |||
private String dataBaseUrl; | |||
@Value("${spring.datasource.username}") | |||
private String username; | |||
@Value("${spring.datasource.password}") | |||
private String password; | |||
// 定时更新雷达实时数据 每1分钟一次 | |||
@Scheduled(cron = "0 */1 * * * ?") | |||
public void doTask() { | |||
if (!flag){ | |||
log.info("雷达实时数据更新定时任务未开启!"); | |||
return; | |||
} | |||
log.info("=========== 雷达实时数据更新 ======== 任务开始"); | |||
StopWatch stopWatch = new StopWatch(); | |||
stopWatch.start(); | |||
// 从表中分别获取三个盒子的最新的目标轨迹数据和交通流量数据 | |||
List<RadarOriginalData> dataList = radarOriginalDataService.list(); | |||
log.info("=========== 雷达实时数据更新 ======== 获取数据条数:{}",dataList.size()); | |||
// 按照盒子ip分组 | |||
Map<String, List<RadarOriginalData>> dataMap = dataList.stream().collect(Collectors.groupingBy(RadarOriginalData::getArmIp)); | |||
List<RadarOriginalData> finalDataList = Lists.newArrayList(); | |||
for (Map.Entry<String, List<RadarOriginalData>> entry : dataMap.entrySet()) { | |||
RadarOriginalData data = new RadarOriginalData(); | |||
// 针对每个盒子分别解析数据 | |||
String armIp = entry.getKey(); | |||
List<RadarOriginalData> datas = entry.getValue(); | |||
log.info("=========== 雷达实时数据更新 ======== 盒子ip:{},数据条数:{}",armIp, datas.size()); | |||
// 盒子有对应的原始数据 | |||
if (CollUtil.isNotEmpty(datas)) { | |||
// 根据数据类型进行分组 | |||
Map<String, List<RadarOriginalData>> dataTypeMap = datas.stream().filter(d -> Objects.nonNull(d.getDataType())).collect(Collectors.groupingBy(RadarOriginalData::getDataType)); | |||
// 获取每个数据类型(目标轨迹数据或者交通流量数据)的数据中最新的一条数据 | |||
for (Map.Entry<String, List<RadarOriginalData>> dataTypeEntry : dataTypeMap.entrySet()) { | |||
String dataType = dataTypeEntry.getKey(); | |||
List<RadarOriginalData> dataTypeDatas = dataTypeEntry.getValue(); | |||
if (Objects.nonNull(dataTypeDatas) && CollUtil.isNotEmpty(dataTypeDatas)) { | |||
log.info("=========== 雷达实时数据更新 ======== 数据类型:{},数据量:{}",dataType,dataTypeDatas.size()); | |||
// 按照创建时间排序 取最新的一条数据 | |||
RadarOriginalData radarOriginalData1 = dataTypeDatas.get(0); | |||
log.info("=========== 雷达实时数据更新 ======== 数据类型:{},最新数据:{}",dataType,radarOriginalData1); | |||
// 按照创建时间排序 取最新的一条数据 | |||
RadarOriginalData radarOriginalData = dataTypeDatas.stream().filter(d -> Objects.nonNull(d) && Objects.nonNull(d.getCreateOn())).max(Comparator.comparing(RadarOriginalData::getCreateOn)).orElse(null); | |||
if (Objects.nonNull(radarOriginalData)) { | |||
data.setId(radarOriginalData.getId()); | |||
data.setData(radarOriginalData.getData()); | |||
data.setArmIp(armIp); | |||
data.setDataType(dataType); | |||
finalDataList.add(data); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
List<Long> finalIdList = finalDataList.stream().map(RadarOriginalData::getId).collect(Collectors.toList()); | |||
// 过滤出dataList中其他数据 从表中删除 | |||
if (CollUtil.isNotEmpty(finalIdList)) { | |||
List<Long> removeIdList = dataList.stream().map(RadarOriginalData::getId).filter(id -> !finalIdList.contains(id)).collect(Collectors.toList()); | |||
radarOriginalDataService.removeBatchByIds(removeIdList); | |||
log.info("=========== 雷达实时数据更新 ======== 删除数据条数:{}", removeIdList.size()); | |||
} | |||
// 处理数据 | |||
List<RadarData> objects = Lists.newArrayList(); | |||
for (RadarOriginalData radarOriginalData : finalDataList) { | |||
String armIp = radarOriginalData.getArmIp(); | |||
String dataType = radarOriginalData.getDataType(); | |||
String data = radarOriginalData.getData(); | |||
List<RadarDataVO> radarDataList = radarDataHelper.getRadarDataList(data); | |||
if (CollUtil.isNotEmpty(radarDataList)) { | |||
// 获取radarDataList中数据类型为dataType的数据 | |||
List<RadarDataVO> dataTypeDataList = radarDataList.stream().filter(radarDataVO -> radarDataVO.getDataType().equals(dataType)).collect(Collectors.toList()); | |||
if (CollUtil.isNotEmpty(dataTypeDataList)) { | |||
for (RadarDataVO radarDataVO : dataTypeDataList) { | |||
RadarData radarData = new RadarData(); | |||
radarData.setArmIp(armIp); | |||
Map<String, String> deviceIdRadarIpMap = new HashMap<>(); | |||
deviceIdRadarIpMap.put("21626092", "192.168.6.43"); | |||
deviceIdRadarIpMap.put("21626098", "192.168.6.40"); | |||
deviceIdRadarIpMap.put("21626114", "192.168.6.45"); | |||
deviceIdRadarIpMap.put("21626163", "192.168.6.47"); | |||
deviceIdRadarIpMap.put("21626197", "192.168.10.63"); | |||
deviceIdRadarIpMap.put("21626107", "192.168.10.60"); | |||
String radarIp = deviceIdRadarIpMap.getOrDefault(radarDataVO.getDeviceId(), null); | |||
radarData.setRadarIp(radarIp); | |||
String voDataType = radarDataVO.getDataType(); | |||
if ("1".equals(voDataType)) { | |||
radarData.setDataType(1); | |||
} else if ("3".equals(voDataType)) { | |||
radarData.setDataType(2); | |||
} | |||
radarData.setCreateOn(LocalDateTime.now()); | |||
radarData.setUpdateOn(LocalDateTime.now()); | |||
radarData.setTargetLane(radarDataVO.getTargetLane()); | |||
Map<String, Integer> carTypeMap = new HashMap<>(); | |||
carTypeMap.put("00", 4); | |||
carTypeMap.put("01", 1); | |||
carTypeMap.put("02", 2); | |||
carTypeMap.put("03", 3); | |||
Integer type = carTypeMap.getOrDefault(radarDataVO.getCarType(), 4); | |||
radarData.setCarType(type); | |||
radarData.setTargetSpeed(radarDataVO.getTargetSpeed()); | |||
radarData.setTargetLongitude(radarDataVO.getTargetLongitude()); | |||
radarData.setTargetLatitude(radarDataVO.getTargetLatitude()); | |||
radarData.setRealTimeTrafficFlow(radarDataVO.getRealTimeTrafficFlow()); | |||
radarData.setAverageCarSpeed(radarDataVO.getAverageCarSpeed()); | |||
radarData.setMaxQueueLength(radarDataVO.getMaxQueueLength()); | |||
radarData.setCreateOn(LocalDateTime.now()); | |||
radarData.setUpdateOn(LocalDateTime.now()); | |||
objects.add(radarData); | |||
} | |||
} | |||
} | |||
} | |||
// 插入数据 | |||
if (CollUtil.isNotEmpty(objects)) { | |||
radarDataService.saveBatch(objects); | |||
log.info("=========== 雷达实时数据更新 ======== 插入数据条数:{}",objects.size()); | |||
} | |||
stopWatch.stop(); | |||
log.info("=========== 雷达实时数据更新 ======== 任务结束 {}s",stopWatch.getTotalTimeSeconds()); | |||
} | |||
private void truncateTable() { | |||
// SQL 语句清空表 | |||
String truncateTableSQL = "TRUNCATE TABLE nd_data_access_gps_real_time_data"; | |||
Connection conn = null; | |||
Statement stmt = null; | |||
try { | |||
// 加载数据库驱动 | |||
Class.forName("com.mysql.cj.jdbc.Driver"); | |||
// 建立连接 | |||
conn = DriverManager.getConnection(dataBaseUrl, username, password); | |||
// 创建 Statement 对象 | |||
stmt = conn.createStatement(); | |||
// 执行 SQL 语句 | |||
stmt.executeUpdate(truncateTableSQL); | |||
log.info("表已被清空,自增主键已重置。"); | |||
} catch (Exception e) { | |||
log.error("Error truncating table: {}", e.getMessage()); | |||
} finally { | |||
// 关闭资源 | |||
try { | |||
if (stmt != null) { | |||
stmt.close(); | |||
} | |||
if (conn != null) { | |||
conn.close(); | |||
} | |||
} catch (Exception e) { | |||
log.error("Error closing resources: {}", e.getMessage()); | |||
} | |||
} | |||
} | |||
private Map<String, String> assemblyParams(String key, String sLon, String eLon, String sLat, String eLat) { | |||
long timeNow = System.currentTimeMillis() / 1000; | |||
String token = gpsDataPullManage.generateMD5Token(timeNow, key); | |||
Map<String, String> params = new HashMap<>(); | |||
params.put("timeNow", String.valueOf(timeNow)); | |||
params.put("token", token); | |||
if (StringUtils.isNotBlank(sLon)) { | |||
params.put("Slon", sLon); | |||
} | |||
if (StringUtils.isNotBlank(eLon)) { | |||
params.put("Elon", eLon); | |||
} | |||
if (StringUtils.isNotBlank(sLat)) { | |||
params.put("Slat", sLat); | |||
} | |||
if (StringUtils.isNotBlank(eLat)) { | |||
params.put("Elat", eLat); | |||
} | |||
return params; | |||
} | |||
} |
@@ -1,21 +1,23 @@ | |||
package com.ningdatech.carapi.road.controller; | |||
import com.ningdatech.carapi.road.manage.RoadMonitorManage; | |||
import com.ningdatech.carapi.road.model.req.RoadMonitorHandleReq; | |||
import com.ningdatech.carapi.road.model.req.RoadMonitorReq; | |||
import com.ningdatech.carapi.road.model.req.VideoDownloadReq; | |||
import com.ningdatech.carapi.road.model.vo.ComprehensiveSituationVO; | |||
import com.ningdatech.carapi.radar.model.vo.RadarTrafficFlowDataVO; | |||
import com.ningdatech.carapi.road.model.vo.RoadDangerBehaviorVO; | |||
import com.ningdatech.carapi.road.model.vo.VehicleGpsDataVO; | |||
import com.ningdatech.log.annotation.WebLog; | |||
import org.springframework.validation.annotation.Validated; | |||
import org.springframework.web.bind.annotation.GetMapping; | |||
import org.springframework.web.bind.annotation.RequestMapping; | |||
import org.springframework.web.bind.annotation.RestController; | |||
import org.springframework.web.bind.annotation.*; | |||
import io.swagger.annotations.ApiOperation; | |||
import lombok.RequiredArgsConstructor; | |||
import lombok.extern.slf4j.Slf4j; | |||
import javax.servlet.http.HttpServletResponse; | |||
import javax.validation.Valid; | |||
import java.util.List; | |||
/** | |||
@@ -53,10 +55,30 @@ public class RoadMonitorController { | |||
return roadMonitorManage.getViolationWarnRecordData(req); | |||
} | |||
@ApiOperation(value = "危险行为数据处理", notes = "危险行为数据处理") | |||
@PostMapping("/handle-violation-record") | |||
public String handleViolationRecord(@Valid @RequestBody RoadMonitorHandleReq req) { | |||
return roadMonitorManage.handleViolationRecord(req); | |||
} | |||
@ApiOperation(value = "数据驾驶舱GPS数据", notes = "数据驾驶舱GPS数据") | |||
@GetMapping("/vehicle-gps-data") | |||
public List<VehicleGpsDataVO> getVehicleGpsData(RoadMonitorReq req) { | |||
return roadMonitorManage.getVehicleGpsData(req); | |||
} | |||
@GetMapping("/video/download") | |||
@ApiOperation("视频下载") | |||
@WebLog("视频下载") | |||
public void downloadOperationManual(VideoDownloadReq req, HttpServletResponse response){ | |||
public void videoDownload(VideoDownloadReq req, HttpServletResponse response){ | |||
roadMonitorManage.videoDownload(req,response); | |||
} | |||
@GetMapping("/picture/download") | |||
@ApiOperation("图片下载") | |||
@WebLog("图片下载") | |||
public void pictureDownload(VideoDownloadReq req, HttpServletResponse response){ | |||
roadMonitorManage.pictureDownload(req,response); | |||
} | |||
} |
@@ -3,15 +3,18 @@ package com.ningdatech.carapi.road.manage; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.IOException; | |||
import java.time.LocalDateTime; | |||
import java.util.*; | |||
import java.util.stream.Collectors; | |||
import javax.servlet.ServletOutputStream; | |||
import javax.servlet.http.HttpServletResponse; | |||
import com.ningdatech.carapi.radar.helper.RadarDataHelper; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.springframework.beans.factory.annotation.Value; | |||
import org.springframework.stereotype.Component; | |||
import org.springframework.transaction.annotation.Transactional; | |||
import com.alibaba.fastjson.JSON; | |||
import com.alibaba.fastjson.JSONArray; | |||
@@ -20,12 +23,16 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; | |||
import com.google.common.collect.Lists; | |||
import com.ningdatech.basic.exception.BizException; | |||
import com.ningdatech.carapi.common.contants.DefValConstants; | |||
import com.ningdatech.carapi.homepage.entity.model.NdDataAccessGps; | |||
import com.ningdatech.carapi.homepage.service.IDataAccessGpsService; | |||
import com.ningdatech.carapi.road.constant.*; | |||
import com.ningdatech.carapi.road.model.entity.RoadBehaviorAnalysis; | |||
import com.ningdatech.carapi.road.model.req.RoadMonitorHandleReq; | |||
import com.ningdatech.carapi.road.model.req.RoadMonitorReq; | |||
import com.ningdatech.carapi.road.model.req.VideoDownloadReq; | |||
import com.ningdatech.carapi.road.model.vo.ComprehensiveSituationVO; | |||
import com.ningdatech.carapi.road.model.vo.RoadDangerBehaviorVO; | |||
import com.ningdatech.carapi.road.model.vo.VehicleGpsDataVO; | |||
import com.ningdatech.carapi.road.service.IRoadBehaviorAnalysisService; | |||
import com.ningdatech.carapi.scheduler.contants.TaskConstant; | |||
@@ -53,11 +60,13 @@ public class RoadMonitorManage { | |||
private String redisPassword; | |||
private final IRoadBehaviorAnalysisService roadBehaviorAnalysisService; | |||
private final IDataAccessGpsService dataAccessGpsService; | |||
private final RadarDataHelper radarDataHelper; | |||
public ComprehensiveSituationVO getComSitData(RoadMonitorReq req) { | |||
Jedis jedis = new Jedis(redisHost,redisPort); | |||
jedis.auth(redisPassword); | |||
// 隧道类型 | |||
// 隧道类型 1 黄山隧道、2 何里隧道、3 两条隧道的汇总(求平均) | |||
Integer tunnel = req.getTunnel(); | |||
// 根据传入的隧道类型 实时从缓存中获取算法分析的结果 | |||
// 分区域分别计算4个指标的分值 比例按照1:1:1:1 加权计算道路安全指数 | |||
@@ -70,6 +79,9 @@ public class RoadMonitorManage { | |||
else if (tunnel == 2) { | |||
List<String> regionList = Lists.newArrayList("5", "6"); | |||
return getCollectDataVo(regionList,jedis,tunnel); | |||
} else if (tunnel == 3) { | |||
List<String> regionList = Lists.newArrayList("1", "2", "3", "4", "5", "6"); | |||
return getCollectDataVo(regionList, jedis, tunnel); | |||
} | |||
return new ComprehensiveSituationVO(); | |||
} | |||
@@ -133,6 +145,9 @@ public class RoadMonitorManage { | |||
} else if (tunnel == 2) { | |||
List<ComprehensiveSituationVO> vos = Lists.newArrayList(hlLxVo, hlYwVo); | |||
vo = averageScores(vos); | |||
} else if (tunnel == 3) { | |||
List<ComprehensiveSituationVO> vos = Lists.newArrayList(hsDyGoVo, hsDyComeVo, hsYwGoVo, hsYwComeVo, hlLxVo, hlYwVo); | |||
vo = averageScores(vos); | |||
} | |||
return vo; | |||
} | |||
@@ -375,21 +390,25 @@ public class RoadMonitorManage { | |||
// 从列表中查询数据 | |||
List<RoadBehaviorAnalysis> list = roadBehaviorAnalysisService.list(Wrappers.lambdaQuery(RoadBehaviorAnalysis.class) | |||
.in(RoadBehaviorAnalysis::getRegion, regionList) | |||
.like(StringUtils.isNoneBlank(req.getBehavior()),RoadBehaviorAnalysis::getBehavior, req.getBehavior()) | |||
.eq(Objects.nonNull(req.getType()),RoadBehaviorAnalysis::getType, req.getType()) | |||
.ge(Objects.nonNull(req.getStartTime()), RoadBehaviorAnalysis::getBehaviorTime, req.getStartTime()) | |||
.le(Objects.nonNull(req.getEndTime()), RoadBehaviorAnalysis::getBehaviorTime, req.getEndTime()) | |||
.eq(Objects.nonNull(req.getIsWarn()),RoadBehaviorAnalysis::getIsWarn, req.getIsWarn()) | |||
.orderByDesc(RoadBehaviorAnalysis::getBehaviorTime)); | |||
if (CollUtil.isEmpty(list)){ | |||
return Collections.emptyList(); | |||
} | |||
return list.stream().map(roadBehaviorAnalysis -> { | |||
RoadDangerBehaviorVO vo = new RoadDangerBehaviorVO(); | |||
vo.setId(roadBehaviorAnalysis.getId()); | |||
vo.setRecord(roadBehaviorAnalysis.getBehavior()); | |||
vo.setRecordCode(roadBehaviorAnalysis.getBehaviorCode()); | |||
vo.setRecordTime(roadBehaviorAnalysis.getBehaviorTime()); | |||
vo.setRecordType(RoadBehaviorTypeEnum.getDescByCode(roadBehaviorAnalysis.getType())); | |||
vo.setRegion(roadBehaviorAnalysis.getRegion()); | |||
vo.setPicturePath(roadBehaviorAnalysis.getPicturePath()); | |||
vo.setVideoPath(roadBehaviorAnalysis.getVideoPath()); | |||
vo.setLatitude(roadBehaviorAnalysis.getLatitude()); | |||
vo.setLongitude(roadBehaviorAnalysis.getLongitude()); | |||
vo.setOverspeedPositionInfo(roadBehaviorAnalysis.getOverspeedPositionInfo()); | |||
vo.setIsWarn(roadBehaviorAnalysis.getIsWarn()); | |||
vo.setIsHandled(roadBehaviorAnalysis.getIsHandled()); | |||
return vo; | |||
@@ -464,18 +483,100 @@ public class RoadMonitorManage { | |||
} | |||
return list.stream().map(roadBehaviorAnalysis -> { | |||
RoadDangerBehaviorVO vo = new RoadDangerBehaviorVO(); | |||
vo.setId(roadBehaviorAnalysis.getId()); | |||
vo.setRecord(roadBehaviorAnalysis.getBehavior()); | |||
vo.setRecordCode(roadBehaviorAnalysis.getBehaviorCode()); | |||
vo.setRecordTime(roadBehaviorAnalysis.getBehaviorTime()); | |||
vo.setRecordType(RoadBehaviorTypeEnum.getDescByCode(roadBehaviorAnalysis.getType())); | |||
vo.setRegion(roadBehaviorAnalysis.getRegion()); | |||
vo.setPicturePath(roadBehaviorAnalysis.getPicturePath()); | |||
vo.setVideoPath(roadBehaviorAnalysis.getVideoPath()); | |||
vo.setLatitude(roadBehaviorAnalysis.getLatitude()); | |||
vo.setLongitude(roadBehaviorAnalysis.getLongitude()); | |||
vo.setOverspeedPositionInfo(roadBehaviorAnalysis.getOverspeedPositionInfo()); | |||
vo.setIsWarn(roadBehaviorAnalysis.getIsWarn()); | |||
vo.setIsHandled(roadBehaviorAnalysis.getIsHandled()); | |||
return vo; | |||
}).collect(Collectors.toList()); | |||
} | |||
@Transactional(rollbackFor = Exception.class) | |||
public String handleViolationRecord(RoadMonitorHandleReq req) { | |||
Long id = req.getId(); | |||
// 获取危险行为数据 | |||
RoadBehaviorAnalysis roadBehaviorAnalysis = roadBehaviorAnalysisService.getById(id); | |||
if (Objects.isNull(roadBehaviorAnalysis)){ | |||
throw new BizException("数据不存在!"); | |||
} | |||
if (Boolean.TRUE.equals(roadBehaviorAnalysis.getIsHandled())){ | |||
throw new BizException("数据已处理!"); | |||
} | |||
// 更新对应数据状态为已处理 | |||
roadBehaviorAnalysis.setIsHandled(Boolean.TRUE); | |||
if (roadBehaviorAnalysisService.updateById(roadBehaviorAnalysis)) { | |||
return "处理成功!"; | |||
}else { | |||
throw new BizException("处理失败!"); | |||
} | |||
} | |||
public List<VehicleGpsDataVO> getVehicleGpsData(RoadMonitorReq req) { | |||
// TODO 暂时不确定隧道的经纬度范围 确定后 需要根据查询的隧道 筛选出对应经纬度范围内的GPS数据 目前返回全部的 | |||
// 只查询最新的5分钟内的数据 | |||
LocalDateTime startTime = LocalDateTime.now().minusMinutes(5); | |||
LocalDateTime endTime = LocalDateTime.now(); | |||
List<NdDataAccessGps> data = dataAccessGpsService.list(Wrappers.lambdaQuery(NdDataAccessGps.class) | |||
.ge(NdDataAccessGps::getUpdateTime, startTime).le(NdDataAccessGps::getUpdateTime, endTime) | |||
.select(NdDataAccessGps::getCarLongitude, NdDataAccessGps::getCarLatitude, NdDataAccessGps::getCarPlate, | |||
NdDataAccessGps::getCarVelocity, NdDataAccessGps::getCarDirection, NdDataAccessGps::getUpdateTime)); | |||
log.info("最近5分钟 查询到{}条数据", data.size()); | |||
// 如果最近15分钟没有数据(GPS数据不更新了) 取表中最新的5000条数据 | |||
if (CollUtil.isEmpty(data)){ | |||
data = dataAccessGpsService.list(Wrappers.lambdaQuery(NdDataAccessGps.class) | |||
.select(NdDataAccessGps::getCarLongitude, NdDataAccessGps::getCarLatitude, | |||
NdDataAccessGps::getCarPlate,NdDataAccessGps::getCarVelocity, | |||
NdDataAccessGps::getCarDirection, NdDataAccessGps::getUpdateTime) | |||
.orderByDesc(NdDataAccessGps::getUpdateTime).last("limit 5000")); | |||
} | |||
return data.stream().map(d -> { | |||
VehicleGpsDataVO vo = new VehicleGpsDataVO(); | |||
vo.setCarPlate(d.getCarPlate()); | |||
vo.setCarVelocity(d.getCarVelocity()); | |||
vo.setCarDirection(d.getCarDirection()); | |||
vo.setLongitude(d.getCarLongitude()); | |||
vo.setLatitude(d.getCarLatitude()); | |||
vo.setUpdateTime(d.getUpdateTime()); | |||
return vo; | |||
}).collect(Collectors.toList()); | |||
} | |||
public void pictureDownload(VideoDownloadReq req, HttpServletResponse response) { | |||
String picturePath = req.getPicturePath(); | |||
// 将路径中的文件分割符替换为系统能够识别的分隔符 | |||
picturePath = picturePath.replace("\\", File.separator); | |||
picturePath = picturePath.replace("/", File.separator); | |||
File file = new File(picturePath); | |||
// 设置文件名(将显示在下载对话框中) | |||
response.setHeader("Content-Disposition", "inline;"); | |||
// 设置文件大小,这样浏览器可以显示下载进度 | |||
response.setContentLengthLong(file.length()); | |||
// 设置缓存控制头 | |||
response.setHeader("Cache-Control", "public, max-age=31536000"); | |||
try { | |||
FileInputStream fileInputStream = new FileInputStream(file); | |||
ServletOutputStream servletOutputStream = response.getOutputStream(); | |||
byte[] buffer = new byte[4096]; | |||
int bytesRead; | |||
while ((bytesRead = fileInputStream.read(buffer)) != -1) { | |||
servletOutputStream.write(buffer, 0, bytesRead); | |||
} | |||
fileInputStream.close(); | |||
servletOutputStream.close(); | |||
} catch (IOException e) { | |||
throw new BizException("文件下载失败! {}",e); | |||
} | |||
} | |||
} |
@@ -0,0 +1,17 @@ | |||
package com.ningdatech.carapi.road.model; | |||
import io.swagger.annotations.ApiModelProperty; | |||
import lombok.Data; | |||
/** | |||
* @author CMM | |||
* @since 2024/11/08 10:55 | |||
*/ | |||
@Data | |||
public class OverspeedPositionInfo { | |||
@ApiModelProperty("经度") | |||
private String longitude; | |||
@ApiModelProperty("纬度") | |||
private String latitude; | |||
} |
@@ -46,14 +46,8 @@ public class RoadBehaviorAnalysis implements Serializable { | |||
@ApiModelProperty("异常记录图片路径") | |||
private String picturePath; | |||
@ApiModelProperty("异常记录视频路径") | |||
private String videoPath; | |||
@ApiModelProperty("异常记录经度") | |||
private String longitude; | |||
@ApiModelProperty("异常记录纬度") | |||
private String latitude; | |||
@ApiModelProperty("超速异常记录位置信息") | |||
private String overspeedPositionInfo; | |||
@ApiModelProperty("是否需要预警") | |||
private Boolean isWarn = Boolean.FALSE; | |||
@@ -0,0 +1,26 @@ | |||
package com.ningdatech.carapi.road.model.req; | |||
import java.time.LocalDateTime; | |||
import java.util.List; | |||
import javax.validation.constraints.NotEmpty; | |||
import javax.validation.constraints.NotNull; | |||
import io.swagger.annotations.ApiModelProperty; | |||
import lombok.Data; | |||
/** | |||
* @author CMM | |||
* @author CMM | |||
* @date 2024/10/23 17:53 | |||
* / | |||
* /** | |||
* @since 2024/10/23 17:53 | |||
*/ | |||
@Data | |||
public class RoadMonitorHandleReq { | |||
@ApiModelProperty("记录ID") | |||
@NotNull(message = "记录ID不能为空!") | |||
private Long id; | |||
} |
@@ -1,5 +1,6 @@ | |||
package com.ningdatech.carapi.road.model.req; | |||
import java.time.LocalDateTime; | |||
import java.util.List; | |||
import io.swagger.annotations.ApiModelProperty; | |||
@@ -21,7 +22,7 @@ public class RoadMonitorReq { | |||
@ApiModelProperty("区域 1 黄山隧道东阳去向、2 黄山隧道东阳来向、3 黄山隧道义乌去向、4 黄山隧道义乌来向、5 何里隧道兰溪方向、6 何里隧道义乌方向") | |||
private String region; | |||
@ApiModelProperty("隧道 1 黄山隧道、2 何里隧道") | |||
@ApiModelProperty("隧道 1 黄山隧道、2 何里隧道、3 两条隧道汇总") | |||
private Integer tunnel; | |||
@ApiModelProperty("隧道包含的区域 列表") | |||
@@ -30,4 +31,16 @@ public class RoadMonitorReq { | |||
@ApiModelProperty("行为类型 1 驾驶员、2 车辆、3 道路、4 环境") | |||
private Integer type; | |||
@ApiModelProperty("危险行为") | |||
private String behavior; | |||
@ApiModelProperty("开始时间") | |||
private LocalDateTime startTime; | |||
@ApiModelProperty("结束时间") | |||
private LocalDateTime endTime; | |||
@ApiModelProperty("是否预警") | |||
private Boolean isWarn; | |||
} |
@@ -21,4 +21,7 @@ public class VideoDownloadReq { | |||
@ApiModelProperty("1 2 3 4 5 6") | |||
private String videoOrder; | |||
@ApiModelProperty("图片路径") | |||
private String picturePath; | |||
} |
@@ -18,6 +18,9 @@ import lombok.NoArgsConstructor; | |||
@AllArgsConstructor | |||
public class RoadDangerBehaviorVO { | |||
@ApiModelProperty("危险行为ID") | |||
private Long id; | |||
@ApiModelProperty("道路异常分析记录") | |||
private String record; | |||
@@ -39,11 +42,8 @@ public class RoadDangerBehaviorVO { | |||
@ApiModelProperty("异常记录视频路径") | |||
private String videoPath; | |||
@ApiModelProperty("异常记录经度") | |||
private String longitude; | |||
@ApiModelProperty("异常记录纬度") | |||
private String latitude; | |||
@ApiModelProperty("超速异常记录位置信息") | |||
private String overspeedPositionInfo; | |||
@ApiModelProperty("是否需要预警") | |||
private Boolean isWarn = Boolean.FALSE; | |||
@@ -0,0 +1,39 @@ | |||
package com.ningdatech.carapi.road.model.vo; | |||
import java.math.BigDecimal; | |||
import java.time.LocalDateTime; | |||
import io.swagger.annotations.ApiModel; | |||
import io.swagger.annotations.ApiModelProperty; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Data; | |||
import lombok.NoArgsConstructor; | |||
/** | |||
* @author CMM | |||
* @since 2024/10/23 10:49 | |||
*/ | |||
@Data | |||
@ApiModel(description = "数据驾驶舱车辆GPS数据-VO") | |||
@NoArgsConstructor | |||
@AllArgsConstructor | |||
public class VehicleGpsDataVO { | |||
@ApiModelProperty("车牌号") | |||
private String carPlate; | |||
@ApiModelProperty("车速") | |||
private BigDecimal carVelocity; | |||
@ApiModelProperty("车辆方向") | |||
private BigDecimal carDirection; | |||
@ApiModelProperty("上报时间") | |||
private LocalDateTime updateTime; | |||
@ApiModelProperty("经度") | |||
private BigDecimal longitude; | |||
@ApiModelProperty("纬度") | |||
private BigDecimal latitude; | |||
} |
@@ -6,6 +6,7 @@ import java.util.stream.Collectors; | |||
import cn.hutool.core.map.MapUtil; | |||
import com.ningdatech.basic.util.StrPool; | |||
import com.ningdatech.carapi.road.model.OverspeedPositionInfo; | |||
import org.apache.commons.compress.utils.Lists; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.springframework.beans.factory.annotation.Value; | |||
@@ -342,26 +343,9 @@ public class RoadMonitorDataPullTask { | |||
String time = jsonObject.getString("Time"); | |||
String region = jsonObject.getString("Region"); | |||
JSONArray pictures = jsonObject.getJSONArray("PicturePath"); | |||
String picturePath = null; | |||
if (Objects.nonNull(pictures) && CollUtil.isNotEmpty(pictures)) { | |||
picturePath = pictures.getString(0); | |||
} | |||
JSONArray videos = jsonObject.getJSONArray("VideoPath"); | |||
String videoPath = null; | |||
if (Objects.nonNull(videos) && CollUtil.isNotEmpty(videos)) { | |||
videoPath = videos.getString(0); | |||
} | |||
String picturePath = jsonObject.getString("PicturePath"); | |||
JSONArray longitudes = jsonObject.getJSONArray("Longitude"); | |||
String longitude = null; | |||
if (Objects.nonNull(longitudes) && CollUtil.isNotEmpty(longitudes)){ | |||
longitude = longitudes.getString(0); | |||
} | |||
JSONArray latitudes = jsonObject.getJSONArray("Latitude"); | |||
String latitude = null; | |||
if (Objects.nonNull(latitudes) && CollUtil.isNotEmpty(latitudes)){ | |||
latitude = latitudes.getString(0); | |||
} | |||
// 转换为LocalDateTime类型 | |||
LocalDateTime createTime = LocalDateTimeUtil.parse(time, NdDateUtils.DEFAULT_DATE_TIME_FORMAT); | |||
@@ -375,9 +359,19 @@ public class RoadMonitorDataPullTask { | |||
roadBehaviorAnalysis.setRegion(region); | |||
roadBehaviorAnalysis.setType(type); | |||
roadBehaviorAnalysis.setPicturePath(picturePath); | |||
roadBehaviorAnalysis.setVideoPath(videoPath); | |||
roadBehaviorAnalysis.setLongitude(longitude); | |||
roadBehaviorAnalysis.setLatitude(latitude); | |||
// 获取超速车辆的经纬度数据 | |||
if ((Objects.nonNull(longitudes) && CollUtil.isNotEmpty(longitudes)) && (Objects.nonNull(latitudes) && CollUtil.isNotEmpty(latitudes))){ | |||
List<OverspeedPositionInfo> positionInfoList = Lists.newArrayList(); | |||
// 按一对一的关系 拼接经纬度 | |||
for (int i = 0; i < longitudes.size(); i++) { | |||
OverspeedPositionInfo positionInfo = new OverspeedPositionInfo(); | |||
positionInfo.setLongitude(longitudes.getString(i)); | |||
positionInfo.setLatitude(latitudes.getString(i)); | |||
positionInfoList.add(positionInfo); | |||
} | |||
// 转化为Json字符串 | |||
roadBehaviorAnalysis.setOverspeedPositionInfo(JSON.toJSONString(positionInfoList)); | |||
} | |||
roadBehaviorAnalysis.setIsWarn(judgeIsWarn(behaviorCode)); | |||
// 首次保存的数据默认未处理 | |||
roadBehaviorAnalysis.setIsHandled(Boolean.FALSE); | |||
@@ -153,16 +153,10 @@ send-urge-warn: | |||
radar-data-task: | |||
enable: false | |||
# 黄山隧道 | |||
hs-yw-come-radar-ip: 192.168.6.40 | |||
hs-yw-come-radar-port: 10000 | |||
hs-yw-go-radar-ip: 192.168.6.43 | |||
hs-yw-go-radar-port: 20000 | |||
hs-dy-come-radar-ip: 192.168.6.47 | |||
hs-dy-come-radar-port: 30000 | |||
hs-dy-go-radar-ip: 192.168.6.45 | |||
hs-dy-go-radar-port: 40000 | |||
hs-yw-radar-arm-ip: 192.168.6.42 | |||
hs-yw-radar-arm-port: 13000 | |||
hs-dy-radar-arm-ip: 192.168.6.49 | |||
hs-dy-radar-arm-port: 13001 | |||
# 何里隧道 | |||
hl-yw-radar-ip: 192.168.10.60 | |||
hl-yw-radar-port: 50000 | |||
hl-lx-radar-ip: 192.168.10.63 | |||
hl-lx-radar-port: 60000 | |||
hl-radar-arm-ip: 192.168.10.62 | |||
hl-radar-arm-port: 13002 |
@@ -46,4 +46,5 @@ security: | |||
- /api/car-rpt/** | |||
- /open/api/** | |||
- /radar/** | |||
- /gps/** | |||
- /gps/** | |||
- /road-monitor/** |
@@ -0,0 +1,437 @@ | |||
package com.ningdatech.carapi.radar; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import java.util.Arrays; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import org.junit.Test; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import com.google.common.collect.Lists; | |||
import com.ningdatech.carapi.AppTests; | |||
import com.ningdatech.carapi.radar.helper.RadarDataHelper; | |||
import lombok.extern.slf4j.Slf4j; | |||
/** | |||
* @author CMM | |||
* @author CMM | |||
* @date 2024/11/8 10:28 | |||
* / | |||
* /** | |||
* @since 2024/11/08 10:28 | |||
*/ | |||
@Slf4j | |||
public class RadarTest extends AppTests { | |||
@Autowired | |||
private RadarDataHelper radarDataHelper; | |||
@Test | |||
public void test(){ | |||
// 目标轨迹数据 | |||
//String data = "55aa55bb01d001763231363236303932000000000000000000000000180b0d0f2d0c00530b7e210232140fffffff0e0cfe5c1aa4000a07761adc0000ff4c000047868a4c11e1baee0b4b210232140fffffff0e0cfe167d4600f0097e22560000fe8e000047868a0311e213880b63220232140fffffff003801183f0cffce06c21856000000b4000047868d2211e1dba20b4e210232140fffffff0e0cfe2069aa019007f81d3bffecffa6fef247868a0e11e201eb0b7fff0232140fffffff0debfbaa14faff2407801b2dfff6fe8eff4c4786878011e1b5d70b57210232140fffffff0e0efe3e53e8fff607d01c200000fdda000047868a2d11e1ee5f0b661f0232140fffffff0e0efb32442afff6091a20c40000ffa6ffa64786870411e1e03b0b5d2003961925ffffff0e0cfc9a4a60ffa606cc18800000000000004786887911e1e5cf0b71200232140fffffff0e0ffccc29ae0000073a1a04000000000000478688ad11e1c8710b67200232140fffffff0e0ffcf43c14000007bc1bd7000000000000478688d611e1d8f80b7b200232140fffffff0e0efcb81f900000078a1b230000000000004786889811e1bf5a0b3e210232140fffffff0e0cfdda6cf2007805be14bd0000ff4c0000478689c511e204de55cc55dd"; | |||
// 交通流量数据 | |||
String data = "55aa55bb007803703231363236303932000000000000000000000000180b0d102914000f0000001f0064000600000000000602f9001e00660000000000200064000400000000000402e5003200350000000000210064000300000000000302eb004600350000000000220064000200000000000202520064002b000055cc55dd"; | |||
String head = "55aa55bb"; | |||
String tail = "55cc55dd"; | |||
// 解析上面的数据帧data 包头是head 包尾是tail 从包头到包尾分段取出数据 放入一个字符串列表中 等待解析 | |||
List<String> segments = Lists.newArrayList(); | |||
int headIndex = data.indexOf(head); | |||
while (headIndex != -1) { | |||
int tailIndex = data.indexOf(tail, headIndex + head.length()); | |||
if (tailIndex != -1) { | |||
// 提取从包头到包尾的数据段 | |||
String segment = data.substring(headIndex + head.length(), tailIndex); | |||
segments.add(segment); | |||
// 移动到下一个包头的位置 | |||
headIndex = data.indexOf(head, tailIndex + tail.length()); | |||
} else { | |||
// 没有找到包尾,结束循环 | |||
break; | |||
} | |||
} | |||
// 遍历这些数据帧 解析其中携带的数据 | |||
for (String segment : segments) { | |||
System.out.println(segment); | |||
//// 获取数据长度 2字节 4字符 判断数据包长度 | |||
byte[] dataLengthData = radarDataHelper.extractHexData(segment, 0, 2); | |||
String dataLengthHexString = radarDataHelper.byteArrayToHexStringAll(dataLengthData); | |||
// 将16进制字符串转换为字节数组 | |||
byte[] lengthBytes = new byte[2]; | |||
// 高8位 | |||
lengthBytes[0] = (byte) Integer.parseInt(dataLengthHexString.substring(0, 2), 16); | |||
// 低8位 | |||
lengthBytes[1] = (byte) Integer.parseInt(dataLengthHexString.substring(2, 4), 16); | |||
// 将字节数组转换为16位整数,注意网络字节序是大端序 | |||
int byteLength = ((lengthBytes[0] & 0xFF) << 8) | (lengthBytes[1] & 0xFF); | |||
// 打印结果 | |||
System.out.println("数据长度: " + byteLength); | |||
// 获取数据帧类型 1字节 2字符 判断是目标轨迹数据还是交通流量数据 | |||
byte[] objectTypeData = radarDataHelper.extractHexData(segment, 2, 1); | |||
String objectTypeHexString = radarDataHelper.byteArrayToHexString(objectTypeData); | |||
System.out.println("数据帧类型:" + objectTypeHexString); | |||
// 获取设备编号 20字节 40字符 判断是哪个雷达的发送的数据 | |||
byte[] deviceIdData = radarDataHelper.extractHexData(segment, 4, 16); | |||
StringBuilder builder = new StringBuilder(); | |||
for (int i = 0; i < deviceIdData.length; i++) { | |||
byte b = deviceIdData[i]; | |||
// 16进制数 "32" | |||
String hexString = String.format("%02X", b); | |||
// 将16进制字符串转换为十进制整数 | |||
int decimalValue = Integer.parseInt(hexString, 16); | |||
// 将十进制整数转换为对应的ASCII字符 | |||
char asciiChar = (char) decimalValue; | |||
// 如果不为0则添加到字符串中 | |||
if (decimalValue != 0) { | |||
builder.append(asciiChar); | |||
} | |||
} | |||
System.out.println("设备编号:" + builder); | |||
//String deviceIdHexString = radarDataHelper.byteArrayToHexString(deviceIdData); | |||
//System.out.println("设备编号:" + deviceIdHexString); | |||
byte[] timestampData = radarDataHelper.extractHexData(segment, 24, 8); | |||
String timestampHexString = radarDataHelper.byteArrayToHexString(timestampData); | |||
System.out.println("时间戳:" + timestampHexString); | |||
String localDateTime = getLocalDateTime(segment, timestampData); | |||
System.out.println("时间戳:" + localDateTime); | |||
// 获取数据 | |||
// 用获取到的数据长度 减去数据长度(2字节)、数据帧类型(1字节)、校验和(1字节)、数据编号)、时间戳(8字节)、包头包尾(8字节) | |||
int dataLength = byteLength - 2 - 1 - 1 - 20 - 8; | |||
segment = segment.substring(64); | |||
System.out.println("总数据:" + segment); | |||
// 如果是目标轨迹数据 | |||
if (objectTypeHexString.equals("01")) { | |||
if (dataLength >= 36) { | |||
byte[] dataBytes = radarDataHelper.hexStringToByteArray(segment); | |||
// 36个字节为一组数据 进行分组解析 | |||
for (int i = 0; i < dataBytes.length; i += 36) { | |||
byte[] dataSegment = Arrays.copyOfRange(dataBytes, i, i + 36); | |||
String dataSegmentHexString = radarDataHelper.byteArrayToHexStringAll(dataSegment); | |||
System.out.println("数据:" + dataSegmentHexString); | |||
// 获取目标所属车道 | |||
byte[] targetLaneData = radarDataHelper.extractHexData(dataSegmentHexString, 2, 1); | |||
String targetLaneHexString = radarDataHelper.byteArrayToHexStringAll(targetLaneData); | |||
// 将16进制数转换为10进制数 | |||
int targetLane = Integer.parseInt(targetLaneHexString, 16); | |||
System.out.println("目标所属车道:" + targetLane); | |||
// 获取目标类型 | |||
byte[] carTypeData = radarDataHelper.extractHexData(dataSegmentHexString, 3, 1); | |||
String carTypeHexString = radarDataHelper.byteArrayToHexStringAll(carTypeData); | |||
System.out.println("目标类型:" + carTypeHexString); | |||
// 获取目标速度 | |||
byte[] targetSpeedData = radarDataHelper.extractHexData(dataSegmentHexString, 20, 2); | |||
String targetSpeedHexString = radarDataHelper.byteArrayToHexStringAll(targetSpeedData); | |||
double targetSpeed = Integer.parseInt(targetSpeedHexString, 16) * 0.01; | |||
System.out.println("目标速度:" + targetSpeed); | |||
// 获取目标经度 | |||
byte[] targetLongitudeData = radarDataHelper.extractHexData(dataSegmentHexString, 28, 4); | |||
String targetLongitudeHexString = radarDataHelper.byteArrayToHexStringAll(targetLongitudeData); | |||
// 将十六进制字符串转换为long类型的数字 | |||
long targetLongitudeLongValue = Long.parseLong(targetLongitudeHexString, 16); | |||
// 将long类型的数字转换为double类型的数字 | |||
double targetLongitude = (double) targetLongitudeLongValue / 10000000; | |||
System.out.println("目标经度:" + targetLongitude); | |||
// 获取目标纬度 | |||
byte[] targetLatitudeData = radarDataHelper.extractHexData(dataSegmentHexString, 32, 4); | |||
String targetLatitudeHexString = radarDataHelper.byteArrayToHexStringAll(targetLatitudeData); | |||
// 将十六进制字符串转换为long类型的数字 | |||
long targetLatitudeLongValue = Long.parseLong(targetLatitudeHexString, 16); | |||
// 将long类型的数字转换为double类型的数字 | |||
double targetLatitude = (double) targetLatitudeLongValue / 10000000; | |||
System.out.println("目标纬度:" + targetLatitude); | |||
} | |||
} | |||
} | |||
// 如果是交通流量数据 | |||
else if (objectTypeHexString.equals("03")) { | |||
if (dataLength >= 22) { | |||
byte[] dataBytes = radarDataHelper.hexStringToByteArray(segment); | |||
// 22个字节为一组数据 进行分组解析 | |||
for (int i = 0; i < dataBytes.length; i += 22) { | |||
byte[] dataSegment = Arrays.copyOfRange(dataBytes, i, i + 22); | |||
String dataSegmentHexString = radarDataHelper.byteArrayToHexStringAll(dataSegment); | |||
System.out.println("数据:" + dataSegmentHexString); | |||
// 获取统计周期 | |||
byte[] statisticsCycleData = radarDataHelper.extractHexData(dataSegmentHexString, 0, 2); | |||
String statisticsCycleHexString = radarDataHelper.byteArrayToHexStringAll(statisticsCycleData); | |||
// 将16进制数转换为10进制数 | |||
int statisticsCycle = Integer.parseInt(statisticsCycleHexString, 16); | |||
System.out.println("统计周期:" + statisticsCycle); | |||
// 获取实时车流量 | |||
byte[] realTimeTrafficFlowData = radarDataHelper.extractHexData(dataSegmentHexString, 12, 2); | |||
String realTimeTrafficFlowHexString = radarDataHelper.byteArrayToHexStringAll(realTimeTrafficFlowData); | |||
// 将16进制数转换为10进制数 | |||
int realTimeTrafficFlow = Integer.parseInt(realTimeTrafficFlowHexString, 16); | |||
System.out.println("总流量:" + realTimeTrafficFlow); | |||
// 获取平均车速 | |||
byte[] averageSpeedData = radarDataHelper.extractHexData(dataSegmentHexString, 14, 2); | |||
String averageSpeedHexString = radarDataHelper.byteArrayToHexStringAll(averageSpeedData); | |||
double averageSpeed = Integer.parseInt(averageSpeedHexString, 16) * 0.1; | |||
System.out.println("平均速度:" + averageSpeed); | |||
// 获取最大排队长度 | |||
byte[] maxQueueLengthData = radarDataHelper.extractHexData(dataSegmentHexString, 20, 2); | |||
String maxQueueLengthHexString = radarDataHelper.byteArrayToHexStringAll(maxQueueLengthData); | |||
// 将16进制数转换为10进制数 | |||
int maxQueueLength = Integer.parseInt(maxQueueLengthHexString, 16); | |||
System.out.println("最大排队长度:" + maxQueueLength); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
@Test | |||
public void test2(){ | |||
String data = "\\x55\\xaa\\x55\\xbb\\x01\\x88\\x01\\x0121626098\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x18\\x0b\\x08\\x122\\x1e\\x02W\\x0fF\\x0b\\x022\\x14\\x0f\\xff\\xff\\xff\\x07\\x07\\x04~\\x15r\\x00Z\\xf9\\xde\\x16\\x1d\\x00\\n\\x00\\xb4\\xff\\xa6G\\x86\\x90\\xa8\\x11\\xe1\\xb6C\\x0f^\\r\\x022\\x14\\x0f\\xff\\xff\\xff\\x07\\x08\\x01\\xd66\\xe2\\x00\\x00\\xf8\\xd0\\x19\\xdf\\x06\\xa4\\x00\\x00\\x00\\x00G\\x86\\x8d\\xe7\\x11\\xe1\\xd4M\\x0fG\\x0c\\x022\\x14\\x0f\\xff\\xff\\xff\\x07\\x07\\x02N.\\xf4\\x00\\x00\\xf9>\\x18S\\x00\\x00\\xfe\\xf2\\x00\\x00G\\x86\\x8ec\\x11\\xe1\\xcd-\\x0fH\\x0c\\x022\\x14\\x0f\\xff\\xff\\xff\\x07\\x07\\x02\\xa8\\x18V\\x00\\x1e\\xf9 \\x18\\xc0\\x00\\x00\\x00\\x00\\x00\\x00G\\x86\\x8e\\xc1\\x11\\xe1\\xb8\\xdc\\x0f]\\r\\x022\\x14\\x0f\\xff\\xff\\xff\\x07\\x08\\x01\\xa4c\\xe2\\x00\\x00\\xf7\\xea\\x1d\\x1c\\x00\\x00\\x00\\x00\\x00\\x00G\\x86\\x8d\\xb3\\x11\\xe1\\xfc\\xb9\\x0fA\\x0c\\x022\\x14\\x0f\\xff\\xff\\xff\\x07\\x07\\x03H\\x11D\\x00F\\xf9\\x84\\x17]\\xff\\xec\\x00\\x00\\x01\\x0eG\\x86\\x8fg\\x11\\xe1\\xb2\\x82\\x0f@\\x0b\\x022\\x14\\x0f\\xff\\xff\\xff\\x07\\x07\\x04V\\x1e2\\x00x\\xfaL\\x14\\x99\\x00\\x00\\x04\\xb0\\x00\\x00G\\x86\\x90\\x7f\\x11\\xe1\\xbe\\x1f\\x0fP\\x0b\\x022\\x14\\x0f\\xff\\xff\\xff\\x07\\x07\\x04`\\\\\\xbc\\x00F\\xf9\\xa2\\x16\\xf1\\x00\\x00\\x00\\x00\\x00\\x00G\\x86\\x90\\x89\\x11\\xe1\\xf6N\\x0f\\x1d\\x0b\\x022\\x14\\x0f\\xff\\xff\\xff\\x07\\x07\\x03\\xca\\'\\x1a\\xff\\xe2\\xfa8\\x14\\xd1\\x00\\x00\\x01r\\x00\\x00G\\x86\\x8f\\xee\\x11\\xe1\\xc6 \\x0f\\\\\\x0c\\x022\\x14\\x0f\\xff\\xff\\xff\\x07\\x08\\x03>j\"\\x00\\x00\\xf9z\\x17|\\x00\\x00\\x00\\x00\\x00\\x00G\\x86\\x8f\\\\\\x11\\xe2\\x02W\\x55\\xcc\\x55\\xdd'"; | |||
// 替换 \n、\r、\t | |||
String dataString = data.replaceAll("\\\\n", "").replaceAll("\\\\r", "").replaceAll("\\\\t", "").replaceAll(" ", ""); | |||
System.out.println(dataString); | |||
// 以\x为分割符将数据分组 | |||
String[] segments = dataString.split("\\\\x"); | |||
// 对每个字符串做分析 前两个字符作为为一个16进制字节 添加到字节数组中 如果还有剩余的字符 每个字符作为一个字节添加到字节数组中 | |||
LinkedList<byte[]> byteArrayList = new LinkedList<>(); | |||
for (int i = 0; i < segments.length; i++) { | |||
String segment = segments[i]; | |||
if (segment.length() >= 2) { | |||
// 截取前两个字符 | |||
String hexString = segment.substring(0, 2); | |||
// 将两位的字符转换为十六进制的字节 | |||
byte b = hexStringToByte(hexString); | |||
byte[] bytes1 = {b}; | |||
byteArrayList.add(bytes1); | |||
// 截取剩余的字符串 | |||
String remainingString = segment.substring(2); | |||
// 如果是设备编号 | |||
if (remainingString.equals("21626098") || remainingString.equals("21626092") || | |||
remainingString.equals("21626163") || remainingString.equals("21626114") || | |||
remainingString.equals("21626107") || remainingString.equals("21626197")) { | |||
// 每个字符转换为ASIC码 | |||
for (int j = 0; j < remainingString.length(); j++) { | |||
byte asic = charToAsciiByte(remainingString.substring(j, j + 1)); | |||
byte[] bytes2 = new byte[]{asic}; | |||
byteArrayList.add(bytes2); | |||
} | |||
}else { | |||
// 每个字符作为一个字节添加到字节数组列表中 | |||
for (int j = 0; j < remainingString.length(); j++) { | |||
byte asic = charToAsciiByte(remainingString.substring(j, j + 1)); | |||
byte[] bytes2 = new byte[]{asic}; | |||
byteArrayList.add(bytes2); | |||
} | |||
} | |||
}else if (segment.length() == 1){ | |||
byte asic = charToAsciiByte(segment); | |||
byte[] bytes2 = new byte[]{asic}; | |||
byteArrayList.add(bytes2); | |||
}else { | |||
System.out.println("无效的字符串:" + segment); | |||
} | |||
} | |||
// 将字节数组列表转换为字节数组 | |||
byte[] mergedData; | |||
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { | |||
for (byte[] bytes : byteArrayList) { | |||
outputStream.write(bytes); | |||
} | |||
mergedData = outputStream.toByteArray(); | |||
} catch (IOException e) { | |||
throw new RuntimeException("Failed to merge byte arrays", e); | |||
} | |||
StringBuilder hexString = new StringBuilder(); | |||
for (byte b : mergedData) { | |||
String hex = Integer.toHexString(0xFF & b); | |||
if (hex.length() == 1) { | |||
hexString.append('0'); | |||
} | |||
hexString.append(hex); | |||
} | |||
System.out.println(hexString); | |||
} | |||
public static byte charToAsciiByte(String charStr) { | |||
if (charStr == null || charStr.length() != 1) { | |||
throw new IllegalArgumentException("Input string must be one character long."); | |||
} | |||
// 获取字符的ASCII码值 | |||
int asciiValue = charStr.charAt(0); | |||
// 将ASCII码值转换为字节 | |||
return (byte) asciiValue; | |||
} | |||
public static byte hexStringToByte(String hexString) { | |||
if (hexString == null || hexString.length() != 2) { | |||
throw new IllegalArgumentException("Input string must be two characters long."); | |||
} | |||
// 将字符串转换为整数,并指定基数为16 | |||
int intValue = Integer.parseInt(hexString, 16); | |||
// 将整数转换为字节 | |||
return (byte) intValue; | |||
} | |||
@Test | |||
public void test3(){ | |||
// 给定的转义过后的字节数据 | |||
byte[] data = new byte[] { | |||
(byte) 0x55, (byte) 0xaa, (byte) 0x55, (byte) 0xbb, | |||
0x01, (byte) 0x88, 0x01, 0x01, | |||
'2', '1', '6', '2', '6', '0', '9', '8', | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, (byte) 0x18, 0x0b, 0x08, (byte) 0x12, | |||
'2', (byte) 0x1e, 0x02, 'W', (byte) 0x0f, 'F', (byte) 0x0b, 0x02, | |||
'2', (byte) 0x14, (byte) 0x0f, (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x07, 0x07, | |||
0x04, '~', (byte) 0x15, 'r', 0x00, 'Z', (byte) 0xf9, (byte) 0xde, | |||
0x16, (byte) 0x1d, 0x00, 0x0a, 0x00, (byte) 0xb4, (byte) 0xff, (byte) 0xa6, | |||
'G', (byte) 0x86, (byte) 0x90, (byte) 0xa8, (byte) 0x11, (byte) 0xe1, (byte) 0xb6, 'C', | |||
0x0f, '^', 0x0d, 0x02, '2', (byte) 0x14, (byte) 0x0f, (byte) 0xff, | |||
(byte) 0xff, (byte) 0xff, 0x07, 0x08, 0x01, (byte) 0xd6, '6', (byte) 0xe2, | |||
0x00, 0x00, (byte) 0xf8, (byte) 0xd0, (byte) 0x19, (byte) 0xdf, 0x06, (byte) 0xa4, | |||
0x00, 0x00, 0x00, 0x00, 'G', (byte) 0x86, (byte) 0x8d, (byte) 0xe7, | |||
(byte) 0x11, (byte) 0xe1, (byte) 0xd4, 'M', 0x0f, 'G', 0x0c, 0x02, | |||
'2', (byte) 0x14, (byte) 0x0f, (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x07, 0x07, | |||
0x02, 'N', '.', (byte) 0xf4, 0x00, 0x00, (byte) 0xf9, '>', | |||
(byte) 0x18, 'S', 0x00, 0x00, (byte) 0xfe, (byte) 0xf2, 0x00, 0x00, | |||
'G', (byte) 0x86, (byte) 0x8e, 'c', (byte) 0x11, (byte) 0xe1, (byte) 0xcd, '-', | |||
0x0f, 'H', 0x0c, 0x02, '2', (byte) 0x14, (byte) 0x0f, (byte) 0xff, | |||
(byte) 0xff, (byte) 0xff, 0x07, 0x07, 0x02, (byte) 0xa8, 0x18, 'V', | |||
0x00, (byte) 0x1e, (byte) 0xf9, ' ', (byte) 0x18, (byte) 0xc0, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 'G', (byte) 0x86, (byte) 0x8e, (byte) 0xc1, | |||
(byte) 0x11, (byte) 0xe1, (byte) 0xb8, (byte) 0xdc, 0x0f, ']', | |||
0x0d, 0x02, '2', (byte) 0x14, (byte) 0x0f, (byte) 0xff, (byte) 0xff, | |||
(byte) 0xff, 0x07, 0x08, 0x01, (byte) 0xa4, 'c', (byte) 0xe2, 0x00, | |||
0x00, (byte) 0xf7, (byte) 0xea, (byte) 0x1d, (byte) 0x1c, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 'G', (byte) 0x86, (byte) 0x8d, (byte) 0xb3, | |||
(byte) 0x11, (byte) 0xe1, (byte) 0xfc, (byte) 0xb9, 0x0f, 'A', | |||
0x0c, 0x02, '2', (byte) 0x14, (byte) 0x0f, (byte) 0xff, (byte) 0xff, | |||
(byte) 0xff, 0x07, 0x07, 0x03, 'H', 0x11, 'D', 0x00, | |||
'F', (byte) 0xf9, (byte) 0x84, 0x17, ']', (byte) 0xff, (byte) 0xec, 0x00, | |||
0x00, 0x01, 0x0e, 'G', (byte) 0x86, (byte) 0x8f, 'g', (byte) 0x11, | |||
(byte) 0xe1, (byte) 0xb2, (byte) 0x82, 0x0f, '@', 0x0b, 0x02, '2', | |||
(byte) 0x14, (byte) 0x0f, (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x07, 0x07, 0x04, | |||
'V', 0x1e, '2', 0x00, 'x', (byte) 0xfa, 'L', 0x14, | |||
(byte) 0x99, 0x00, 0x00, 0x04, (byte) 0xb0, 0x00, 0x00, 'G', | |||
(byte) 0x86, (byte) 0x90, (byte) 0x7f, (byte) 0x11, (byte) 0xe1, (byte) 0xbe, 0x1f, 0x0f, | |||
'P', 0x0b, 0x02, '2', (byte) 0x14, (byte) 0x0f, (byte) 0xff, (byte) 0xff, | |||
(byte) 0xff, 0x07, 0x07, 0x04, '`', '\\', (byte) 0xbc, 0x00, | |||
'F', (byte) 0xf9, (byte) 0xa2, 0x16, (byte) 0xf1, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 'G', (byte) 0x86, (byte) 0x90, (byte) 0x89, (byte) 0x11, | |||
(byte) 0xe1, (byte) 0xf6, 'N', 0x0f, 0x1d, 0x0b, 0x02, '2', | |||
(byte) 0x14, (byte) 0x0f, (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x07, 0x07, 0x03, | |||
(byte) 0xca, 0x27, 0x1a, (byte) 0xff, (byte) 0xe2, (byte) 0xfa, '8', 0x14, | |||
(byte) 0xd1, 0x00, 0x00, 0x01, 'r', 0x00, 0x00, 'G', | |||
(byte) 0x86, (byte) 0x8f, (byte) 0xee, (byte) 0x11, (byte) 0xe2, 0x02, 'W', 'U', | |||
(byte) 0xcc, 'U', (byte) 0xdd | |||
}; | |||
StringBuilder hexString = new StringBuilder(); | |||
for (byte b : data) { | |||
String hex = Integer.toHexString(0xFF & b); | |||
if (hex.length() == 1) { | |||
hexString.append('0'); | |||
} | |||
hexString.append(hex); | |||
} | |||
System.out.println(hexString.toString()); | |||
} | |||
private String getLocalDateTime(String segment, byte[] timestampData) { | |||
// 第一个字节 -> year | |||
byte year = timestampData[0]; | |||
// 将16进制字节转换为整数 | |||
int yearInt = Integer.parseInt(String.format("%02X", year), 16); | |||
// 创建位掩码来提取第0到7位 | |||
int yearMask = 0xFF; | |||
// 使用位掩码提取第0到第7位 | |||
int extractedYearBits = yearInt & yearMask; | |||
// 数据 0~255 对应 2000~2255 year 获取实际的年份 | |||
int yearValue = extractedYearBits + 2000; | |||
System.out.println("年份:" + yearValue); | |||
// 第二个字节 -> month | |||
byte month = timestampData[1]; | |||
// 将16进制字节转换为整数 | |||
int monthInt = Integer.parseInt(String.format("%02X", month), 16); | |||
// 创建位掩码来提取第0到3位 | |||
// 1~12 对应 1~12 month | |||
int monthMask = 0x0F; | |||
// 使用位掩码提取第0到第3位 | |||
int monthValue = monthInt & monthMask; | |||
System.out.println("月份:" + monthValue); | |||
// 第三个字节 -> day | |||
byte day = timestampData[2]; | |||
// 将16进制字节转换为整数 | |||
int dayInt = Integer.parseInt(String.format("%02X", day), 16); | |||
// 创建位掩码来提取第0到4位 | |||
// 数据 1~31 对应 1~31 day | |||
int dayMask = 0x1F; | |||
// 使用位掩码提取第0到第4位 | |||
// 数据 1~31 对应 1~31 day | |||
int dayValue = dayInt & dayMask; | |||
System.out.println("天:" + dayValue); | |||
// 第四个字节 -> hour | |||
byte hour = timestampData[3]; | |||
// 将16进制字节转换为整数 | |||
int hourInt = Integer.parseInt(String.format("%02X", hour), 16); | |||
// 创建位掩码来提取第0到4位 | |||
int hourMask = 0x1F; | |||
// 使用位掩码提取第0到第4位 | |||
// 数据 0~31 对应 0~23 hour | |||
int hourValue = hourInt & hourMask; | |||
System.out.println("小时:" + hourValue); | |||
// 第五个字节 -> minute | |||
byte minute = timestampData[4]; | |||
// 将16进制字节转换为整数 | |||
int minuteInt = Integer.parseInt(String.format("%02X", minute), 16); | |||
// 创建位掩码来提取第0到5位 | |||
int minuteMask = 0x3F; | |||
// 使用位掩码提取第0到第5位 | |||
// 数据 0~63 对应 0~59 minute | |||
int minuteValue = minuteInt & minuteMask; | |||
System.out.println("分钟:" + minuteValue); | |||
// 第六个字节 -> second | |||
byte second = timestampData[5]; | |||
// 将16进制字节转换为整数 | |||
int secondInt = Integer.parseInt(String.format("%02X", second), 16); | |||
// 创建位掩码来提取第0到5位 | |||
int secondMask = 0x3F; | |||
// 使用位掩码提取第0到第5位 | |||
// 数据 0~63 对应 0~59 second | |||
int secondValue = secondInt & secondMask; | |||
System.out.println("秒:" + secondValue); | |||
// 第7、8个字节-> millisecond | |||
// 把第7和第8个字节拼接成16进制字符串 再转换为整数 | |||
byte[] millisecondData = radarDataHelper.extractHexData(segment, 30, 2); | |||
String millisecondHexString = radarDataHelper.byteArrayToHexString(millisecondData); | |||
int millisecondValue = Integer.parseInt(millisecondHexString, 16); | |||
// 创建位掩码来提取第0到9位 | |||
int millisecondMask = 0x3FF; | |||
// 使用位掩码提取第0到第9位 | |||
millisecondValue = millisecondValue & millisecondMask; | |||
// 数据 0~999 对应 0~999 毫秒 | |||
System.out.println("毫秒:" + millisecondValue); | |||
// 拼接成LocalDateTime 类型的数据 | |||
String localDateTime = yearValue + "-" + monthValue + "-" + dayValue + " " + hourValue + ":" + minuteValue + ":" + secondValue; | |||
return localDateTime; | |||
} | |||
} |