@@ -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 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 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) { | private static void generate(String author, String packageName, String path, String... tableNames) { | ||||
FastAutoGenerator.create(URL, USER_NAME, PASSWORD) | FastAutoGenerator.create(URL, USER_NAME, PASSWORD) | ||||
@@ -59,7 +59,7 @@ public class GeneratorCodeConfig { | |||||
public static void main(String[] args) { | public static void main(String[] args) { | ||||
//generate("PoffyZhang", "car.monitor",PATH_ZPF, "nd_vehicle_security_monitor"); | //generate("PoffyZhang", "car.monitor",PATH_ZPF, "nd_vehicle_security_monitor"); | ||||
//generate("WendyYang", "irs",PATH_YYD, "nd_drivers_license"); | //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("下载路径请求失败!"); | log.error("下载路径请求失败!"); | ||||
return; | 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); | log.info("Response from server: {}", object); | ||||
String downloadUrl = object.getString("data"); | String downloadUrl = object.getString("data"); | ||||
if (StringUtils.isBlank(downloadUrl)){ | if (StringUtils.isBlank(downloadUrl)){ | ||||
@@ -99,8 +99,15 @@ public class GpsRealTimeDataPullTask { | |||||
log.error("下载路径请求失败!"); | log.error("下载路径请求失败!"); | ||||
return; | 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"); | String downloadUrl = object.getString("data"); | ||||
if (downloadUrl == null){ | if (downloadUrl == null){ | ||||
log.error("下载路径请求失败!"); | log.error("下载路径请求失败!"); | ||||
@@ -3,13 +3,24 @@ package com.ningdatech.carapi.radar.client; | |||||
import java.io.ByteArrayOutputStream; | import java.io.ByteArrayOutputStream; | ||||
import java.io.DataInputStream; | import java.io.DataInputStream; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | |||||
import java.net.Socket; | import java.net.Socket; | ||||
import java.nio.charset.StandardCharsets; | import java.nio.charset.StandardCharsets; | ||||
import java.time.LocalDateTime; | 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.model.entity.RadarData; | ||||
import com.ningdatech.carapi.radar.service.IRadarDataService; | import com.ningdatech.carapi.radar.service.IRadarDataService; | ||||
import lombok.RequiredArgsConstructor; | |||||
import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||
/** | /** | ||||
@@ -22,50 +33,63 @@ public class RadarHandler implements Runnable{ | |||||
private final Socket clientSocket; | private final Socket clientSocket; | ||||
private final IRadarDataService radarDataService; | private final IRadarDataService radarDataService; | ||||
private final String radarIp; | 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.clientSocket = socket; | ||||
this.radarDataService = radarDataService; | this.radarDataService = radarDataService; | ||||
this.radarIp = radarIp; | this.radarIp = radarIp; | ||||
this.radarDataHelper = radarDataHelper; | |||||
} | } | ||||
@Override | @Override | ||||
public void run() { | public void run() { | ||||
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); | |||||
// 获取输入流以读取雷达发送的数据 | // 获取输入流以读取雷达发送的数据 | ||||
try { | 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); | 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); | log.info("Received: {}", dataString); | ||||
RadarData radarData = new RadarData(); | RadarData radarData = new RadarData(); | ||||
radarData.setRadarIp(radarIp); | |||||
radarData.setData(dataString); | |||||
radarData.setArmIp(radarIp); | |||||
// 解析数据 | |||||
radarData.setCreateOn(LocalDateTime.now()); | radarData.setCreateOn(LocalDateTime.now()); | ||||
radarData.setUpdateOn(LocalDateTime.now()); | radarData.setUpdateOn(LocalDateTime.now()); | ||||
radarDataService.save(radarData); | radarDataService.save(radarData); | ||||
} | } | ||||
// 关闭当前周期的连接 | |||||
clientSocket.close(); | |||||
} catch (IOException e) { | } catch (IOException e) { | ||||
throw new RuntimeException(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; | package com.ningdatech.carapi.radar.controller; | ||||
import com.ningdatech.carapi.radar.manage.RadarManage; | 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.validation.annotation.Validated; | ||||
import org.springframework.web.bind.annotation.GetMapping; | import org.springframework.web.bind.annotation.GetMapping; | ||||
import org.springframework.web.bind.annotation.RequestMapping; | import org.springframework.web.bind.annotation.RequestMapping; | ||||
import org.springframework.web.bind.annotation.RestController; | import org.springframework.web.bind.annotation.RestController; | ||||
import com.ningdatech.carapi.common.util.OssUtils; | |||||
import io.swagger.annotations.ApiOperation; | import io.swagger.annotations.ApiOperation; | ||||
import lombok.RequiredArgsConstructor; | import lombok.RequiredArgsConstructor; | ||||
import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||
import java.util.List; | |||||
/** | /** | ||||
* <p> | * <p> | ||||
* 前端控制器 | * 前端控制器 | ||||
@@ -29,9 +32,33 @@ public class RadarController { | |||||
private final RadarManage radarManage; | 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; | package com.ningdatech.carapi.radar.manage; | ||||
import java.io.ByteArrayOutputStream; | |||||
import java.io.DataInputStream; | |||||
import java.io.IOException; | 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.ServerSocket; | ||||
import java.net.Socket; | import java.net.Socket; | ||||
import java.nio.charset.StandardCharsets; | 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 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.RequiredArgsConstructor; | ||||
import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||
@@ -25,47 +46,219 @@ import lombok.extern.slf4j.Slf4j; | |||||
@RequiredArgsConstructor | @RequiredArgsConstructor | ||||
public class RadarManage { | 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) { | } catch (IOException e) { | ||||
return e.getMessage(); | 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) | @TableId(value = "id", type = IdType.AUTO) | ||||
private Long id; | private Long id; | ||||
@ApiModelProperty("雷达推送的数据包") | |||||
private String data; | |||||
@ApiModelProperty("推送数据的盒子ip") | |||||
private String armIp; | |||||
@ApiModelProperty("推送数据的雷达ip") | @ApiModelProperty("推送数据的雷达ip") | ||||
private String radarIp; | private String radarIp; | ||||
@ApiModelProperty("数据类型 1 目标数据、2 流量数据") | |||||
private Integer dataType; | |||||
@ApiModelProperty("创建时间") | @ApiModelProperty("创建时间") | ||||
private LocalDateTime createOn; | private LocalDateTime createOn; | ||||
@ApiModelProperty("更新时间") | @ApiModelProperty("更新时间") | ||||
private LocalDateTime updateOn; | 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; | 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; | 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.io.IOException; | ||||
import java.net.*; | |||||
import java.net.Socket; | |||||
import java.util.List; | import java.util.List; | ||||
import java.util.Objects; | import java.util.Objects; | ||||
import java.util.concurrent.ExecutorService; | import java.util.concurrent.ExecutorService; | ||||
import java.util.concurrent.Executors; | 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 | ||||
* @author CMM | * @author CMM | ||||
@@ -26,35 +28,32 @@ public class MultiRadarServer { | |||||
private final IRadarDataService radarDataService; | private final IRadarDataService radarDataService; | ||||
private final ExecutorService executorService; | 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.radarDataService = radarDataService; | ||||
this.executorService = Executors.newFixedThreadPool(radarInfoList.size()); | this.executorService = Executors.newFixedThreadPool(radarInfoList.size()); | ||||
this.radarDataHelper = radarDataHelper; | |||||
} | } | ||||
public void startServers(List<RadarInfoDTO> radarInfoList) { | public void startServers(List<RadarInfoDTO> radarInfoList) { | ||||
log.info("Radar server started. Waiting for connections..."); | log.info("Radar server started. Waiting for connections..."); | ||||
for (RadarInfoDTO radarInfo : radarInfoList) { | for (RadarInfoDTO radarInfo : radarInfoList) { | ||||
if (Objects.nonNull(radarInfo.getRadarIp()) && Objects.nonNull(radarInfo.getRadarPort())) { | if (Objects.nonNull(radarInfo.getRadarIp()) && Objects.nonNull(radarInfo.getRadarPort())) { | ||||
// 每个设备创建一个线程 建立一个ServerSocket | |||||
// 每个设备创建一个线程 建立一个Socket | |||||
executorService.execute(() -> { | 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()); | log.info("ServerSocket started on port: {}", radarInfo.getRadarPort()); | ||||
while (!Thread.currentThread().isInterrupted()) { | 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 thread = new Thread(task); | ||||
thread.start(); | thread.start(); | ||||
} | } | ||||
} catch (IOException e) { | } 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 java.util.List; | ||||
import javax.annotation.PostConstruct; | |||||
import org.apache.commons.compress.utils.Lists; | import org.apache.commons.compress.utils.Lists; | ||||
import org.springframework.stereotype.Component; | 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.model.dto.RadarInfoDTO; | ||||
import com.ningdatech.carapi.radar.properties.RadarDataTaskProperties; | import com.ningdatech.carapi.radar.properties.RadarDataTaskProperties; | ||||
import com.ningdatech.carapi.radar.server.MultiRadarServer; | import com.ningdatech.carapi.radar.server.MultiRadarServer; | ||||
@@ -27,15 +26,16 @@ public class RadarDataTask { | |||||
private final RadarDataTaskProperties properties; | private final RadarDataTaskProperties properties; | ||||
private final IRadarDataService radarDataService; | 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() { | private void initRadarDataTaskByStart() { | ||||
log.info("雷达数据接收任务已启动……"); | log.info("雷达数据接收任务已启动……"); | ||||
List<RadarInfoDTO> radarInfoList = Lists.newArrayList(); | 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); | 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; | package com.ningdatech.carapi.road.controller; | ||||
import com.ningdatech.carapi.road.manage.RoadMonitorManage; | 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.RoadMonitorReq; | ||||
import com.ningdatech.carapi.road.model.req.VideoDownloadReq; | import com.ningdatech.carapi.road.model.req.VideoDownloadReq; | ||||
import com.ningdatech.carapi.road.model.vo.ComprehensiveSituationVO; | 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.RoadDangerBehaviorVO; | ||||
import com.ningdatech.carapi.road.model.vo.VehicleGpsDataVO; | |||||
import com.ningdatech.log.annotation.WebLog; | import com.ningdatech.log.annotation.WebLog; | ||||
import org.springframework.validation.annotation.Validated; | 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 io.swagger.annotations.ApiOperation; | ||||
import lombok.RequiredArgsConstructor; | import lombok.RequiredArgsConstructor; | ||||
import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||
import javax.servlet.http.HttpServletResponse; | import javax.servlet.http.HttpServletResponse; | ||||
import javax.validation.Valid; | |||||
import java.util.List; | import java.util.List; | ||||
/** | /** | ||||
@@ -53,10 +55,30 @@ public class RoadMonitorController { | |||||
return roadMonitorManage.getViolationWarnRecordData(req); | 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") | @GetMapping("/video/download") | ||||
@ApiOperation("视频下载") | @ApiOperation("视频下载") | ||||
@WebLog("视频下载") | @WebLog("视频下载") | ||||
public void downloadOperationManual(VideoDownloadReq req, HttpServletResponse response){ | |||||
public void videoDownload(VideoDownloadReq req, HttpServletResponse response){ | |||||
roadMonitorManage.videoDownload(req,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.File; | ||||
import java.io.FileInputStream; | import java.io.FileInputStream; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.time.LocalDateTime; | |||||
import java.util.*; | import java.util.*; | ||||
import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||
import javax.servlet.ServletOutputStream; | import javax.servlet.ServletOutputStream; | ||||
import javax.servlet.http.HttpServletResponse; | import javax.servlet.http.HttpServletResponse; | ||||
import com.ningdatech.carapi.radar.helper.RadarDataHelper; | |||||
import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||
import org.springframework.beans.factory.annotation.Value; | import org.springframework.beans.factory.annotation.Value; | ||||
import org.springframework.stereotype.Component; | import org.springframework.stereotype.Component; | ||||
import org.springframework.transaction.annotation.Transactional; | |||||
import com.alibaba.fastjson.JSON; | import com.alibaba.fastjson.JSON; | ||||
import com.alibaba.fastjson.JSONArray; | import com.alibaba.fastjson.JSONArray; | ||||
@@ -20,12 +23,16 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; | |||||
import com.google.common.collect.Lists; | import com.google.common.collect.Lists; | ||||
import com.ningdatech.basic.exception.BizException; | import com.ningdatech.basic.exception.BizException; | ||||
import com.ningdatech.carapi.common.contants.DefValConstants; | 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.constant.*; | ||||
import com.ningdatech.carapi.road.model.entity.RoadBehaviorAnalysis; | 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.RoadMonitorReq; | ||||
import com.ningdatech.carapi.road.model.req.VideoDownloadReq; | import com.ningdatech.carapi.road.model.req.VideoDownloadReq; | ||||
import com.ningdatech.carapi.road.model.vo.ComprehensiveSituationVO; | import com.ningdatech.carapi.road.model.vo.ComprehensiveSituationVO; | ||||
import com.ningdatech.carapi.road.model.vo.RoadDangerBehaviorVO; | 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.road.service.IRoadBehaviorAnalysisService; | ||||
import com.ningdatech.carapi.scheduler.contants.TaskConstant; | import com.ningdatech.carapi.scheduler.contants.TaskConstant; | ||||
@@ -53,11 +60,13 @@ public class RoadMonitorManage { | |||||
private String redisPassword; | private String redisPassword; | ||||
private final IRoadBehaviorAnalysisService roadBehaviorAnalysisService; | private final IRoadBehaviorAnalysisService roadBehaviorAnalysisService; | ||||
private final IDataAccessGpsService dataAccessGpsService; | |||||
private final RadarDataHelper radarDataHelper; | |||||
public ComprehensiveSituationVO getComSitData(RoadMonitorReq req) { | public ComprehensiveSituationVO getComSitData(RoadMonitorReq req) { | ||||
Jedis jedis = new Jedis(redisHost,redisPort); | Jedis jedis = new Jedis(redisHost,redisPort); | ||||
jedis.auth(redisPassword); | jedis.auth(redisPassword); | ||||
// 隧道类型 | |||||
// 隧道类型 1 黄山隧道、2 何里隧道、3 两条隧道的汇总(求平均) | |||||
Integer tunnel = req.getTunnel(); | Integer tunnel = req.getTunnel(); | ||||
// 根据传入的隧道类型 实时从缓存中获取算法分析的结果 | // 根据传入的隧道类型 实时从缓存中获取算法分析的结果 | ||||
// 分区域分别计算4个指标的分值 比例按照1:1:1:1 加权计算道路安全指数 | // 分区域分别计算4个指标的分值 比例按照1:1:1:1 加权计算道路安全指数 | ||||
@@ -70,6 +79,9 @@ public class RoadMonitorManage { | |||||
else if (tunnel == 2) { | else if (tunnel == 2) { | ||||
List<String> regionList = Lists.newArrayList("5", "6"); | List<String> regionList = Lists.newArrayList("5", "6"); | ||||
return getCollectDataVo(regionList,jedis,tunnel); | 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(); | return new ComprehensiveSituationVO(); | ||||
} | } | ||||
@@ -133,6 +145,9 @@ public class RoadMonitorManage { | |||||
} else if (tunnel == 2) { | } else if (tunnel == 2) { | ||||
List<ComprehensiveSituationVO> vos = Lists.newArrayList(hlLxVo, hlYwVo); | List<ComprehensiveSituationVO> vos = Lists.newArrayList(hlLxVo, hlYwVo); | ||||
vo = averageScores(vos); | vo = averageScores(vos); | ||||
} else if (tunnel == 3) { | |||||
List<ComprehensiveSituationVO> vos = Lists.newArrayList(hsDyGoVo, hsDyComeVo, hsYwGoVo, hsYwComeVo, hlLxVo, hlYwVo); | |||||
vo = averageScores(vos); | |||||
} | } | ||||
return vo; | return vo; | ||||
} | } | ||||
@@ -375,21 +390,25 @@ public class RoadMonitorManage { | |||||
// 从列表中查询数据 | // 从列表中查询数据 | ||||
List<RoadBehaviorAnalysis> list = roadBehaviorAnalysisService.list(Wrappers.lambdaQuery(RoadBehaviorAnalysis.class) | List<RoadBehaviorAnalysis> list = roadBehaviorAnalysisService.list(Wrappers.lambdaQuery(RoadBehaviorAnalysis.class) | ||||
.in(RoadBehaviorAnalysis::getRegion, regionList) | .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)); | .orderByDesc(RoadBehaviorAnalysis::getBehaviorTime)); | ||||
if (CollUtil.isEmpty(list)){ | if (CollUtil.isEmpty(list)){ | ||||
return Collections.emptyList(); | return Collections.emptyList(); | ||||
} | } | ||||
return list.stream().map(roadBehaviorAnalysis -> { | return list.stream().map(roadBehaviorAnalysis -> { | ||||
RoadDangerBehaviorVO vo = new RoadDangerBehaviorVO(); | RoadDangerBehaviorVO vo = new RoadDangerBehaviorVO(); | ||||
vo.setId(roadBehaviorAnalysis.getId()); | |||||
vo.setRecord(roadBehaviorAnalysis.getBehavior()); | vo.setRecord(roadBehaviorAnalysis.getBehavior()); | ||||
vo.setRecordCode(roadBehaviorAnalysis.getBehaviorCode()); | vo.setRecordCode(roadBehaviorAnalysis.getBehaviorCode()); | ||||
vo.setRecordTime(roadBehaviorAnalysis.getBehaviorTime()); | vo.setRecordTime(roadBehaviorAnalysis.getBehaviorTime()); | ||||
vo.setRecordType(RoadBehaviorTypeEnum.getDescByCode(roadBehaviorAnalysis.getType())); | vo.setRecordType(RoadBehaviorTypeEnum.getDescByCode(roadBehaviorAnalysis.getType())); | ||||
vo.setRegion(roadBehaviorAnalysis.getRegion()); | vo.setRegion(roadBehaviorAnalysis.getRegion()); | ||||
vo.setPicturePath(roadBehaviorAnalysis.getPicturePath()); | 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.setIsWarn(roadBehaviorAnalysis.getIsWarn()); | ||||
vo.setIsHandled(roadBehaviorAnalysis.getIsHandled()); | vo.setIsHandled(roadBehaviorAnalysis.getIsHandled()); | ||||
return vo; | return vo; | ||||
@@ -464,18 +483,100 @@ public class RoadMonitorManage { | |||||
} | } | ||||
return list.stream().map(roadBehaviorAnalysis -> { | return list.stream().map(roadBehaviorAnalysis -> { | ||||
RoadDangerBehaviorVO vo = new RoadDangerBehaviorVO(); | RoadDangerBehaviorVO vo = new RoadDangerBehaviorVO(); | ||||
vo.setId(roadBehaviorAnalysis.getId()); | |||||
vo.setRecord(roadBehaviorAnalysis.getBehavior()); | vo.setRecord(roadBehaviorAnalysis.getBehavior()); | ||||
vo.setRecordCode(roadBehaviorAnalysis.getBehaviorCode()); | vo.setRecordCode(roadBehaviorAnalysis.getBehaviorCode()); | ||||
vo.setRecordTime(roadBehaviorAnalysis.getBehaviorTime()); | vo.setRecordTime(roadBehaviorAnalysis.getBehaviorTime()); | ||||
vo.setRecordType(RoadBehaviorTypeEnum.getDescByCode(roadBehaviorAnalysis.getType())); | vo.setRecordType(RoadBehaviorTypeEnum.getDescByCode(roadBehaviorAnalysis.getType())); | ||||
vo.setRegion(roadBehaviorAnalysis.getRegion()); | vo.setRegion(roadBehaviorAnalysis.getRegion()); | ||||
vo.setPicturePath(roadBehaviorAnalysis.getPicturePath()); | 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.setIsWarn(roadBehaviorAnalysis.getIsWarn()); | ||||
vo.setIsHandled(roadBehaviorAnalysis.getIsHandled()); | vo.setIsHandled(roadBehaviorAnalysis.getIsHandled()); | ||||
return vo; | return vo; | ||||
}).collect(Collectors.toList()); | }).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("异常记录图片路径") | @ApiModelProperty("异常记录图片路径") | ||||
private String picturePath; | private String picturePath; | ||||
@ApiModelProperty("异常记录视频路径") | |||||
private String videoPath; | |||||
@ApiModelProperty("异常记录经度") | |||||
private String longitude; | |||||
@ApiModelProperty("异常记录纬度") | |||||
private String latitude; | |||||
@ApiModelProperty("超速异常记录位置信息") | |||||
private String overspeedPositionInfo; | |||||
@ApiModelProperty("是否需要预警") | @ApiModelProperty("是否需要预警") | ||||
private Boolean isWarn = Boolean.FALSE; | 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; | package com.ningdatech.carapi.road.model.req; | ||||
import java.time.LocalDateTime; | |||||
import java.util.List; | import java.util.List; | ||||
import io.swagger.annotations.ApiModelProperty; | import io.swagger.annotations.ApiModelProperty; | ||||
@@ -21,7 +22,7 @@ public class RoadMonitorReq { | |||||
@ApiModelProperty("区域 1 黄山隧道东阳去向、2 黄山隧道东阳来向、3 黄山隧道义乌去向、4 黄山隧道义乌来向、5 何里隧道兰溪方向、6 何里隧道义乌方向") | @ApiModelProperty("区域 1 黄山隧道东阳去向、2 黄山隧道东阳来向、3 黄山隧道义乌去向、4 黄山隧道义乌来向、5 何里隧道兰溪方向、6 何里隧道义乌方向") | ||||
private String region; | private String region; | ||||
@ApiModelProperty("隧道 1 黄山隧道、2 何里隧道") | |||||
@ApiModelProperty("隧道 1 黄山隧道、2 何里隧道、3 两条隧道汇总") | |||||
private Integer tunnel; | private Integer tunnel; | ||||
@ApiModelProperty("隧道包含的区域 列表") | @ApiModelProperty("隧道包含的区域 列表") | ||||
@@ -30,4 +31,16 @@ public class RoadMonitorReq { | |||||
@ApiModelProperty("行为类型 1 驾驶员、2 车辆、3 道路、4 环境") | @ApiModelProperty("行为类型 1 驾驶员、2 车辆、3 道路、4 环境") | ||||
private Integer type; | 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") | @ApiModelProperty("1 2 3 4 5 6") | ||||
private String videoOrder; | private String videoOrder; | ||||
@ApiModelProperty("图片路径") | |||||
private String picturePath; | |||||
} | } |
@@ -18,6 +18,9 @@ import lombok.NoArgsConstructor; | |||||
@AllArgsConstructor | @AllArgsConstructor | ||||
public class RoadDangerBehaviorVO { | public class RoadDangerBehaviorVO { | ||||
@ApiModelProperty("危险行为ID") | |||||
private Long id; | |||||
@ApiModelProperty("道路异常分析记录") | @ApiModelProperty("道路异常分析记录") | ||||
private String record; | private String record; | ||||
@@ -39,11 +42,8 @@ public class RoadDangerBehaviorVO { | |||||
@ApiModelProperty("异常记录视频路径") | @ApiModelProperty("异常记录视频路径") | ||||
private String videoPath; | private String videoPath; | ||||
@ApiModelProperty("异常记录经度") | |||||
private String longitude; | |||||
@ApiModelProperty("异常记录纬度") | |||||
private String latitude; | |||||
@ApiModelProperty("超速异常记录位置信息") | |||||
private String overspeedPositionInfo; | |||||
@ApiModelProperty("是否需要预警") | @ApiModelProperty("是否需要预警") | ||||
private Boolean isWarn = Boolean.FALSE; | 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 cn.hutool.core.map.MapUtil; | ||||
import com.ningdatech.basic.util.StrPool; | import com.ningdatech.basic.util.StrPool; | ||||
import com.ningdatech.carapi.road.model.OverspeedPositionInfo; | |||||
import org.apache.commons.compress.utils.Lists; | import org.apache.commons.compress.utils.Lists; | ||||
import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||
import org.springframework.beans.factory.annotation.Value; | import org.springframework.beans.factory.annotation.Value; | ||||
@@ -342,26 +343,9 @@ public class RoadMonitorDataPullTask { | |||||
String time = jsonObject.getString("Time"); | String time = jsonObject.getString("Time"); | ||||
String region = jsonObject.getString("Region"); | 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"); | JSONArray longitudes = jsonObject.getJSONArray("Longitude"); | ||||
String longitude = null; | |||||
if (Objects.nonNull(longitudes) && CollUtil.isNotEmpty(longitudes)){ | |||||
longitude = longitudes.getString(0); | |||||
} | |||||
JSONArray latitudes = jsonObject.getJSONArray("Latitude"); | JSONArray latitudes = jsonObject.getJSONArray("Latitude"); | ||||
String latitude = null; | |||||
if (Objects.nonNull(latitudes) && CollUtil.isNotEmpty(latitudes)){ | |||||
latitude = latitudes.getString(0); | |||||
} | |||||
// 转换为LocalDateTime类型 | // 转换为LocalDateTime类型 | ||||
LocalDateTime createTime = LocalDateTimeUtil.parse(time, NdDateUtils.DEFAULT_DATE_TIME_FORMAT); | LocalDateTime createTime = LocalDateTimeUtil.parse(time, NdDateUtils.DEFAULT_DATE_TIME_FORMAT); | ||||
@@ -375,9 +359,19 @@ public class RoadMonitorDataPullTask { | |||||
roadBehaviorAnalysis.setRegion(region); | roadBehaviorAnalysis.setRegion(region); | ||||
roadBehaviorAnalysis.setType(type); | roadBehaviorAnalysis.setType(type); | ||||
roadBehaviorAnalysis.setPicturePath(picturePath); | 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.setIsWarn(judgeIsWarn(behaviorCode)); | ||||
// 首次保存的数据默认未处理 | // 首次保存的数据默认未处理 | ||||
roadBehaviorAnalysis.setIsHandled(Boolean.FALSE); | roadBehaviorAnalysis.setIsHandled(Boolean.FALSE); | ||||
@@ -153,16 +153,10 @@ send-urge-warn: | |||||
radar-data-task: | radar-data-task: | ||||
enable: false | 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/** | - /api/car-rpt/** | ||||
- /open/api/** | - /open/api/** | ||||
- /radar/** | - /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; | |||||
} | |||||
} |