Browse Source

雷达数据对接

master
CMM 2 weeks ago
parent
commit
6458f63810
38 changed files with 2191 additions and 195 deletions
  1. +2
    -2
      ningda-generator/src/main/java/com/ningdatech/generator/config/GeneratorCodeConfig.java
  2. +8
    -1
      ningda-yw-api/src/main/java/com/ningdatech/carapi/gps/task/GpsFullDataPullTask.java
  3. +9
    -2
      ningda-yw-api/src/main/java/com/ningdatech/carapi/gps/task/GpsRealTimeDataPullTask.java
  4. +46
    -22
      ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/client/RadarHandler.java
  5. +68
    -0
      ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/constant/RadarCarTypeEnum.java
  6. +66
    -0
      ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/constant/RadarDataTypeEnum.java
  7. +33
    -6
      ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/controller/RadarController.java
  8. +482
    -0
      ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/helper/RadarDataHelper.java
  9. +233
    -40
      ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/manage/RadarManage.java
  10. +16
    -0
      ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/mapper/RadarOriginalDataMapper.java
  11. +5
    -0
      ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/mapper/RadarOriginalDataMapper.xml
  12. +33
    -2
      ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/model/entity/RadarData.java
  13. +45
    -0
      ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/model/entity/RadarOriginalData.java
  14. +53
    -0
      ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/model/vo/RadarDataVO.java
  15. +38
    -0
      ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/model/vo/RadarObjectDataVO.java
  16. +32
    -0
      ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/model/vo/RadarTrafficFlowDataVO.java
  17. +14
    -26
      ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/properties/RadarDataTaskProperties.java
  18. +17
    -18
      ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/server/MultiRadarServer.java
  19. +16
    -0
      ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/service/IRadarOriginalDataService.java
  20. +20
    -0
      ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/service/impl/RadarOriginalDataServiceImpl.java
  21. +15
    -17
      ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/task/RadarDataTask.java
  22. +240
    -0
      ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/task/RadarRealTimeDataUpdateTask.java
  23. +26
    -4
      ningda-yw-api/src/main/java/com/ningdatech/carapi/road/controller/RoadMonitorController.java
  24. +108
    -7
      ningda-yw-api/src/main/java/com/ningdatech/carapi/road/manage/RoadMonitorManage.java
  25. +17
    -0
      ningda-yw-api/src/main/java/com/ningdatech/carapi/road/model/OverspeedPositionInfo.java
  26. +2
    -8
      ningda-yw-api/src/main/java/com/ningdatech/carapi/road/model/entity/RoadBehaviorAnalysis.java
  27. +26
    -0
      ningda-yw-api/src/main/java/com/ningdatech/carapi/road/model/req/RoadMonitorHandleReq.java
  28. +14
    -1
      ningda-yw-api/src/main/java/com/ningdatech/carapi/road/model/req/RoadMonitorReq.java
  29. +3
    -0
      ningda-yw-api/src/main/java/com/ningdatech/carapi/road/model/req/VideoDownloadReq.java
  30. +5
    -5
      ningda-yw-api/src/main/java/com/ningdatech/carapi/road/model/vo/RoadDangerBehaviorVO.java
  31. +39
    -0
      ningda-yw-api/src/main/java/com/ningdatech/carapi/road/model/vo/VehicleGpsDataVO.java
  32. +15
    -21
      ningda-yw-api/src/main/java/com/ningdatech/carapi/road/task/RoadMonitorDataPullTask.java
  33. +6
    -12
      ningda-yw-api/src/main/resources/application-dev.yml
  34. +2
    -1
      ningda-yw-api/src/main/resources/security/auth-dev.yml
  35. +437
    -0
      ningda-yw-api/src/test/java/com/ningdatech/carapi/radar/RadarTest.java
  36. BIN
      picture/1.jpg
  37. BIN
      picture/2.jpg
  38. BIN
      picture/3.jpg

+ 2
- 2
ningda-generator/src/main/java/com/ningdatech/generator/config/GeneratorCodeConfig.java View File

@@ -22,7 +22,7 @@ public class GeneratorCodeConfig {

private static final String URL = "jdbc:mysql://47.98.125.47:3306/nd-yw-road?serverTimezone=Asia/Shanghai&characterEncoding=utf8&allowPublicKeyRetrieval=true&useSSL=false";
private static final String USER_NAME = "root";
private static final String PASSWORD = "NingdaKeji123!";
private static final String PASSWORD = "Ndkj@1104";

private static void generate(String author, String packageName, String path, String... tableNames) {
FastAutoGenerator.create(URL, USER_NAME, PASSWORD)
@@ -59,7 +59,7 @@ public class GeneratorCodeConfig {
public static void main(String[] args) {
//generate("PoffyZhang", "car.monitor",PATH_ZPF, "nd_vehicle_security_monitor");
//generate("WendyYang", "irs",PATH_YYD, "nd_drivers_license");
generate("CMM", "test",PATH_CMM, "nd_road_behavior_analysis");
generate("CMM", "test",PATH_CMM, "nd_radar_original_data");
}

}

+ 8
- 1
ningda-yw-api/src/main/java/com/ningdatech/carapi/gps/task/GpsFullDataPullTask.java View File

@@ -88,7 +88,14 @@ public class GpsFullDataPullTask {
log.error("下载路径请求失败!");
return;
}
JSONObject object = JSONObject.parseObject(response);
log.info("Response from server: {}", response);
JSONObject object = null;
try {
object = JSONObject.parseObject(response);
} catch (Exception e) {
log.error("数据拉取失败! {}",e.getMessage());
return;
}
log.info("Response from server: {}", object);
String downloadUrl = object.getString("data");
if (StringUtils.isBlank(downloadUrl)){


+ 9
- 2
ningda-yw-api/src/main/java/com/ningdatech/carapi/gps/task/GpsRealTimeDataPullTask.java View File

@@ -99,8 +99,15 @@ public class GpsRealTimeDataPullTask {
log.error("下载路径请求失败!");
return;
}
JSONObject object = JSONObject.parseObject(response);
System.out.println("Response from server: " + response);
log.info("Response from server: {}", response);
JSONObject object = null;
try {
object = JSONObject.parseObject(response);
} catch (Exception e) {
log.error("数据拉取失败! {}",e.getMessage());
return;
}
log.info("Response from server: {}", object);
String downloadUrl = object.getString("data");
if (downloadUrl == null){
log.error("下载路径请求失败!");


+ 46
- 22
ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/client/RadarHandler.java View File

@@ -3,13 +3,24 @@ package com.ningdatech.carapi.radar.client;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import cn.hutool.socket.SocketUtil;
import com.google.common.collect.Lists;
import com.ningdatech.carapi.radar.helper.RadarDataHelper;
import com.ningdatech.carapi.radar.model.entity.RadarData;
import com.ningdatech.carapi.radar.service.IRadarDataService;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
@@ -22,50 +33,63 @@ public class RadarHandler implements Runnable{
private final Socket clientSocket;
private final IRadarDataService radarDataService;
private final String radarIp;
private final RadarDataHelper radarDataHelper;

public RadarHandler(Socket socket, IRadarDataService radarDataService, String radarIp) {
public RadarHandler(Socket socket, IRadarDataService radarDataService, String radarIp, RadarDataHelper radarDataHelper) {
this.clientSocket = socket;
this.radarDataService = radarDataService;
this.radarIp = radarIp;
this.radarDataHelper = radarDataHelper;
}

@Override
public void run() {
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
// 获取输入流以读取雷达发送的数据
try {
DataInputStream dataInputStream = new DataInputStream(clientSocket.getInputStream());
// 从接入数据 开始 持续接收1分钟的数据 接收完成后 关闭连接
// 设置1分钟后关闭连接
executor.schedule(() -> {
try {
clientSocket.close();
} catch (IOException e) {
log.error("Error closing client socket: ", e);
}
}, 1, TimeUnit.MINUTES);

InputStream dataInputStream = clientSocket.getInputStream();
// 开启保活选项
clientSocket.setKeepAlive(true);
byte[] allData = readAllBytesFromDataInputStream(dataInputStream);
// 处理从雷达接收到的数据
if (allData.length > 0) {
String dataString = new String(allData, StandardCharsets.UTF_8);
byte[] buffer = new byte[1024];
int bytesRead;
// 循环读取数据,直到Socket被关闭
while (!clientSocket.isClosed() && (bytesRead = dataInputStream.read(buffer)) != -1) {
// 处理读取到的数据
String dataString = new String(buffer, 0, bytesRead, StandardCharsets.UTF_8);
log.info("Received: {}", dataString);
RadarData radarData = new RadarData();
radarData.setRadarIp(radarIp);
radarData.setData(dataString);
radarData.setArmIp(radarIp);
// 解析数据
radarData.setCreateOn(LocalDateTime.now());
radarData.setUpdateOn(LocalDateTime.now());
radarDataService.save(radarData);
}
// 关闭当前周期的连接
clientSocket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
// 关闭ScheduledExecutorService
executor.shutdown();
try {
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
}
}
}

private byte[] readAllBytesFromDataInputStream(DataInputStream dis) throws IOException {
// 使用 ByteArrayOutputStream 来收集所有读取的字节
try (ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
// 使用一个合适的缓冲区大小
byte[] data = new byte[4096];
int nRead;
while ((nRead = dis.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
return buffer.toByteArray();
}
private void processData(String dataString, RadarData radarData) {
// 校验包头、包尾
}
}

+ 68
- 0
ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/constant/RadarCarTypeEnum.java View File

@@ -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);
}
}

+ 66
- 0
ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/constant/RadarDataTypeEnum.java View File

@@ -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);
}
}

+ 33
- 6
ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/controller/RadarController.java View File

@@ -1,17 +1,20 @@
package com.ningdatech.carapi.radar.controller;

import com.ningdatech.carapi.radar.manage.RadarManage;
import com.ningdatech.carapi.radar.model.vo.RadarObjectDataVO;
import com.ningdatech.carapi.radar.model.vo.RadarTrafficFlowDataVO;
import com.ningdatech.carapi.road.model.req.RoadMonitorReq;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.ningdatech.carapi.common.util.OssUtils;

import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

/**
* <p>
* 前端控制器
@@ -29,9 +32,33 @@ public class RadarController {

private final RadarManage radarManage;

@ApiOperation(value = "雷达监听连接测试", notes = "雷达监听连接测试")
@GetMapping("/get-radar-data")
public String getRadarData() {
return radarManage.getRadarData();
@ApiOperation(value = "客户端-雷达监听连接测试", notes = "客户端-雷达监听连接测试")
@GetMapping("/get-radar-data-as-client")
public String getRadarDataAsClient() {
return radarManage.getRadarDataAsClient();
}

@ApiOperation(value = "服务端-雷达监听连接测试", notes = "服务端-雷达监听连接测试")
@GetMapping("/get-radar-data-as-server")
public String getRadarDataAsServer() {
return radarManage.getRadarDataAsServer();
}

@ApiOperation(value = "工作台雷达交通流量数据", notes = "工作台雷达交通流量数据")
@GetMapping("/workbench/radar-traffic-flow-data")
public RadarTrafficFlowDataVO workbenchRadarTrafficFlowData(RoadMonitorReq req) {
return radarManage.workbenchRadarTrafficFlowData(req);
}

@ApiOperation(value = "道路监控驾驶舱雷达交通流量数据", notes = "道路监控驾驶舱雷达交通流量数据")
@GetMapping("/radar-traffic-flow-data")
public RadarTrafficFlowDataVO radarTrafficFlowData(RoadMonitorReq req) {
return radarManage.radarTrafficFlowData(req);
}

@ApiOperation(value = "道路监控驾驶舱雷达目标轨迹数据", notes = "道路监控驾驶舱雷达目标轨迹数据")
@GetMapping("/radar-object-data")
public List<RadarObjectDataVO> radarObjectData(RoadMonitorReq req) {
return radarManage.radarObjectData(req);
}
}

+ 482
- 0
ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/helper/RadarDataHelper.java View File

@@ -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;
}
}

+ 233
- 40
ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/manage/RadarManage.java View File

@@ -1,16 +1,37 @@
package com.ningdatech.carapi.radar.manage;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.apache.log4j.net.SocketServer;
import cn.hutool.socket.SocketUtil;
import com.ningdatech.carapi.radar.helper.RadarDataHelper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.carapi.radar.constant.RadarCarTypeEnum;
import com.ningdatech.carapi.radar.constant.RadarDataTypeEnum;
import com.ningdatech.carapi.radar.model.entity.RadarData;
import com.ningdatech.carapi.radar.model.vo.RadarObjectDataVO;
import com.ningdatech.carapi.radar.model.vo.RadarTrafficFlowDataVO;
import com.ningdatech.carapi.radar.service.IRadarDataService;
import com.ningdatech.carapi.road.model.req.RoadMonitorReq;

import cn.hutool.core.collection.CollUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@@ -25,47 +46,219 @@ import lombok.extern.slf4j.Slf4j;
@RequiredArgsConstructor
public class RadarManage {

public String getRadarData() {
try {
// 192.168.6.42
ServerSocket serverSocket = new ServerSocket(13000);
// 设置为0表示无限等待,可以根据需要设置超时时间
serverSocket.setSoTimeout(6000);
System.out.println("ServerSocket started on port: " + 13000);
log.info("ServerSocket started on port: {}", 13000);
Socket clientSocket = serverSocket.accept();
System.out.println("Connect Successfully!");
log.info("Connect Successfully!");

DataInputStream dataInputStream = new DataInputStream(clientSocket.getInputStream());
// 开启保活选项
clientSocket.setKeepAlive(true);
byte[] allData;
while (!clientSocket.isClosed()) {
// 使用 ByteArrayOutputStream 来收集所有读取的字节
try (ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
// 使用一个合适的缓冲区大小
byte[] data = new byte[4096];
int nRead;
while ((nRead = dataInputStream.read(data, 0, data.length)) != -1 && buffer.size() < 4096) {
buffer.write(data, 0, nRead);
}
buffer.flush();
allData = buffer.toByteArray();
private final IRadarDataService radarDataService;
private final RadarDataHelper radarDataHelper;

public String getRadarDataAsClient() {
// 盒子ip地址:192.168.6.42 --> 192.168.6.40(黄山隧道义乌来向雷达)、192.168.6.43(黄山隧道义乌去向雷达)
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
// 默认结果
String result = "接收失败!";
String ip = "192.168.6.42";
// String ip = "192.168.2.249";
try (Socket socket = SocketUtil.connect(ip, 13000)) {
log.info("Client connected to server on port: {}", 13000);
InputStream inputStream = socket.getInputStream();
if (Objects.nonNull(inputStream)) {
// 读取输入流中的1024个字节
byte[] buffer = new byte[1024];
if (inputStream.available() > 0) {
int len = inputStream.read(buffer);
// 处理读取到的数据
byte[] data = Arrays.copyOf(buffer, len);
log.info("Received: {}", Arrays.toString(data));
log.info("Connect Successfully!");
}else {
return "流数据为空!";
}
// 处理从雷达接收到的数据
if (allData.length > 0) {
String dataString = new String(allData, StandardCharsets.UTF_8);
System.out.println("Received: " + dataString);
log.info("Received: {}", dataString);
break;
}else{
return result;
}
// 从接入数据 开始 持续接收1分钟的数据 接收完成后 关闭连接
// 设置1分钟后关闭连接
executor.schedule(() -> {
try {
socket.close();
} catch (IOException e) {
log.error("Error closing client socket: ", e);
}
}, 1, TimeUnit.MINUTES);

// 开启保活选项
socket.setKeepAlive(true);
byte[] buffer = new byte[1024];
int bytesRead;
// 循环读取数据,直到Socket被关闭
while (!socket.isClosed() && (bytesRead = inputStream.read(buffer)) != -1) {
// 处理读取到的数据
String dataString = new String(buffer, 0, bytesRead, StandardCharsets.UTF_8);
log.info("Received: {}", dataString);
radarDataHelper.processRadarData(dataString);
}
// 关闭当前周期的连接
clientSocket.close();
// 如果循环正常结束,则修改结果
result = "接收成功!";
} catch (IOException e) {
return e.getMessage();
}finally {
// 关闭ScheduledExecutorService
executor.shutdown();
try {
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
}
}
// 返回最终结果
return result;
}

public String getRadarDataAsServer() {
// 盒子ip地址:192.168.6.42 --> 192.168.6.40(黄山隧道义乌来向雷达)、192.168.6.43(黄山隧道义乌去向雷达)
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
// 默认结果
String result = "接收失败!";
String ip = "192.168.6.42";
//String ip = "192.168.2.249";
try (ServerSocket serverSocket = new ServerSocket()) {
// 设置超时时间
serverSocket.setSoTimeout(60000);
InetSocketAddress address = new InetSocketAddress(ip, 13000);
//InetSocketAddress address = new InetSocketAddress("192.168.2.254", 13000);
serverSocket.bind(address);

// 等待客户端连接
System.out.println("等待客户端连接...");
Socket socket = serverSocket.accept();
System.out.println("客户端已连接!");

// 读取客户端推送的数据
InputStream inputStream = socket.getInputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer)) != -1) {
// 处理接收到的数据
String data = new String(buffer, 0, len);
System.out.println("接收到数据:" + data);
}
result = "接收成功!";
} catch (Exception e) {
log.error("Error closing server socket: ", e);
}
// 返回最终结果
return result;
}


public RadarTrafficFlowDataVO radarTrafficFlowData(RoadMonitorReq req) {
RadarTrafficFlowDataVO vo = new RadarTrafficFlowDataVO();
String region = req.getRegion();
// 雷达区域-ip对应关系
Map<String, String> radarRegionIpMap = getRadarRegionIpMap();
// 判断查询的是哪个设备的数据
String radarIp = radarRegionIpMap.getOrDefault(region, null);
if (StringUtils.isBlank(radarIp)){
throw new BizException("查询区域数据不存在!");
}
// 从雷达数据信息表中获取查询区域最新的一条流量数据
RadarData radarData = radarDataService.getOne(Wrappers.lambdaQuery(RadarData.class)
.eq(RadarData::getRadarIp, radarIp)
.eq(RadarData::getDataType, RadarDataTypeEnum.TRAFFIC_DATA.getCode())
.orderByDesc(RadarData::getUpdateOn)
.last("limit 1"));
if (Objects.nonNull(radarData)) {
BeanUtils.copyProperties(radarData, vo);
}
return vo;
}

private static Map<String, String> getRadarRegionIpMap() {
Map<String, String> radarRegionIpMap = new HashMap<>();
// 黄山隧道东阳去向
radarRegionIpMap.put("1", "192.168.6.45");
// 黄山隧道东阳来向
radarRegionIpMap.put("2", "192.168.6.47");
// 黄山隧道义乌去向
radarRegionIpMap.put("3", "192.168.6.43");
// 黄山隧道义乌来向
radarRegionIpMap.put("4", "192.168.6.40");
// 何里隧道兰溪方向
radarRegionIpMap.put("5", "192.168.10.63");
// 何里隧道义乌方向
radarRegionIpMap.put("6", "192.168.10.60");
return radarRegionIpMap;
}

public List<RadarObjectDataVO> radarObjectData(RoadMonitorReq req) {
String region = req.getRegion();
// 雷达区域-ip对应关系
Map<String, String> radarRegionIpMap = getRadarRegionIpMap();
// 判断查询的是哪个设备的数据
String radarIp = radarRegionIpMap.getOrDefault(region, null);
if (StringUtils.isBlank(radarIp)){
throw new BizException("查询区域数据不存在!");
}
return null;
// 从雷达数据信息表中获取查询区域最近1分钟的目标数据
LocalDateTime startTime = LocalDateTime.now().minusMinutes(1);
List<RadarData> radarDataList = radarDataService.list(Wrappers.lambdaQuery(RadarData.class)
.eq(RadarData::getRadarIp, radarIp)
.eq(RadarData::getDataType, RadarDataTypeEnum.OBJECT_DATA.getCode())
.ge(RadarData::getUpdateOn, startTime)
.orderByDesc(RadarData::getUpdateOn));
// 如果查询不到数据,则返回雷达数据表中最新的100条目标轨迹数据
if (CollUtil.isEmpty(radarDataList)){
radarDataList = radarDataService.list(Wrappers.lambdaQuery(RadarData.class)
.eq(RadarData::getRadarIp, radarIp)
.eq(RadarData::getDataType, RadarDataTypeEnum.OBJECT_DATA.getCode())
.orderByDesc(RadarData::getUpdateOn)
.last("limit 100"));
}
return radarDataList.stream().map(d -> {
RadarObjectDataVO vo = new RadarObjectDataVO();
// TODO 待接入雷达数据后 转换为中文 当前以原始编号返回
vo.setTargetLane(d.getTargetLane());
vo.setCarType(RadarCarTypeEnum.getDescByCode(d.getCarType()));
vo.setTargetSpeed(d.getTargetSpeed());
vo.setTargetLatitude(d.getTargetLatitude());
vo.setTargetLongitude(d.getTargetLongitude());
vo.setUpdateOn(d.getUpdateOn());
return vo;
}).collect(Collectors.toList());
}

public RadarTrafficFlowDataVO workbenchRadarTrafficFlowData(RoadMonitorReq req) {
RadarTrafficFlowDataVO vo = new RadarTrafficFlowDataVO();
// 雷达区域-ip对应关系
List<RadarData> radarDataList = getRadarDataList();
if (CollUtil.isEmpty(radarDataList)){
vo.setRealTimeTrafficFlow("0");
return vo;
}
// 计算实时流量的和
List<String> list = radarDataList.stream().map(RadarData::getRealTimeTrafficFlow).collect(Collectors.toList());
List<BigDecimal> decimals = list.stream().map(d -> BigDecimal.valueOf(Double.parseDouble(d))).collect(Collectors.toList());
BigDecimal total = decimals.stream().reduce(BigDecimal.ZERO, BigDecimal::add).stripTrailingZeros();
vo.setRealTimeTrafficFlow(String.valueOf(total));
return vo;
}

private List<RadarData> getRadarDataList() {
Map<String, String> radarRegionIpMap = getRadarRegionIpMap();
List<String> radarIpList = new ArrayList<>(radarRegionIpMap.values());

// 从雷达数据信息表中获取每个区域最新的一条流量数据
List<RadarData> radarDataList = Lists.newArrayList();
radarIpList.forEach(radarIp -> {
RadarData radarData = radarDataService.getOne(Wrappers.lambdaQuery(RadarData.class)
.eq(RadarData::getRadarIp, radarIp)
.eq(RadarData::getDataType, RadarDataTypeEnum.TRAFFIC_DATA.getCode())
.orderByDesc(RadarData::getUpdateOn)
.last("limit 1"));
if (Objects.nonNull(radarData)) {
radarDataList.add(radarData);
}
});
return radarDataList;
}
}

+ 16
- 0
ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/mapper/RadarOriginalDataMapper.java View File

@@ -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> {

}

+ 5
- 0
ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/mapper/RadarOriginalDataMapper.xml View File

@@ -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>

+ 33
- 2
ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/model/entity/RadarData.java View File

@@ -28,15 +28,46 @@ public class RadarData implements Serializable {
@TableId(value = "id", type = IdType.AUTO)
private Long id;

@ApiModelProperty("雷达推送的数据包")
private String data;
@ApiModelProperty("推送数据的盒子ip")
private String armIp;

@ApiModelProperty("推送数据的雷达ip")
private String radarIp;

@ApiModelProperty("数据类型 1 目标数据、2 流量数据")
private Integer dataType;

@ApiModelProperty("创建时间")
private LocalDateTime createOn;

@ApiModelProperty("更新时间")
private LocalDateTime updateOn;


//---------- 目标轨迹数据 ----------//
@ApiModelProperty("目标所属车道")
private String targetLane;

@ApiModelProperty("车辆类型")
private Integer carType;

@ApiModelProperty("目标速度")
private String targetSpeed;

@ApiModelProperty("目标经度")
private String targetLongitude;

@ApiModelProperty("目标纬度")
private String targetLatitude;

//---------- 交通流量数据 ----------//
@ApiModelProperty("实时车流量")
private String realTimeTrafficFlow;

@ApiModelProperty("平均车速")
private String averageCarSpeed;

@ApiModelProperty("最大排队长度")
private String maxQueueLength;

}

+ 45
- 0
ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/model/entity/RadarOriginalData.java View File

@@ -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;
}

+ 53
- 0
ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/model/vo/RadarDataVO.java View File

@@ -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;
}

+ 38
- 0
ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/model/vo/RadarObjectDataVO.java View File

@@ -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;
}

+ 32
- 0
ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/model/vo/RadarTrafficFlowDataVO.java View File

@@ -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;
}

+ 14
- 26
ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/properties/RadarDataTaskProperties.java View File

@@ -21,31 +21,19 @@ public class RadarDataTaskProperties {
*/
private Boolean enable;

// 雷达设备的IP地址
// 黄山隧道义乌来向
private String hsYwComeRadarIp;
// 黄山隧道义乌去向
private String hsYwGoRadarIp;
// 黄山隧道东阳来向
private String hsDyComeRadarIp;
// 黄山隧道东阳去向
private String hsDyGoRadarIp;
// 何里隧道义乌方向
private String hlYwRadarIp;
// 何里隧道兰溪方向
private String hlLxRadarIp;
// 雷达盒子设备的IP地址
// 黄山隧道-义乌方向
private String hsYwRadarArmIp;
// 黄山隧道-东阳方向
private String hsDyRadarArmIp;
// 何里隧道
private String hlRadarArmIp;

// 雷达设备监听的端口号
// 黄山隧道义乌来向
private Integer hsYwComeRadarPort;
// 黄山隧道义乌去向
private Integer hsYwGoRadarPort;
// 黄山隧道东阳来向
private Integer hsDyComeRadarPort;
// 黄山隧道东阳去向
private Integer hsDyGoRadarPort;
// 何里隧道义乌方向
private Integer hlYwRadarPort;
// 何里隧道兰溪方向
private Integer hlLxRadarPort;
// 雷达盒子监听的端口号
// 黄山隧道-义乌方向
private Integer hsYwRadarArmPort;
// 黄山隧道-东阳方向
private Integer hsDyRadarArmPort;
// 何里隧道
private Integer hlRadarArmPort;
}

+ 17
- 18
ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/server/MultiRadarServer.java View File

@@ -1,18 +1,20 @@
package com.ningdatech.carapi.radar.server;

import com.ningdatech.carapi.radar.client.RadarHandler;
import com.ningdatech.carapi.radar.model.dto.RadarInfoDTO;
import com.ningdatech.carapi.radar.service.IRadarDataService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.*;
import java.net.Socket;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.ningdatech.carapi.radar.client.RadarHandler;
import com.ningdatech.carapi.radar.helper.RadarDataHelper;
import com.ningdatech.carapi.radar.model.dto.RadarInfoDTO;
import com.ningdatech.carapi.radar.service.IRadarDataService;

import cn.hutool.socket.SocketUtil;
import lombok.extern.slf4j.Slf4j;

/**
* @author CMM
* @author CMM
@@ -26,35 +28,32 @@ public class MultiRadarServer {

private final IRadarDataService radarDataService;
private final ExecutorService executorService;
private final RadarDataHelper radarDataHelper;

public MultiRadarServer(IRadarDataService radarDataService,List<RadarInfoDTO> radarInfoList) {
public MultiRadarServer(IRadarDataService radarDataService, List<RadarInfoDTO> radarInfoList, RadarDataHelper radarDataHelper) {
this.radarDataService = radarDataService;
this.executorService = Executors.newFixedThreadPool(radarInfoList.size());
this.radarDataHelper = radarDataHelper;
}


public void startServers(List<RadarInfoDTO> radarInfoList) {
log.info("Radar server started. Waiting for connections...");
for (RadarInfoDTO radarInfo : radarInfoList) {
if (Objects.nonNull(radarInfo.getRadarIp()) && Objects.nonNull(radarInfo.getRadarPort())) {
// 每个设备创建一个线程 建立一个ServerSocket
// 每个设备创建一个线程 建立一个Socket
executorService.execute(() -> {
try (ServerSocket serverSocket = new ServerSocket(radarInfo.getRadarPort(), 600, InetAddress.getByName(radarInfo.getRadarIp()))){
// 设置为0表示无限等待,可以根据需要设置超时时间
serverSocket.setSoTimeout(0);
try (Socket socket = SocketUtil.connect(radarInfo.getRadarIp(), radarInfo.getRadarPort(),60000)){
log.info("ServerSocket started on port: {}", radarInfo.getRadarPort());
while (!Thread.currentThread().isInterrupted()) {
Socket clientSocket = serverSocket.accept();
//log.info("Connected to a radar on port: {}", radarInfo.getRadarPort());
RadarHandler task = new RadarHandler(clientSocket, radarDataService, radarInfo.getRadarIp());
log.info("Connected to a radar on port: {}", radarInfo.getRadarPort());
RadarHandler task = new RadarHandler(socket, radarDataService, radarInfo.getRadarIp(),radarDataHelper);
Thread thread = new Thread(task);
thread.start();
}
} catch (IOException e) {
log.error("Error occurred while accepting client connection: ", e);
log.error("Error occurred while client connection: ", e);
}
});
}
}
}


+ 16
- 0
ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/service/IRadarOriginalDataService.java View File

@@ -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> {

}

+ 20
- 0
ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/service/impl/RadarOriginalDataServiceImpl.java View File

@@ -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 {

}

+ 15
- 17
ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/task/RadarDataTask.java View File

@@ -2,11 +2,10 @@ package com.ningdatech.carapi.radar.task;

import java.util.List;

import javax.annotation.PostConstruct;

import org.apache.commons.compress.utils.Lists;
import org.springframework.stereotype.Component;

import com.ningdatech.carapi.radar.helper.RadarDataHelper;
import com.ningdatech.carapi.radar.model.dto.RadarInfoDTO;
import com.ningdatech.carapi.radar.properties.RadarDataTaskProperties;
import com.ningdatech.carapi.radar.server.MultiRadarServer;
@@ -27,15 +26,16 @@ public class RadarDataTask {

private final RadarDataTaskProperties properties;
private final IRadarDataService radarDataService;
private final RadarDataHelper radarDataHelper;

@PostConstruct
public void initTask() {
if (!properties.getEnable()) {
log.warn("雷达数据同步已关闭……");
return;
}
//initRadarDataTaskByStart();
}
//@PostConstruct
//public void initTask() {
// if (!properties.getEnable()) {
// log.warn("雷达数据同步已关闭……");
// return;
// }
// //initRadarDataTaskByStart();
//}

/**
* 项目重启之后重新初始化雷达接收定时任务
@@ -43,13 +43,11 @@ public class RadarDataTask {
private void initRadarDataTaskByStart() {
log.info("雷达数据接收任务已启动……");
List<RadarInfoDTO> radarInfoList = Lists.newArrayList();
radarInfoList.add(new RadarInfoDTO(properties.getHsYwComeRadarIp(),properties.getHsYwComeRadarPort()));
radarInfoList.add(new RadarInfoDTO(properties.getHsYwGoRadarIp(),properties.getHsYwGoRadarPort()));
radarInfoList.add(new RadarInfoDTO(properties.getHsDyComeRadarIp(),properties.getHsDyComeRadarPort()));
radarInfoList.add(new RadarInfoDTO(properties.getHsDyGoRadarIp(),properties.getHsDyGoRadarPort()));
radarInfoList.add(new RadarInfoDTO(properties.getHlYwRadarIp(),properties.getHlYwRadarPort()));
radarInfoList.add(new RadarInfoDTO(properties.getHlLxRadarIp(),properties.getHlLxRadarPort()));
MultiRadarServer server = new MultiRadarServer(radarDataService,radarInfoList);
// 每个盒子开启一个线程
radarInfoList.add(new RadarInfoDTO(properties.getHsYwRadarArmIp(),properties.getHsYwRadarArmPort()));
radarInfoList.add(new RadarInfoDTO(properties.getHsDyRadarArmIp(),properties.getHsDyRadarArmPort()));
radarInfoList.add(new RadarInfoDTO(properties.getHlRadarArmIp(),properties.getHlRadarArmPort()));
MultiRadarServer server = new MultiRadarServer(radarDataService,radarInfoList,radarDataHelper);
server.startServers(radarInfoList);
}
}

+ 240
- 0
ningda-yw-api/src/main/java/com/ningdatech/carapi/radar/task/RadarRealTimeDataUpdateTask.java View File

@@ -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;
}

}

+ 26
- 4
ningda-yw-api/src/main/java/com/ningdatech/carapi/road/controller/RoadMonitorController.java View File

@@ -1,21 +1,23 @@
package com.ningdatech.carapi.road.controller;

import com.ningdatech.carapi.road.manage.RoadMonitorManage;
import com.ningdatech.carapi.road.model.req.RoadMonitorHandleReq;
import com.ningdatech.carapi.road.model.req.RoadMonitorReq;
import com.ningdatech.carapi.road.model.req.VideoDownloadReq;
import com.ningdatech.carapi.road.model.vo.ComprehensiveSituationVO;
import com.ningdatech.carapi.radar.model.vo.RadarTrafficFlowDataVO;
import com.ningdatech.carapi.road.model.vo.RoadDangerBehaviorVO;
import com.ningdatech.carapi.road.model.vo.VehicleGpsDataVO;
import com.ningdatech.log.annotation.WebLog;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.util.List;

/**
@@ -53,10 +55,30 @@ public class RoadMonitorController {
return roadMonitorManage.getViolationWarnRecordData(req);
}

@ApiOperation(value = "危险行为数据处理", notes = "危险行为数据处理")
@PostMapping("/handle-violation-record")
public String handleViolationRecord(@Valid @RequestBody RoadMonitorHandleReq req) {
return roadMonitorManage.handleViolationRecord(req);
}

@ApiOperation(value = "数据驾驶舱GPS数据", notes = "数据驾驶舱GPS数据")
@GetMapping("/vehicle-gps-data")
public List<VehicleGpsDataVO> getVehicleGpsData(RoadMonitorReq req) {
return roadMonitorManage.getVehicleGpsData(req);
}


@GetMapping("/video/download")
@ApiOperation("视频下载")
@WebLog("视频下载")
public void downloadOperationManual(VideoDownloadReq req, HttpServletResponse response){
public void videoDownload(VideoDownloadReq req, HttpServletResponse response){
roadMonitorManage.videoDownload(req,response);
}

@GetMapping("/picture/download")
@ApiOperation("图片下载")
@WebLog("图片下载")
public void pictureDownload(VideoDownloadReq req, HttpServletResponse response){
roadMonitorManage.pictureDownload(req,response);
}
}

+ 108
- 7
ningda-yw-api/src/main/java/com/ningdatech/carapi/road/manage/RoadMonitorManage.java View File

@@ -3,15 +3,18 @@ package com.ningdatech.carapi.road.manage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import com.ningdatech.carapi.radar.helper.RadarDataHelper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
@@ -20,12 +23,16 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.carapi.common.contants.DefValConstants;
import com.ningdatech.carapi.homepage.entity.model.NdDataAccessGps;
import com.ningdatech.carapi.homepage.service.IDataAccessGpsService;
import com.ningdatech.carapi.road.constant.*;
import com.ningdatech.carapi.road.model.entity.RoadBehaviorAnalysis;
import com.ningdatech.carapi.road.model.req.RoadMonitorHandleReq;
import com.ningdatech.carapi.road.model.req.RoadMonitorReq;
import com.ningdatech.carapi.road.model.req.VideoDownloadReq;
import com.ningdatech.carapi.road.model.vo.ComprehensiveSituationVO;
import com.ningdatech.carapi.road.model.vo.RoadDangerBehaviorVO;
import com.ningdatech.carapi.road.model.vo.VehicleGpsDataVO;
import com.ningdatech.carapi.road.service.IRoadBehaviorAnalysisService;
import com.ningdatech.carapi.scheduler.contants.TaskConstant;

@@ -53,11 +60,13 @@ public class RoadMonitorManage {
private String redisPassword;

private final IRoadBehaviorAnalysisService roadBehaviorAnalysisService;
private final IDataAccessGpsService dataAccessGpsService;
private final RadarDataHelper radarDataHelper;

public ComprehensiveSituationVO getComSitData(RoadMonitorReq req) {
Jedis jedis = new Jedis(redisHost,redisPort);
jedis.auth(redisPassword);
// 隧道类型
// 隧道类型 1 黄山隧道、2 何里隧道、3 两条隧道的汇总(求平均)
Integer tunnel = req.getTunnel();
// 根据传入的隧道类型 实时从缓存中获取算法分析的结果
// 分区域分别计算4个指标的分值 比例按照1:1:1:1 加权计算道路安全指数
@@ -70,6 +79,9 @@ public class RoadMonitorManage {
else if (tunnel == 2) {
List<String> regionList = Lists.newArrayList("5", "6");
return getCollectDataVo(regionList,jedis,tunnel);
} else if (tunnel == 3) {
List<String> regionList = Lists.newArrayList("1", "2", "3", "4", "5", "6");
return getCollectDataVo(regionList, jedis, tunnel);
}
return new ComprehensiveSituationVO();
}
@@ -133,6 +145,9 @@ public class RoadMonitorManage {
} else if (tunnel == 2) {
List<ComprehensiveSituationVO> vos = Lists.newArrayList(hlLxVo, hlYwVo);
vo = averageScores(vos);
} else if (tunnel == 3) {
List<ComprehensiveSituationVO> vos = Lists.newArrayList(hsDyGoVo, hsDyComeVo, hsYwGoVo, hsYwComeVo, hlLxVo, hlYwVo);
vo = averageScores(vos);
}
return vo;
}
@@ -375,21 +390,25 @@ public class RoadMonitorManage {
// 从列表中查询数据
List<RoadBehaviorAnalysis> list = roadBehaviorAnalysisService.list(Wrappers.lambdaQuery(RoadBehaviorAnalysis.class)
.in(RoadBehaviorAnalysis::getRegion, regionList)
.like(StringUtils.isNoneBlank(req.getBehavior()),RoadBehaviorAnalysis::getBehavior, req.getBehavior())
.eq(Objects.nonNull(req.getType()),RoadBehaviorAnalysis::getType, req.getType())
.ge(Objects.nonNull(req.getStartTime()), RoadBehaviorAnalysis::getBehaviorTime, req.getStartTime())
.le(Objects.nonNull(req.getEndTime()), RoadBehaviorAnalysis::getBehaviorTime, req.getEndTime())
.eq(Objects.nonNull(req.getIsWarn()),RoadBehaviorAnalysis::getIsWarn, req.getIsWarn())
.orderByDesc(RoadBehaviorAnalysis::getBehaviorTime));
if (CollUtil.isEmpty(list)){
return Collections.emptyList();
}
return list.stream().map(roadBehaviorAnalysis -> {
RoadDangerBehaviorVO vo = new RoadDangerBehaviorVO();
vo.setId(roadBehaviorAnalysis.getId());
vo.setRecord(roadBehaviorAnalysis.getBehavior());
vo.setRecordCode(roadBehaviorAnalysis.getBehaviorCode());
vo.setRecordTime(roadBehaviorAnalysis.getBehaviorTime());
vo.setRecordType(RoadBehaviorTypeEnum.getDescByCode(roadBehaviorAnalysis.getType()));
vo.setRegion(roadBehaviorAnalysis.getRegion());
vo.setPicturePath(roadBehaviorAnalysis.getPicturePath());
vo.setVideoPath(roadBehaviorAnalysis.getVideoPath());
vo.setLatitude(roadBehaviorAnalysis.getLatitude());
vo.setLongitude(roadBehaviorAnalysis.getLongitude());
vo.setOverspeedPositionInfo(roadBehaviorAnalysis.getOverspeedPositionInfo());
vo.setIsWarn(roadBehaviorAnalysis.getIsWarn());
vo.setIsHandled(roadBehaviorAnalysis.getIsHandled());
return vo;
@@ -464,18 +483,100 @@ public class RoadMonitorManage {
}
return list.stream().map(roadBehaviorAnalysis -> {
RoadDangerBehaviorVO vo = new RoadDangerBehaviorVO();
vo.setId(roadBehaviorAnalysis.getId());
vo.setRecord(roadBehaviorAnalysis.getBehavior());
vo.setRecordCode(roadBehaviorAnalysis.getBehaviorCode());
vo.setRecordTime(roadBehaviorAnalysis.getBehaviorTime());
vo.setRecordType(RoadBehaviorTypeEnum.getDescByCode(roadBehaviorAnalysis.getType()));
vo.setRegion(roadBehaviorAnalysis.getRegion());
vo.setPicturePath(roadBehaviorAnalysis.getPicturePath());
vo.setVideoPath(roadBehaviorAnalysis.getVideoPath());
vo.setLatitude(roadBehaviorAnalysis.getLatitude());
vo.setLongitude(roadBehaviorAnalysis.getLongitude());
vo.setOverspeedPositionInfo(roadBehaviorAnalysis.getOverspeedPositionInfo());
vo.setIsWarn(roadBehaviorAnalysis.getIsWarn());
vo.setIsHandled(roadBehaviorAnalysis.getIsHandled());
return vo;
}).collect(Collectors.toList());
}

@Transactional(rollbackFor = Exception.class)
public String handleViolationRecord(RoadMonitorHandleReq req) {
Long id = req.getId();
// 获取危险行为数据
RoadBehaviorAnalysis roadBehaviorAnalysis = roadBehaviorAnalysisService.getById(id);
if (Objects.isNull(roadBehaviorAnalysis)){
throw new BizException("数据不存在!");
}
if (Boolean.TRUE.equals(roadBehaviorAnalysis.getIsHandled())){
throw new BizException("数据已处理!");
}
// 更新对应数据状态为已处理
roadBehaviorAnalysis.setIsHandled(Boolean.TRUE);
if (roadBehaviorAnalysisService.updateById(roadBehaviorAnalysis)) {
return "处理成功!";
}else {
throw new BizException("处理失败!");
}
}

public List<VehicleGpsDataVO> getVehicleGpsData(RoadMonitorReq req) {
// TODO 暂时不确定隧道的经纬度范围 确定后 需要根据查询的隧道 筛选出对应经纬度范围内的GPS数据 目前返回全部的
// 只查询最新的5分钟内的数据
LocalDateTime startTime = LocalDateTime.now().minusMinutes(5);
LocalDateTime endTime = LocalDateTime.now();
List<NdDataAccessGps> data = dataAccessGpsService.list(Wrappers.lambdaQuery(NdDataAccessGps.class)
.ge(NdDataAccessGps::getUpdateTime, startTime).le(NdDataAccessGps::getUpdateTime, endTime)
.select(NdDataAccessGps::getCarLongitude, NdDataAccessGps::getCarLatitude, NdDataAccessGps::getCarPlate,
NdDataAccessGps::getCarVelocity, NdDataAccessGps::getCarDirection, NdDataAccessGps::getUpdateTime));
log.info("最近5分钟 查询到{}条数据", data.size());
// 如果最近15分钟没有数据(GPS数据不更新了) 取表中最新的5000条数据
if (CollUtil.isEmpty(data)){
data = dataAccessGpsService.list(Wrappers.lambdaQuery(NdDataAccessGps.class)
.select(NdDataAccessGps::getCarLongitude, NdDataAccessGps::getCarLatitude,
NdDataAccessGps::getCarPlate,NdDataAccessGps::getCarVelocity,
NdDataAccessGps::getCarDirection, NdDataAccessGps::getUpdateTime)
.orderByDesc(NdDataAccessGps::getUpdateTime).last("limit 5000"));
}
return data.stream().map(d -> {
VehicleGpsDataVO vo = new VehicleGpsDataVO();
vo.setCarPlate(d.getCarPlate());
vo.setCarVelocity(d.getCarVelocity());
vo.setCarDirection(d.getCarDirection());
vo.setLongitude(d.getCarLongitude());
vo.setLatitude(d.getCarLatitude());
vo.setUpdateTime(d.getUpdateTime());
return vo;
}).collect(Collectors.toList());
}

public void pictureDownload(VideoDownloadReq req, HttpServletResponse response) {

String picturePath = req.getPicturePath();
// 将路径中的文件分割符替换为系统能够识别的分隔符
picturePath = picturePath.replace("\\", File.separator);
picturePath = picturePath.replace("/", File.separator);

File file = new File(picturePath);

// 设置文件名(将显示在下载对话框中)
response.setHeader("Content-Disposition", "inline;");
// 设置文件大小,这样浏览器可以显示下载进度
response.setContentLengthLong(file.length());

// 设置缓存控制头
response.setHeader("Cache-Control", "public, max-age=31536000");

try {
FileInputStream fileInputStream = new FileInputStream(file);
ServletOutputStream servletOutputStream = response.getOutputStream();

byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = fileInputStream.read(buffer)) != -1) {
servletOutputStream.write(buffer, 0, bytesRead);
}
fileInputStream.close();
servletOutputStream.close();
} catch (IOException e) {
throw new BizException("文件下载失败! {}",e);
}
}
}

+ 17
- 0
ningda-yw-api/src/main/java/com/ningdatech/carapi/road/model/OverspeedPositionInfo.java View File

@@ -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;
}

+ 2
- 8
ningda-yw-api/src/main/java/com/ningdatech/carapi/road/model/entity/RoadBehaviorAnalysis.java View File

@@ -46,14 +46,8 @@ public class RoadBehaviorAnalysis implements Serializable {
@ApiModelProperty("异常记录图片路径")
private String picturePath;

@ApiModelProperty("异常记录视频路径")
private String videoPath;

@ApiModelProperty("异常记录经度")
private String longitude;

@ApiModelProperty("异常记录纬度")
private String latitude;
@ApiModelProperty("超速异常记录位置信息")
private String overspeedPositionInfo;

@ApiModelProperty("是否需要预警")
private Boolean isWarn = Boolean.FALSE;


+ 26
- 0
ningda-yw-api/src/main/java/com/ningdatech/carapi/road/model/req/RoadMonitorHandleReq.java View File

@@ -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;
}

+ 14
- 1
ningda-yw-api/src/main/java/com/ningdatech/carapi/road/model/req/RoadMonitorReq.java View File

@@ -1,5 +1,6 @@
package com.ningdatech.carapi.road.model.req;

import java.time.LocalDateTime;
import java.util.List;

import io.swagger.annotations.ApiModelProperty;
@@ -21,7 +22,7 @@ public class RoadMonitorReq {
@ApiModelProperty("区域 1 黄山隧道东阳去向、2 黄山隧道东阳来向、3 黄山隧道义乌去向、4 黄山隧道义乌来向、5 何里隧道兰溪方向、6 何里隧道义乌方向")
private String region;

@ApiModelProperty("隧道 1 黄山隧道、2 何里隧道")
@ApiModelProperty("隧道 1 黄山隧道、2 何里隧道、3 两条隧道汇总")
private Integer tunnel;

@ApiModelProperty("隧道包含的区域 列表")
@@ -30,4 +31,16 @@ public class RoadMonitorReq {

@ApiModelProperty("行为类型 1 驾驶员、2 车辆、3 道路、4 环境")
private Integer type;

@ApiModelProperty("危险行为")
private String behavior;

@ApiModelProperty("开始时间")
private LocalDateTime startTime;

@ApiModelProperty("结束时间")
private LocalDateTime endTime;

@ApiModelProperty("是否预警")
private Boolean isWarn;
}

+ 3
- 0
ningda-yw-api/src/main/java/com/ningdatech/carapi/road/model/req/VideoDownloadReq.java View File

@@ -21,4 +21,7 @@ public class VideoDownloadReq {

@ApiModelProperty("1 2 3 4 5 6")
private String videoOrder;

@ApiModelProperty("图片路径")
private String picturePath;
}

+ 5
- 5
ningda-yw-api/src/main/java/com/ningdatech/carapi/road/model/vo/RoadDangerBehaviorVO.java View File

@@ -18,6 +18,9 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
public class RoadDangerBehaviorVO {

@ApiModelProperty("危险行为ID")
private Long id;

@ApiModelProperty("道路异常分析记录")
private String record;

@@ -39,11 +42,8 @@ public class RoadDangerBehaviorVO {
@ApiModelProperty("异常记录视频路径")
private String videoPath;

@ApiModelProperty("异常记录经度")
private String longitude;

@ApiModelProperty("异常记录纬度")
private String latitude;
@ApiModelProperty("超速异常记录位置信息")
private String overspeedPositionInfo;

@ApiModelProperty("是否需要预警")
private Boolean isWarn = Boolean.FALSE;


+ 39
- 0
ningda-yw-api/src/main/java/com/ningdatech/carapi/road/model/vo/VehicleGpsDataVO.java View File

@@ -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;
}

+ 15
- 21
ningda-yw-api/src/main/java/com/ningdatech/carapi/road/task/RoadMonitorDataPullTask.java View File

@@ -6,6 +6,7 @@ import java.util.stream.Collectors;

import cn.hutool.core.map.MapUtil;
import com.ningdatech.basic.util.StrPool;
import com.ningdatech.carapi.road.model.OverspeedPositionInfo;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
@@ -342,26 +343,9 @@ public class RoadMonitorDataPullTask {
String time = jsonObject.getString("Time");
String region = jsonObject.getString("Region");

JSONArray pictures = jsonObject.getJSONArray("PicturePath");
String picturePath = null;
if (Objects.nonNull(pictures) && CollUtil.isNotEmpty(pictures)) {
picturePath = pictures.getString(0);
}
JSONArray videos = jsonObject.getJSONArray("VideoPath");
String videoPath = null;
if (Objects.nonNull(videos) && CollUtil.isNotEmpty(videos)) {
videoPath = videos.getString(0);
}
String picturePath = jsonObject.getString("PicturePath");
JSONArray longitudes = jsonObject.getJSONArray("Longitude");
String longitude = null;
if (Objects.nonNull(longitudes) && CollUtil.isNotEmpty(longitudes)){
longitude = longitudes.getString(0);
}
JSONArray latitudes = jsonObject.getJSONArray("Latitude");
String latitude = null;
if (Objects.nonNull(latitudes) && CollUtil.isNotEmpty(latitudes)){
latitude = latitudes.getString(0);
}

// 转换为LocalDateTime类型
LocalDateTime createTime = LocalDateTimeUtil.parse(time, NdDateUtils.DEFAULT_DATE_TIME_FORMAT);
@@ -375,9 +359,19 @@ public class RoadMonitorDataPullTask {
roadBehaviorAnalysis.setRegion(region);
roadBehaviorAnalysis.setType(type);
roadBehaviorAnalysis.setPicturePath(picturePath);
roadBehaviorAnalysis.setVideoPath(videoPath);
roadBehaviorAnalysis.setLongitude(longitude);
roadBehaviorAnalysis.setLatitude(latitude);
// 获取超速车辆的经纬度数据
if ((Objects.nonNull(longitudes) && CollUtil.isNotEmpty(longitudes)) && (Objects.nonNull(latitudes) && CollUtil.isNotEmpty(latitudes))){
List<OverspeedPositionInfo> positionInfoList = Lists.newArrayList();
// 按一对一的关系 拼接经纬度
for (int i = 0; i < longitudes.size(); i++) {
OverspeedPositionInfo positionInfo = new OverspeedPositionInfo();
positionInfo.setLongitude(longitudes.getString(i));
positionInfo.setLatitude(latitudes.getString(i));
positionInfoList.add(positionInfo);
}
// 转化为Json字符串
roadBehaviorAnalysis.setOverspeedPositionInfo(JSON.toJSONString(positionInfoList));
}
roadBehaviorAnalysis.setIsWarn(judgeIsWarn(behaviorCode));
// 首次保存的数据默认未处理
roadBehaviorAnalysis.setIsHandled(Boolean.FALSE);


+ 6
- 12
ningda-yw-api/src/main/resources/application-dev.yml View File

@@ -153,16 +153,10 @@ send-urge-warn:
radar-data-task:
enable: false
# 黄山隧道
hs-yw-come-radar-ip: 192.168.6.40
hs-yw-come-radar-port: 10000
hs-yw-go-radar-ip: 192.168.6.43
hs-yw-go-radar-port: 20000
hs-dy-come-radar-ip: 192.168.6.47
hs-dy-come-radar-port: 30000
hs-dy-go-radar-ip: 192.168.6.45
hs-dy-go-radar-port: 40000
hs-yw-radar-arm-ip: 192.168.6.42
hs-yw-radar-arm-port: 13000
hs-dy-radar-arm-ip: 192.168.6.49
hs-dy-radar-arm-port: 13001
# 何里隧道
hl-yw-radar-ip: 192.168.10.60
hl-yw-radar-port: 50000
hl-lx-radar-ip: 192.168.10.63
hl-lx-radar-port: 60000
hl-radar-arm-ip: 192.168.10.62
hl-radar-arm-port: 13002

+ 2
- 1
ningda-yw-api/src/main/resources/security/auth-dev.yml View File

@@ -46,4 +46,5 @@ security:
- /api/car-rpt/**
- /open/api/**
- /radar/**
- /gps/**
- /gps/**
- /road-monitor/**

+ 437
- 0
ningda-yw-api/src/test/java/com/ningdatech/carapi/radar/RadarTest.java View File

@@ -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;
}
}

BIN
picture/1.jpg View File

Before After
Width: 1693  |  Height: 1094  |  Size: 278KB

BIN
picture/2.jpg View File

Before After
Width: 1693  |  Height: 1094  |  Size: 278KB

BIN
picture/3.jpg View File

Before After
Width: 1693  |  Height: 1094  |  Size: 278KB

Loading…
Cancel
Save