简化了路径冲突检测的数据库表结构,优化了告警逻辑和告警类型及消息结构
This commit is contained in:
parent
f5d830b713
commit
3f323361ef
@ -1 +1 @@
|
||||
0.3.4
|
||||
0.3.5
|
||||
49
changelog.md
49
changelog.md
@ -5,6 +5,55 @@
|
||||
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/)。
|
||||
版本规范基于 [Semantic Versioning](https://semver.org/lang/zh-CN/)。
|
||||
|
||||
## [0.3.5] - 2025-07-12
|
||||
|
||||
### ✨ **新功能与优化:路径冲突检测增强与告警体系精细化**
|
||||
|
||||
- **数据库与Java实体类同步**:
|
||||
- 将所有引用其他表的 `id` 字段类型改为 `Long`,确保数据一致性。
|
||||
- 删除了 `transport_routes`、`object_route_assignments` 和 `conflict_alert_logs` 表中的特定冗余 `id` 字段。
|
||||
- 根据需求,`object_name` 字段设置为 `NOT NULL`。
|
||||
|
||||
- **新增车辆运动类型**:
|
||||
- 在 `route_type` 和 `object_type` 字段的 `CHECK` 约束中添加了 `'NORMAL_VEHICLE'`(普通车)类型,并在示例数据中更新。
|
||||
|
||||
- **告警类型与级别精简**:
|
||||
- `ConflictAlertLog.AlertType` 枚举值精简为 `CONFLICT_WARNING`(冲突预警)和 `CONFLICT_ALERT`(冲突告警)。
|
||||
- `AlertLevel` 枚举中彻底移除了 `INFO` 级别,所有相关代码逻辑已同步移除,确保只处理 `WARNING`、`CRITICAL` 和 `EMERGENCY` 级别的告警。
|
||||
- `alertLevel` 为 `CRITICAL` 和 `EMERGENCY` 的告警统一映射为 `CONFLICT_ALERT` 类型。
|
||||
|
||||
- **距离计算与消息精度**:
|
||||
- 确认 `PathConflictDetectionService.java` 中的距离计算单位为“度”,并注入 `CoordinateSystemService` 将坐标转换为局部米制坐标进行计算。
|
||||
- 前端消息中 `object1Distance` 和 `object2Distance` 的值已四舍五入保留两位小数。
|
||||
- `PositionUpdatePayload` 中的 `speed` 字段已进行 `BigDecimal` 四舍五入处理,保留两位小数。
|
||||
|
||||
- **冲突告警逻辑优化**:
|
||||
- `detectConflictBetweenObjects` 方法修改为**只从无人车 (`UNMANNED_VEHICLE`) 角度检测冲突**,其他类型 (`AIRCRAFT`, `SPECIAL_VEHICLE`, `NORMAL_VEHICLE`) 之间不检测冲突。
|
||||
- `evaluateAlertLevel` 调整逻辑,在判断 `WARNING` 或 `CRITICAL` 级别时,只关注 `UNMANNED_VEHICLE` 距离冲突点的距离。如果双方都不是无人车,则不生成告警(`Optional.empty()`)。
|
||||
- `ConflictAlertEventListener.java` 中的 `handleConflictAlert` 方法添加过滤逻辑,确保只有 `WARNING`、`CRITICAL` 或 `EMERGENCY` 级别的告警才发送 WebSocket 消息和车辆指令。
|
||||
|
||||
- **代码健壮性与可维护性提升**:
|
||||
- 修复了 `PathConflictDetectionService.java` 在处理重叠线段时可能生成空交点导致的 `NullPointerException`。
|
||||
- 解决了 `PathConflictAlertWebSocketEvent.java` 中 `getAlertType()` 和 `getAlertLevel()` 方法的类型不兼容问题,统一返回 `String` 类型。
|
||||
- 修正了 `ConflictAlertLog.java` 中 `alertType` 字段的注释不一致问题。
|
||||
|
||||
### 📋 影响文件
|
||||
|
||||
- `sql/create_path_conflict_detection_tables.sql`:数据库表结构、约束、示例数据更新。
|
||||
- `qaup-collision/src/main/java/com/qaup/collision/common/model/MovingObjectType.java`:新增枚举类型。
|
||||
- `qaup-collision/src/main/java/com/qaup/collision/datacollector/service/DataCollectorService.java`:速度四舍五入、调用 `convertToVehicleLocation`。
|
||||
- `qaup-collision/src/main/java/com/qaup/collision/pathconflict/event/ConflictAlertEventListener.java`:告警过滤、距离四舍五入。
|
||||
- `qaup-collision/src/main/java/com/qaup/collision/pathconflict/model/dto/ConflictAlertEvent.java`:字段类型更新。
|
||||
- `qaup-collision/src/main/java/com/qaup/collision/pathconflict/model/entity/ConflictAlertLog.java`:告警类型/级别定义、字段注释更新。
|
||||
- `qaup-collision/src/main/java/com/qaup/collision/pathconflict/model/entity/ObjectRouteAssignment.java`:字段类型、非空约束、枚举更新。
|
||||
- `qaup-collision/src/main/java/com/qaup/collision/pathconflict/model/entity/TransportRoute.java`:字段删除、枚举更新。
|
||||
- `qaup-collision/src/main/java/com/qaup/collision/pathconflict/repository/ObjectRouteAssignmentRepository.java`:查询方法更新。
|
||||
- `qaup-collision/src/main/java/com/qaup/collision/pathconflict/repository/TransportRouteRepository.java`:查询方法更新。
|
||||
- `qaup-collision/src/main/java/com/qaup/collision/pathconflict/service/PathConflictDetectionService.java`:核心逻辑、距离计算、告警评估更新。
|
||||
- `qaup-collision/src/main/java/com/qaup/collision/websocket/message/PathConflictAlertMessage.java`:字段类型更新。
|
||||
- `qaup-collision/src/main/java/com/qaup/collision/websocket/event/PathConflictAlertWebSocketEvent.java`:getter 返回类型更新。
|
||||
- `VERSION.md`:版本号更新为 `0.3.5`。
|
||||
|
||||
## [0.3.4] - 2025-07-11
|
||||
|
||||
### 🚀 **核心功能增强:路径冲突与违规检测全面优化**
|
||||
|
||||
@ -53,7 +53,8 @@ public class MovingObject {
|
||||
public enum ObjectType {
|
||||
UNMANNED_VEHICLE, // 无人车
|
||||
AIRCRAFT, // 航空器
|
||||
SPECIAL_VEHICLE // 特种车辆(如机场车辆)
|
||||
SPECIAL_VEHICLE, // 特种车辆(如机场车辆)
|
||||
NORMAL_VEHICLE // 普通车辆
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -7,6 +7,8 @@ public enum MovingObjectType {
|
||||
AIRPORT_VEHICLE,
|
||||
// 特勤车辆(具有特殊权限)- 特勤车辆如警车、消防车、救护车等
|
||||
SPECIAL_VEHICLE,
|
||||
// 普通车(普通车辆)- 新增的普通车辆类型
|
||||
NORMAL_VEHICLE,
|
||||
// 无人车(可控)- 无人车控制接口
|
||||
UNMANNED_VEHICLE,
|
||||
// 未知类型
|
||||
|
||||
@ -34,6 +34,9 @@ import org.locationtech.jts.geom.GeometryFactory;
|
||||
import org.locationtech.jts.geom.Point;
|
||||
import org.locationtech.jts.geom.PrecisionModel;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
/**
|
||||
* 数据采集服务 - 重构版本
|
||||
*
|
||||
@ -171,7 +174,7 @@ public class DataCollectorService {
|
||||
.objectType(MovingObject.ObjectType.AIRCRAFT.name())
|
||||
.position(positionPayload)
|
||||
.heading(movingObject.getCurrentHeading())
|
||||
.speed(movingObject.getCurrentSpeed())
|
||||
.speed(new BigDecimal(movingObject.getCurrentSpeed()).setScale(2, RoundingMode.HALF_UP).doubleValue()) // 对速度进行四舍五入
|
||||
.timestamp(currentTime) // 更新为Long类型时间戳
|
||||
.build();
|
||||
|
||||
@ -294,7 +297,7 @@ public class DataCollectorService {
|
||||
.objectType(MovingObject.ObjectType.SPECIAL_VEHICLE.name())
|
||||
.position(positionPayload)
|
||||
.heading(movingObject.getCurrentHeading())
|
||||
.speed(movingObject.getCurrentSpeed())
|
||||
.speed(new BigDecimal(movingObject.getCurrentSpeed()).setScale(2, RoundingMode.HALF_UP).doubleValue()) // 对速度进行四舍五入
|
||||
.timestamp(currentTime)
|
||||
.build();
|
||||
|
||||
@ -409,6 +412,12 @@ public class DataCollectorService {
|
||||
// unmannedVehicleLocations.add(createTemporaryVehicleLocationForDetection(movingObject)); // 移除此行
|
||||
// 将最新数据更新到缓存
|
||||
activeMovingObjectsCache.put(movingObject.getObjectId(), movingObject);
|
||||
|
||||
// 将UnmannedVehicle转换为VehicleLocation并保存到数据库
|
||||
VehicleLocation convertedLocation = convertToVehicleLocation(unmannedVehicle);
|
||||
if (convertedLocation != null) {
|
||||
vehicleDataPersistenceService.saveUnmannedVehicleLocation(convertedLocation);
|
||||
}
|
||||
|
||||
// 创建位置更新消息负载(WebSocket推送)
|
||||
PositionUpdatePayload.Position positionPayload = PositionUpdatePayload.Position.builder()
|
||||
@ -421,7 +430,7 @@ public class DataCollectorService {
|
||||
.objectType(MovingObject.ObjectType.UNMANNED_VEHICLE.name())
|
||||
.position(positionPayload)
|
||||
.heading(movingObject.getCurrentHeading())
|
||||
.speed(movingObject.getCurrentSpeed())
|
||||
.speed(new BigDecimal(movingObject.getCurrentSpeed()).setScale(2, RoundingMode.HALF_UP).doubleValue()) // 对速度进行四舍五入
|
||||
.timestamp(currentTime)
|
||||
.build();
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import com.qaup.collision.common.service.VehicleLocationService;
|
||||
import com.qaup.collision.datacollector.model.entity.VehicleCommandEntity;
|
||||
import com.qaup.collision.datacollector.repository.VehicleCommandRepository;
|
||||
import com.qaup.collision.websocket.event.PositionUpdateEvent;
|
||||
import com.qaup.collision.websocket.message.PositionUpdatePayload;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -67,7 +68,20 @@ public class VehicleDataPersistenceService {
|
||||
|
||||
// 数据保存完成后发布WebSocket位置更新事件
|
||||
try {
|
||||
eventPublisher.publishEvent(new PositionUpdateEvent(savedLocation));
|
||||
// 将VehicleLocation转换为PositionUpdatePayload
|
||||
PositionUpdatePayload positionPayload = PositionUpdatePayload.builder()
|
||||
.objectId(String.valueOf(savedLocation.getVehicleId())) // 使用vehicleId作为objectId
|
||||
.objectType(savedLocation.getVehicleType().name())
|
||||
.position(PositionUpdatePayload.Position.builder()
|
||||
.latitude(savedLocation.getLocation().getY())
|
||||
.longitude(savedLocation.getLocation().getX())
|
||||
.build())
|
||||
.heading(savedLocation.getHeading())
|
||||
.speed(savedLocation.getSpeed())
|
||||
.timestamp(System.currentTimeMillis() * 1000L) // 微秒级时间戳
|
||||
.build();
|
||||
|
||||
eventPublisher.publishEvent(new PositionUpdateEvent(positionPayload));
|
||||
log.debug("发布无人车位置更新事件: vehicleId={}", savedLocation.getVehicleId());
|
||||
} catch (Exception eventException) {
|
||||
log.error("发布位置更新事件失败: vehicleId={}", savedLocation.getVehicleId(), eventException);
|
||||
|
||||
@ -6,6 +6,7 @@ import com.qaup.collision.websocket.broadcaster.RuleEventWebSocketPublisher;
|
||||
import com.qaup.collision.websocket.message.PathConflictAlertMessage;
|
||||
import com.qaup.collision.pathconflict.model.entity.ObjectRouteAssignment;
|
||||
import com.qaup.collision.pathconflict.repository.ObjectRouteAssignmentRepository;
|
||||
import com.qaup.collision.pathconflict.model.entity.ConflictAlertLog;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -13,6 +14,9 @@ import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
/**
|
||||
* 冲突告警事件监听器
|
||||
* 负责处理路径冲突告警事件,发送WebSocket消息和车辆控制指令
|
||||
@ -36,18 +40,28 @@ public class ConflictAlertEventListener {
|
||||
@Async
|
||||
@EventListener
|
||||
public void handleConflictAlert(ConflictAlertEvent event) {
|
||||
log.info("处理路径冲突告警事件: conflictId={}, alertType={}",
|
||||
event.getConflictId(), event.getAlertType());
|
||||
log.info("处理路径冲突告警事件: conflictId={}, alertType={}, alertLevel={}",
|
||||
event.getConflictId().map(String::valueOf).orElse("N/A"),
|
||||
event.getAlertType().map(Enum::name).orElse("N/A"),
|
||||
event.getAlertLevel().map(Enum::name).orElse("N/A"));
|
||||
|
||||
try {
|
||||
// 发送WebSocket消息到控制台
|
||||
sendWebSocketAlert(event);
|
||||
|
||||
// 向涉及的无人车发送控制指令
|
||||
sendVehicleCommands(event);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理冲突告警事件失败: conflictId={}", event.getConflictId(), e);
|
||||
// 只有当告警级别为 WARNING, CRITICAL, EMERGENCY 时才发送给前端和车辆指令
|
||||
if (event.getAlertLevel().isPresent() &&
|
||||
(event.getAlertLevel().get() == ConflictAlertLog.AlertLevel.WARNING ||
|
||||
event.getAlertLevel().get() == ConflictAlertLog.AlertLevel.CRITICAL ||
|
||||
event.getAlertLevel().get() == ConflictAlertLog.AlertLevel.EMERGENCY)) {
|
||||
try {
|
||||
// 发送WebSocket消息到控制台
|
||||
sendWebSocketAlert(event);
|
||||
|
||||
// 向涉及的无人车发送控制指令
|
||||
sendVehicleCommands(event);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理冲突告警事件失败: conflictId={}", event.getConflictId().orElse(null), e);
|
||||
}
|
||||
} else {
|
||||
log.debug("告警级别不符合发送条件,不发送WebSocket告警和车辆指令: conflictId={}, alertLevel={}", event.getConflictId().orElse(null), event.getAlertLevel().orElse(null));
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,23 +71,25 @@ public class ConflictAlertEventListener {
|
||||
private void sendWebSocketAlert(ConflictAlertEvent event) {
|
||||
try {
|
||||
PathConflictAlertMessage message = PathConflictAlertMessage.builder()
|
||||
.conflictId(event.getConflictId())
|
||||
.alertType(event.getAlertType().name()) // 使用枚举的name()
|
||||
.alertLevel(event.getAlertLevel().name()) // 使用枚举的name()
|
||||
.conflictId(event.getConflictId().orElse(null))
|
||||
.alertType(event.getAlertType().orElse(null)) // 直接传递 Optional 里的枚举对象
|
||||
.alertLevel(event.getAlertLevel().orElse(null)) // 直接传递 Optional 里的枚举对象
|
||||
.message(event.getMessage())
|
||||
.object1(PathConflictAlertMessage.ConflictObject.builder()
|
||||
.objectId(event.getObject1Id())
|
||||
.build()) // 移除 objectType
|
||||
.objectName(event.getObject1Name())
|
||||
.objectType(event.getObject1Type().name())
|
||||
.build())
|
||||
.object2(PathConflictAlertMessage.ConflictObject.builder()
|
||||
.objectId(event.getObject2Id())
|
||||
.build()) // 移除 objectType
|
||||
.objectName(event.getObject2Name())
|
||||
.objectType(event.getObject2Type().name())
|
||||
.build())
|
||||
.position(event.getConflictPointLatitude() != null && event.getConflictPointLongitude() != null ?
|
||||
PathConflictAlertMessage.Position.builder()
|
||||
.latitude(event.getConflictPointLatitude())
|
||||
.longitude(event.getConflictPointLongitude())
|
||||
.build() : null)
|
||||
.object1Distance(event.getObject1Distance()) // 新增
|
||||
.object2Distance(event.getObject2Distance()) // 新增
|
||||
.object1Distance(new BigDecimal(event.getObject1Distance()).setScale(2, RoundingMode.HALF_UP).doubleValue()) // 内联四舍五入
|
||||
.object2Distance(new BigDecimal(event.getObject2Distance()).setScale(2, RoundingMode.HALF_UP).doubleValue()) // 内联四舍五入
|
||||
.timeToConflict1(event.getEstimatedTimeToConflictObj1()) // 新增
|
||||
.timeToConflict2(event.getEstimatedTimeToConflictObj2()) // 新增
|
||||
.timeGap(event.getTimeGapSeconds()) // 新增
|
||||
@ -83,10 +99,10 @@ public class ConflictAlertEventListener {
|
||||
// 发送WebSocket消息
|
||||
webSocketPublisher.publishPathConflictAlert(message);
|
||||
|
||||
log.info("WebSocket告警消息已发送: conflictId={}", event.getConflictId());
|
||||
log.info("WebSocket告警消息已发送: conflictId={}", event.getConflictId().map(String::valueOf).orElse(null));
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("发送WebSocket告警消息失败: conflictId={}", event.getConflictId(), e);
|
||||
log.error("发送WebSocket告警消息失败: conflictId={}", event.getConflictId().map(String::valueOf).orElse(null), e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,17 +112,17 @@ public class ConflictAlertEventListener {
|
||||
private void sendVehicleCommands(ConflictAlertEvent event) {
|
||||
try {
|
||||
// 向对象1发送指令(如果是无人车)
|
||||
objectRouteAssignmentRepository.findFirstByObjectIdAndObjectTypeOrderByAssignedAtDesc(
|
||||
event.getObject1Id(), ObjectRouteAssignment.ObjectType.UNMANNED_VEHICLE)
|
||||
.ifPresent(assignment -> sendVehicleCommand(event.getObject1Id(), event));
|
||||
objectRouteAssignmentRepository.findFirstByObjectNameAndObjectTypeOrderByAssignedAtDesc(
|
||||
event.getObject1Name(), ObjectRouteAssignment.ObjectType.UNMANNED_VEHICLE)
|
||||
.ifPresent(assignment -> sendVehicleCommand(event.getObject1Name(), event));
|
||||
|
||||
// 向对象2发送指令(如果是无人车)
|
||||
objectRouteAssignmentRepository.findFirstByObjectIdAndObjectTypeOrderByAssignedAtDesc(
|
||||
event.getObject2Id(), ObjectRouteAssignment.ObjectType.UNMANNED_VEHICLE)
|
||||
.ifPresent(assignment -> sendVehicleCommand(event.getObject2Id(), event));
|
||||
objectRouteAssignmentRepository.findFirstByObjectNameAndObjectTypeOrderByAssignedAtDesc(
|
||||
event.getObject2Name(), ObjectRouteAssignment.ObjectType.UNMANNED_VEHICLE)
|
||||
.ifPresent(assignment -> sendVehicleCommand(event.getObject2Name(), event));
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("发送车辆控制指令失败: conflictId={}", event.getConflictId(), e);
|
||||
log.error("发送车辆控制指令失败: conflictId={}", event.getConflictId().orElse(null), e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,25 +148,29 @@ public class ConflictAlertEventListener {
|
||||
commandMessage = String.format("路径冲突预警:前方检测到潜在冲突,请注意观察。对象1距离冲突点:%.1f米,对象2距离冲突点:%.1f米",
|
||||
event.getObject1Distance(), event.getObject2Distance());
|
||||
} else {
|
||||
commandType = "INFO";
|
||||
commandMessage = "路径冲突信息提醒";
|
||||
// 如果是非警告、非关键、非紧急的告警(即旧的INFO级别),则不发送指令。
|
||||
// 在PathConflictDetectionService中已经过滤了INFO级别的事件生成,
|
||||
// 所以理论上此处不会进入此分支,但作为防御性编程,避免意外情况。
|
||||
log.warn("收到非WARNING/CRITICAL/EMERGENCY级别的告警事件,不发送车辆指令: conflictId={}, alertLevel={}",
|
||||
event.getConflictId().orElse(null), event.getAlertLevel().orElse(null));
|
||||
return; // 不发送指令
|
||||
}
|
||||
|
||||
// 发送指令到无人车
|
||||
boolean success = vehicleCommandService.sendCommand(
|
||||
vehicleId, commandType, commandMessage, event.getConflictId());
|
||||
vehicleId, commandType, commandMessage, event.getConflictId().map(String::valueOf).orElse(null));
|
||||
|
||||
if (success) {
|
||||
log.info("车辆控制指令已发送: vehicleId={}, commandType={}, conflictId={}",
|
||||
vehicleId, commandType, event.getConflictId());
|
||||
vehicleId, commandType, event.getConflictId().map(String::valueOf).orElse(null));
|
||||
} else {
|
||||
log.warn("车辆控制指令发送失败: vehicleId={}, conflictId={}",
|
||||
vehicleId, event.getConflictId());
|
||||
vehicleId, event.getConflictId().map(String::valueOf).orElse(null));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("发送车辆控制指令时出错: vehicleId={}, conflictId={}",
|
||||
vehicleId, event.getConflictId(), e);
|
||||
vehicleId, event.getConflictId().map(String::valueOf).orElse(null), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,132 +1,54 @@
|
||||
package com.qaup.collision.pathconflict.model.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.Builder;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.locationtech.jts.geom.Point;
|
||||
import com.qaup.collision.pathconflict.model.entity.ConflictAlertLog;
|
||||
import com.qaup.collision.common.model.MovingObject;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import org.locationtech.jts.geom.Point;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 冲突告警事件DTO
|
||||
* 用于在系统内部传递冲突告警信息
|
||||
*
|
||||
* @author AI Assistant
|
||||
* @version 1.0
|
||||
* @since 2025-01-17
|
||||
* 路径冲突告警事件
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ConflictAlertEvent {
|
||||
|
||||
/**
|
||||
* 冲突ID
|
||||
*/
|
||||
private String conflictId;
|
||||
|
||||
/**
|
||||
* 告警类型:WARNING, ALERT, EMERGENCY
|
||||
*/
|
||||
private ConflictAlertLog.AlertType alertType;
|
||||
|
||||
/**
|
||||
* 告警级别:INFO, WARNING, CRITICAL, EMERGENCY
|
||||
*/
|
||||
private ConflictAlertLog.AlertLevel alertLevel;
|
||||
|
||||
/**
|
||||
* 对象1 ID
|
||||
*/
|
||||
private String object1Id;
|
||||
|
||||
/**
|
||||
* 对象2 ID
|
||||
*/
|
||||
private String object2Id;
|
||||
|
||||
/**
|
||||
* 冲突点位置
|
||||
*/
|
||||
private Point conflictPoint;
|
||||
|
||||
/**
|
||||
* 告警消息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 对象1距离冲突点距离 (米)
|
||||
*/
|
||||
private Double object1Distance;
|
||||
private Optional<Long> conflictId; // 告警日志ID
|
||||
private Optional<ConflictAlertLog.AlertType> alertType; // 告警类型
|
||||
private Optional<ConflictAlertLog.AlertLevel> alertLevel; // 告警级别
|
||||
private String message; // 告警消息
|
||||
private String object1Name; // 对象1名称
|
||||
private MovingObject.ObjectType object1Type; // 对象1类型
|
||||
private String object2Name; // 对象2名称
|
||||
private MovingObject.ObjectType object2Type; // 对象2类型
|
||||
private Point conflictPoint; // 冲突点
|
||||
private Double object1Distance; // 对象1距离冲突点距离 (米)
|
||||
private Double object2Distance; // 对象2距离冲突点距离 (米)
|
||||
private Integer estimatedTimeToConflictObj1; // 对象1预计到达冲突点的时间 (秒)
|
||||
private Integer estimatedTimeToConflictObj2; // 对象2预计到达冲突点的时间 (秒)
|
||||
private Double timeGapSeconds; // 两个对象到达冲突点的时间差 (秒)
|
||||
private LocalDateTime eventTime; // 事件发生时间
|
||||
|
||||
/**
|
||||
* 对象2距离冲突点距离 (米)
|
||||
*/
|
||||
private Double object2Distance;
|
||||
|
||||
/**
|
||||
* 预计到达冲突点时间1 (秒)
|
||||
*/
|
||||
private Integer estimatedTimeToConflictObj1;
|
||||
|
||||
/**
|
||||
* 预计到达冲突点时间2 (秒)
|
||||
*/
|
||||
private Integer estimatedTimeToConflictObj2;
|
||||
|
||||
/**
|
||||
* 时间差 (秒)
|
||||
*/
|
||||
private Double timeGapSeconds;
|
||||
|
||||
/**
|
||||
* 事件发生时间
|
||||
*/
|
||||
@Builder.Default
|
||||
private LocalDateTime eventTime = LocalDateTime.now();
|
||||
|
||||
/**
|
||||
* 事件来源
|
||||
*/
|
||||
@Builder.Default
|
||||
private String eventSource = "PATH_CONFLICT_DETECTION";
|
||||
|
||||
/**
|
||||
* 判断是否为紧急告警
|
||||
*/
|
||||
// 辅助方法,用于判断告警级别
|
||||
public boolean isEmergencyAlert() {
|
||||
return ConflictAlertLog.AlertType.EMERGENCY.equals(alertType) || ConflictAlertLog.AlertLevel.CRITICAL.equals(alertLevel);
|
||||
return alertLevel.isPresent() && alertLevel.get() == ConflictAlertLog.AlertLevel.EMERGENCY;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为预警
|
||||
*/
|
||||
public boolean isWarning() {
|
||||
return ConflictAlertLog.AlertType.WARNING.equals(alertType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为告警
|
||||
*/
|
||||
|
||||
public boolean isAlert() {
|
||||
return ConflictAlertLog.AlertType.ALERT.equals(alertType);
|
||||
return alertLevel.isPresent() && alertLevel.get() == ConflictAlertLog.AlertLevel.CRITICAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取冲突点经度
|
||||
*/
|
||||
public Double getConflictPointLongitude() {
|
||||
return conflictPoint != null ? conflictPoint.getX() : null;
|
||||
|
||||
public boolean isWarning() {
|
||||
return alertLevel.isPresent() && alertLevel.get() == ConflictAlertLog.AlertLevel.WARNING;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取冲突点纬度
|
||||
*/
|
||||
|
||||
public Double getConflictPointLatitude() {
|
||||
return conflictPoint != null ? conflictPoint.getY() : null;
|
||||
}
|
||||
|
||||
public Double getConflictPointLongitude() {
|
||||
return conflictPoint != null ? conflictPoint.getX() : null;
|
||||
}
|
||||
}
|
||||
@ -18,7 +18,6 @@ import java.time.LocalDateTime;
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "conflict_alert_logs", indexes = {
|
||||
@Index(name = "idx_conflict_alerts_conflict_id", columnList = "conflict_id"),
|
||||
@Index(name = "idx_conflict_alerts_type", columnList = "alert_type"),
|
||||
@Index(name = "idx_conflict_alerts_level", columnList = "alert_level"),
|
||||
@Index(name = "idx_conflict_alerts_time", columnList = "alert_time DESC")
|
||||
@ -34,20 +33,14 @@ public class ConflictAlertLog {
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 冲突的唯一标识,可能来自实时计算或是一个事件ID
|
||||
*/
|
||||
@Column(name = "conflict_id", nullable = false, length = 50)
|
||||
private String conflictId;
|
||||
|
||||
/**
|
||||
* 告警类型:WARNING, ALERT, EMERGENCY
|
||||
* 告警类型:CONFLICT_WARNING, CONFLICT_ALERT
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "alert_type", nullable = false, length = 20)
|
||||
@Column(name = "alert_type", nullable = false, length = 50) // 增加长度以适应新类型
|
||||
private AlertType alertType;
|
||||
|
||||
/**
|
||||
* 告警级别:INFO, WARNING, CRITICAL, EMERGENCY
|
||||
* 告警级别:WARNING, CRITICAL, EMERGENCY
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "alert_level", nullable = false, length = 20)
|
||||
@ -84,22 +77,20 @@ public class ConflictAlertLog {
|
||||
private LocalDateTime alertTime;
|
||||
|
||||
/**
|
||||
* 告警类型枚举
|
||||
* 告警类型枚举 - 用于区分告警的来源或类别
|
||||
*/
|
||||
public enum AlertType {
|
||||
WARNING, // 预警
|
||||
ALERT, // 告警
|
||||
EMERGENCY // 紧急
|
||||
CONFLICT_WARNING, // 冲突预警
|
||||
CONFLICT_ALERT // 冲突告警
|
||||
}
|
||||
|
||||
/**
|
||||
* 告警级别枚举
|
||||
* 告警级别枚举 - 用于区分告警的严重程度
|
||||
*/
|
||||
public enum AlertLevel {
|
||||
INFO, // 信息
|
||||
WARNING, // 警告
|
||||
CRITICAL, // 严重
|
||||
EMERGENCY // 紧急
|
||||
WARNING, // 警告
|
||||
CRITICAL, // 严重
|
||||
EMERGENCY // 紧急
|
||||
}
|
||||
|
||||
@PrePersist
|
||||
|
||||
@ -18,7 +18,7 @@ import java.time.LocalDateTime;
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "object_route_assignments", indexes = {
|
||||
@Index(name = "idx_object_route_assignments_object", columnList = "object_id,object_type"),
|
||||
@Index(name = "idx_object_route_assignments_object_type", columnList = "object_type"),
|
||||
@Index(name = "idx_object_route_assignments_route", columnList = "assigned_route_id"),
|
||||
@Index(name = "idx_object_route_assignments_assigned_at", columnList = "assigned_at DESC")
|
||||
})
|
||||
@ -33,13 +33,7 @@ public class ObjectRouteAssignment {
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 对象ID:无人车ID或航空器标识
|
||||
*/
|
||||
@Column(name = "object_id", nullable = false, length = 50)
|
||||
private String objectId;
|
||||
|
||||
/**
|
||||
* 对象类型:UNMANNED_VEHICLE, AIRCRAFT, SPECIAL_VEHICLE
|
||||
* 对象类型:UNMANNED_VEHICLE, AIRCRAFT, SPECIAL_VEHICLE, NORMAL_VEHICLE
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "object_type", nullable = false, length = 20)
|
||||
@ -48,14 +42,14 @@ public class ObjectRouteAssignment {
|
||||
/**
|
||||
* 对象名称:车牌号或航班号
|
||||
*/
|
||||
@Column(name = "object_name", length = 100)
|
||||
@Column(name = "object_name", nullable = false, length = 100)
|
||||
private String objectName;
|
||||
|
||||
/**
|
||||
* 分配的路径ID
|
||||
*/
|
||||
@Column(name = "assigned_route_id", nullable = false, length = 50)
|
||||
private String assignedRouteId;
|
||||
@Column(name = "assigned_route_id", nullable = false)
|
||||
private Long assignedRouteId;
|
||||
|
||||
/**
|
||||
* 分配时间
|
||||
@ -69,7 +63,8 @@ public class ObjectRouteAssignment {
|
||||
public enum ObjectType {
|
||||
UNMANNED_VEHICLE, // 无人车
|
||||
AIRCRAFT, // 航空器
|
||||
SPECIAL_VEHICLE // 特种车辆
|
||||
SPECIAL_VEHICLE, // 特种车辆
|
||||
NORMAL_VEHICLE // 普通车辆
|
||||
}
|
||||
|
||||
@PrePersist
|
||||
|
||||
@ -20,7 +20,6 @@ import java.time.LocalDateTime;
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "transport_routes", indexes = {
|
||||
@Index(name = "idx_transport_routes_route_id", columnList = "route_id"),
|
||||
@Index(name = "idx_transport_routes_type", columnList = "route_type"),
|
||||
@Index(name = "idx_transport_routes_status", columnList = "status")
|
||||
})
|
||||
@ -34,12 +33,6 @@ public class TransportRoute {
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 路径唯一标识符
|
||||
*/
|
||||
@Column(name = "route_id", nullable = false, unique = true, length = 50)
|
||||
private String routeId;
|
||||
|
||||
/**
|
||||
* 路径名称
|
||||
*/
|
||||
@ -47,7 +40,7 @@ public class TransportRoute {
|
||||
private String routeName;
|
||||
|
||||
/**
|
||||
* 路径类型:AIRCRAFT, UNMANNED_VEHICLE, SPECIAL_VEHICLE
|
||||
* 路径类型:AIRCRAFT, UNMANNED_VEHICLE, SPECIAL_VEHICLE, NORMAL_VEHICLE
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "route_type", nullable = false, length = 20)
|
||||
@ -134,7 +127,8 @@ public class TransportRoute {
|
||||
public enum RouteType {
|
||||
AIRCRAFT, // 航空器路径
|
||||
UNMANNED_VEHICLE, // 无人车路径
|
||||
SPECIAL_VEHICLE // 特种车辆路径
|
||||
SPECIAL_VEHICLE, // 特种车辆路径
|
||||
NORMAL_VEHICLE // 普通车辆路径
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -17,11 +17,6 @@ import java.util.Optional;
|
||||
@Repository
|
||||
public interface ConflictAlertLogRepository extends JpaRepository<ConflictAlertLog, Long> {
|
||||
|
||||
/**
|
||||
* 根据冲突ID查找告警记录
|
||||
*/
|
||||
List<ConflictAlertLog> findByConflictId(String conflictId);
|
||||
|
||||
/**
|
||||
* 根据告警类型和级别查找告警记录
|
||||
*/
|
||||
@ -34,9 +29,4 @@ public interface ConflictAlertLogRepository extends JpaRepository<ConflictAlertL
|
||||
* 查找最新的一条告警记录
|
||||
*/
|
||||
Optional<ConflictAlertLog> findFirstByOrderByAlertTimeDesc();
|
||||
|
||||
/**
|
||||
* 查找某个冲突ID下最新的告警记录
|
||||
*/
|
||||
Optional<ConflictAlertLog> findFirstByConflictIdOrderByAlertTimeDesc(String conflictId);
|
||||
}
|
||||
@ -18,20 +18,15 @@ import java.util.Optional;
|
||||
public interface ObjectRouteAssignmentRepository extends JpaRepository<ObjectRouteAssignment, Long> {
|
||||
|
||||
/**
|
||||
* 根据对象ID和对象类型查找最新的路径分配记录
|
||||
* 根据对象名称和对象类型查找最新的路径分配记录
|
||||
*/
|
||||
Optional<ObjectRouteAssignment> findFirstByObjectIdAndObjectTypeOrderByAssignedAtDesc(
|
||||
String objectId,
|
||||
Optional<ObjectRouteAssignment> findFirstByObjectNameAndObjectTypeOrderByAssignedAtDesc(
|
||||
String objectName,
|
||||
ObjectRouteAssignment.ObjectType objectType
|
||||
);
|
||||
|
||||
/**
|
||||
* 根据路径ID查找所有分配到该路径的对象
|
||||
*/
|
||||
List<ObjectRouteAssignment> findByAssignedRouteId(String assignedRouteId);
|
||||
|
||||
/**
|
||||
* 根据对象ID查找所有分配到该对象的路径记录
|
||||
*/
|
||||
List<ObjectRouteAssignment> findByObjectId(String objectId);
|
||||
List<ObjectRouteAssignment> findByAssignedRouteId(Long assignedRouteId);
|
||||
}
|
||||
@ -19,11 +19,6 @@ import java.util.Optional;
|
||||
@Repository
|
||||
public interface TransportRouteRepository extends JpaRepository<TransportRoute, Long> {
|
||||
|
||||
/**
|
||||
* 根据路径ID查找路径
|
||||
*/
|
||||
Optional<TransportRoute> findByRouteId(String routeId);
|
||||
|
||||
/**
|
||||
* 根据路径类型和状态查找路径
|
||||
*/
|
||||
@ -46,10 +41,10 @@ public interface TransportRouteRepository extends JpaRepository<TransportRoute,
|
||||
* 查找与指定路径相交的路径
|
||||
*/
|
||||
@Query(value = "SELECT r2.* FROM transport_routes r1, transport_routes r2 WHERE " +
|
||||
"r1.route_id = :routeId AND r2.route_id != :routeId " +
|
||||
"r1.id = :routeId AND r2.id != :routeId " +
|
||||
"AND ST_Intersects(r1.route_geometry, r2.route_geometry) " +
|
||||
"AND r2.status = 'ACTIVE'", nativeQuery = true)
|
||||
List<TransportRoute> findIntersectingRoutes(@Param("routeId") String routeId);
|
||||
List<TransportRoute> findIntersectingRoutes(@Param("routeId") Long routeId);
|
||||
|
||||
/**
|
||||
* 查找最近的路径
|
||||
|
||||
@ -8,18 +8,17 @@ import com.qaup.collision.pathconflict.model.entity.ObjectRouteAssignment;
|
||||
import com.qaup.collision.pathconflict.repository.ObjectRouteAssignmentRepository;
|
||||
import com.qaup.collision.pathconflict.model.entity.ConflictAlertLog;
|
||||
import com.qaup.collision.pathconflict.repository.ConflictAlertLogRepository;
|
||||
import com.qaup.collision.dataprocessing.service.CoordinateSystemService;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.locationtech.jts.geom.*;
|
||||
import org.locationtech.jts.operation.distance.DistanceOp;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 路径冲突检测服务
|
||||
@ -38,6 +37,7 @@ public class PathConflictDetectionService {
|
||||
private final ObjectRouteAssignmentRepository objectRouteAssignmentRepository;
|
||||
private final ConflictAlertLogRepository conflictAlertLogRepository;
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
private final CoordinateSystemService coordinateSystemService; // 注入 CoordinateSystemService
|
||||
|
||||
// 预警距离阈值(米)
|
||||
private static final double WARNING_DISTANCE_THRESHOLD = 200.0;
|
||||
@ -84,13 +84,18 @@ public class PathConflictDetectionService {
|
||||
* 检测两个对象之间的路径冲突
|
||||
*/
|
||||
private Optional<ConflictAlertEvent> detectConflictBetweenObjects(MovingObject obj1, MovingObject obj2) {
|
||||
if (obj1.getObjectType() != MovingObject.ObjectType.UNMANNED_VEHICLE && obj2.getObjectType() != MovingObject.ObjectType.UNMANNED_VEHICLE) {
|
||||
log.debug("对象 {} 和 {} 都不是无人车,跳过冲突检测", obj1.getObjectName(), obj2.getObjectName());
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取对象的路径
|
||||
TransportRoute route1 = getObjectRoute(obj1);
|
||||
TransportRoute route2 = getObjectRoute(obj2);
|
||||
|
||||
if (route1 == null || route2 == null) {
|
||||
log.debug("对象 {} 或 {} 没有分配路径,跳过冲突检测", obj1.getObjectId(), obj2.getObjectId());
|
||||
log.debug("对象 {} 或 {} 没有分配路径,跳过冲突检测", obj1.getObjectName(), obj2.getObjectName());
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@ -102,13 +107,11 @@ public class PathConflictDetectionService {
|
||||
ConflictCalculationResult result = calculateConflictDetails(obj1, obj2, route1, route2, intersectionPoint);
|
||||
|
||||
if (result != null && isSignificantConflict(result)) {
|
||||
String conflictId = generateConflictId(obj1.getObjectId(), obj2.getObjectId());
|
||||
String description = generateConflictDescription(obj1, obj2, route1, route2);
|
||||
|
||||
ConflictAlertLog alertLog = ConflictAlertLog.builder()
|
||||
.conflictId(conflictId)
|
||||
.alertType(result.getAlertType())
|
||||
.alertLevel(result.getAlertLevel())
|
||||
.alertType(result.getAlertType().orElse(null))
|
||||
.alertLevel(result.getAlertLevel().orElse(null))
|
||||
.alertMessage(description)
|
||||
.object1Distance(result.getDistance1())
|
||||
.object2Distance(result.getDistance2())
|
||||
@ -119,12 +122,14 @@ public class PathConflictDetectionService {
|
||||
log.info("保存冲突告警日志: {}", alertLog);
|
||||
|
||||
return Optional.of(ConflictAlertEvent.builder()
|
||||
.conflictId(conflictId)
|
||||
.conflictId(Optional.of(alertLog.getId()))
|
||||
.alertType(result.getAlertType())
|
||||
.alertLevel(result.getAlertLevel())
|
||||
.message(description)
|
||||
.object1Id(obj1.getObjectId())
|
||||
.object2Id(obj2.getObjectId())
|
||||
.object1Name(obj1.getObjectName())
|
||||
.object1Type(obj1.getObjectType())
|
||||
.object2Name(obj2.getObjectName())
|
||||
.object2Type(obj2.getObjectType())
|
||||
.conflictPoint(intersectionPoint)
|
||||
.object1Distance(result.getDistance1())
|
||||
.object2Distance(result.getDistance2())
|
||||
@ -136,7 +141,7 @@ public class PathConflictDetectionService {
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("检测对象 {} 和 {} 之间的冲突时发生异常", obj1.getObjectId(), obj2.getObjectId(), e);
|
||||
log.error("检测对象 {} 和 {} 之间的冲突时发生异常", obj1.getObjectName(), obj2.getObjectName(), e);
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
@ -146,38 +151,15 @@ public class PathConflictDetectionService {
|
||||
* 获取对象当前使用的路径
|
||||
*/
|
||||
private TransportRoute getObjectRoute(MovingObject obj) {
|
||||
Optional<ObjectRouteAssignment> assignmentOptional = objectRouteAssignmentRepository.findFirstByObjectIdAndObjectTypeOrderByAssignedAtDesc(
|
||||
obj.getObjectId(),
|
||||
Optional<ObjectRouteAssignment> assignmentOptional = objectRouteAssignmentRepository.findFirstByObjectNameAndObjectTypeOrderByAssignedAtDesc(
|
||||
obj.getObjectName(), // Changed from getObjectId
|
||||
ObjectRouteAssignment.ObjectType.valueOf(obj.getObjectType().name())
|
||||
);
|
||||
|
||||
return assignmentOptional.map(assignment -> routeRepository.findByRouteId(assignment.getAssignedRouteId()).orElse(null))
|
||||
return assignmentOptional.map(assignment -> routeRepository.findById(assignment.getAssignedRouteId()).orElse(null)) // Changed findByRouteId to findById
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找距离指定位置最近的路径 (此方法可能不再需要,因为我们现在通过分配表获取路径)
|
||||
* 或者可以用于处理未分配路径的对象,但当前逻辑主要依赖于已分配路径
|
||||
*/
|
||||
private TransportRoute findNearestRoute(Point position, List<TransportRoute> routes) {
|
||||
TransportRoute nearestRoute = null;
|
||||
double minDistance = Double.MAX_VALUE;
|
||||
|
||||
for (TransportRoute route : routes) {
|
||||
try {
|
||||
double distance = position.distance(route.getRouteGeometry());
|
||||
if (distance < minDistance) {
|
||||
minDistance = distance;
|
||||
nearestRoute = route;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("计算到路径 {} 的距离时出错", route.getRouteId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
return nearestRoute;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两条路径的交点
|
||||
*/
|
||||
@ -201,11 +183,16 @@ public class PathConflictDetectionService {
|
||||
// 如果是线段重叠,取线段的中点
|
||||
LineString overlapLine = (LineString) intersection;
|
||||
Point midPoint = overlapLine.getInteriorPoint();
|
||||
intersections.add(midPoint);
|
||||
// 确保中间点不为空且不是POINT EMPTY
|
||||
if (midPoint != null && !midPoint.isEmpty()) {
|
||||
intersections.add(midPoint);
|
||||
} else {
|
||||
log.warn("LineString交点生成了空的中间点或无效点,跳过此交点。OverlapLine: {}", overlapLine);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("计算路径 {} 和 {} 的交点时出错", route1.getRouteId(), route2.getRouteId(), e);
|
||||
log.error("计算路径交点时出错", e);
|
||||
}
|
||||
|
||||
return intersections;
|
||||
@ -239,11 +226,13 @@ public class PathConflictDetectionService {
|
||||
double timeGap = Math.abs(timeToConflict1 - timeToConflict2);
|
||||
|
||||
// 评估冲突告警级别和类型
|
||||
ConflictAlertLog.AlertLevel alertLevel = evaluateAlertLevel(distance1, distance2, timeGap);
|
||||
ConflictAlertLog.AlertType alertType = evaluateAlertType(alertLevel);
|
||||
Optional<ConflictAlertLog.AlertLevel> alertLevelOptional = evaluateAlertLevel(distance1, obj1.getObjectType(), distance2, obj2.getObjectType(), timeGap);
|
||||
Optional<ConflictAlertLog.AlertType> alertTypeOptional = evaluateAlertType(alertLevelOptional.orElse(null)); // Changed to orElse(null)
|
||||
|
||||
return new ConflictCalculationResult(
|
||||
distance1, distance2, timeToConflict1, timeToConflict2, timeGap, alertType, alertLevel);
|
||||
distance1, distance2, timeToConflict1, timeToConflict2, timeGap,
|
||||
alertTypeOptional, alertLevelOptional,
|
||||
obj1.getObjectType(), obj2.getObjectType()); // Pass object types
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("计算冲突详细信息时出错", e);
|
||||
@ -254,35 +243,56 @@ public class PathConflictDetectionService {
|
||||
/**
|
||||
* 评估冲突的告警级别
|
||||
*/
|
||||
private ConflictAlertLog.AlertLevel evaluateAlertLevel(
|
||||
double distance1, double distance2, double timeGap) {
|
||||
|
||||
double minDistance = Math.min(distance1, distance2);
|
||||
|
||||
if (minDistance <= ALERT_DISTANCE_THRESHOLD && timeGap <= MIN_TIME_GAP_SECONDS) {
|
||||
return ConflictAlertLog.AlertLevel.CRITICAL;
|
||||
} else if (minDistance <= WARNING_DISTANCE_THRESHOLD && timeGap <= MIN_TIME_GAP_SECONDS * 2) {
|
||||
return ConflictAlertLog.AlertLevel.WARNING;
|
||||
} else if (minDistance <= WARNING_DISTANCE_THRESHOLD * 2 && timeGap <= MAX_PREDICTION_TIME_SECONDS) {
|
||||
return ConflictAlertLog.AlertLevel.INFO;
|
||||
private Optional<ConflictAlertLog.AlertLevel> evaluateAlertLevel(
|
||||
double distance1, MovingObject.ObjectType obj1Type,
|
||||
double distance2, MovingObject.ObjectType obj2Type,
|
||||
double timeGap) {
|
||||
|
||||
boolean obj1IsUnmannedVehicle = MovingObject.ObjectType.UNMANNED_VEHICLE.equals(obj1Type);
|
||||
boolean obj2IsUnmannedVehicle = MovingObject.ObjectType.UNMANNED_VEHICLE.equals(obj2Type);
|
||||
|
||||
if (!obj1IsUnmannedVehicle && !obj2IsUnmannedVehicle) {
|
||||
// 双方都不是无人车,根据业务规则,不生成告警。
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
double distanceToEvaluate;
|
||||
|
||||
if (obj1IsUnmannedVehicle && obj2IsUnmannedVehicle) {
|
||||
// 双方都是无人车,关注两者中离交点最近的距离
|
||||
distanceToEvaluate = Math.min(distance1, distance2);
|
||||
} else if (obj1IsUnmannedVehicle) {
|
||||
// 只有 obj1 是无人车,关注 obj1 的距离。obj2 是无人车需要避让的对象。
|
||||
distanceToEvaluate = distance1;
|
||||
} else { // obj2IsUnmannedVehicle
|
||||
// 只有 obj2 是无人车,关注 obj2 的距离。obj1 是无人车需要避让的对象。
|
||||
distanceToEvaluate = distance2;
|
||||
}
|
||||
|
||||
// 现在根据确定的 relevantDistance 评估告警级别
|
||||
if (distanceToEvaluate <= ALERT_DISTANCE_THRESHOLD && timeGap <= MIN_TIME_GAP_SECONDS) {
|
||||
return Optional.of(ConflictAlertLog.AlertLevel.CRITICAL);
|
||||
} else if (distanceToEvaluate <= WARNING_DISTANCE_THRESHOLD && timeGap <= MIN_TIME_GAP_SECONDS * 2) {
|
||||
return Optional.of(ConflictAlertLog.AlertLevel.WARNING);
|
||||
} else {
|
||||
return ConflictAlertLog.AlertLevel.INFO; // 默认信息级别
|
||||
// 不满足 CRITICAL/WARNING 阈值,不生成告警。
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据告警级别评估告警类型
|
||||
*/
|
||||
private ConflictAlertLog.AlertType evaluateAlertType(ConflictAlertLog.AlertLevel level) {
|
||||
switch (level) {
|
||||
private Optional<ConflictAlertLog.AlertType> evaluateAlertType(ConflictAlertLog.AlertLevel alertLevel) {
|
||||
switch (alertLevel) {
|
||||
case WARNING:
|
||||
return Optional.of(ConflictAlertLog.AlertType.CONFLICT_WARNING);
|
||||
case CRITICAL:
|
||||
case EMERGENCY:
|
||||
return ConflictAlertLog.AlertType.ALERT; // 达到告警级别
|
||||
case WARNING:
|
||||
return ConflictAlertLog.AlertType.WARNING; // 预警级别
|
||||
case INFO:
|
||||
return Optional.of(ConflictAlertLog.AlertType.CONFLICT_ALERT);
|
||||
default:
|
||||
return ConflictAlertLog.AlertType.WARNING; // 默认预警
|
||||
// 应该不会到达这里,因为所有的 AlertLevel 都已经被明确处理
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@ -290,8 +300,10 @@ public class PathConflictDetectionService {
|
||||
* 判断是否为显著冲突
|
||||
*/
|
||||
private boolean isSignificantConflict(ConflictCalculationResult result) {
|
||||
return result.getAlertType() == ConflictAlertLog.AlertType.ALERT ||
|
||||
result.getAlertType() == ConflictAlertLog.AlertType.WARNING;
|
||||
return result.getAlertLevel().isPresent() &&
|
||||
(result.getAlertLevel().get() == ConflictAlertLog.AlertLevel.WARNING ||
|
||||
result.getAlertLevel().get() == ConflictAlertLog.AlertLevel.CRITICAL ||
|
||||
result.getAlertLevel().get() == ConflictAlertLog.AlertLevel.EMERGENCY);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -299,27 +311,47 @@ public class PathConflictDetectionService {
|
||||
*/
|
||||
private double calculateDistanceAlongRoute(Point currentPos, Point targetPos, TransportRoute route) {
|
||||
try {
|
||||
LineString routeLine = route.getRouteGeometry();
|
||||
// 1. 获取原始路径几何和点
|
||||
LineString originalRouteLine = route.getRouteGeometry();
|
||||
|
||||
// 找到当前位置在路径上的最近点
|
||||
DistanceOp distOp1 = new DistanceOp(currentPos, routeLine);
|
||||
// 2. 将所有相关几何对象转换为局部米制坐标系
|
||||
// 2.1 转换 currentPos 和 targetPos
|
||||
double[] localCurrentCoords = coordinateSystemService.convertToLocalCoordinate(currentPos.getX(), currentPos.getY());
|
||||
double[] localTargetCoords = coordinateSystemService.convertToLocalCoordinate(targetPos.getX(), targetPos.getY());
|
||||
|
||||
GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), 0); // SRID 0 for Cartesian system (meters)
|
||||
Point localCurrentPos = geometryFactory.createPoint(new Coordinate(localCurrentCoords[0], localCurrentCoords[1]));
|
||||
Point localTargetPos = geometryFactory.createPoint(new Coordinate(localTargetCoords[0], localTargetCoords[1]));
|
||||
|
||||
// 2.2 转换 routeLine
|
||||
Coordinate[] originalCoords = originalRouteLine.getCoordinates();
|
||||
Coordinate[] localRouteCoords = new Coordinate[originalCoords.length];
|
||||
for (int i = 0; i < originalCoords.length; i++) {
|
||||
double[] localCoord = coordinateSystemService.convertToLocalCoordinate(originalCoords[i].x, originalCoords[i].y);
|
||||
localRouteCoords[i] = new Coordinate(localCoord[0], localCoord[1]);
|
||||
}
|
||||
LineString localRouteLine = geometryFactory.createLineString(localRouteCoords);
|
||||
|
||||
// 3. 在局部米制坐标系中执行距离计算
|
||||
// 3.1 找到当前位置在路径上的最近点
|
||||
DistanceOp distOp1 = new DistanceOp(localCurrentPos, localRouteLine);
|
||||
Coordinate[] nearestPoints1 = distOp1.nearestPoints();
|
||||
Point nearestOnRoute1 = currentPos.getFactory().createPoint(nearestPoints1[1]);
|
||||
Point nearestOnRoute1 = geometryFactory.createPoint(nearestPoints1[1]);
|
||||
|
||||
// 找到目标位置在路径上的最近点
|
||||
DistanceOp distOp2 = new DistanceOp(targetPos, routeLine);
|
||||
// 3.2 找到目标位置在路径上的最近点
|
||||
DistanceOp distOp2 = new DistanceOp(localTargetPos, localRouteLine);
|
||||
Coordinate[] nearestPoints2 = distOp2.nearestPoints();
|
||||
Point nearestOnRoute2 = currentPos.getFactory().createPoint(nearestPoints2[1]);
|
||||
Point nearestOnRoute2 = geometryFactory.createPoint(nearestPoints2[1]);
|
||||
|
||||
// 计算沿路径的距离
|
||||
double distanceToCurrentOnRoute = getLinearLengthToPoint(routeLine, nearestOnRoute1);
|
||||
double distanceToTargetOnRoute = getLinearLengthToPoint(routeLine, nearestOnRoute2);
|
||||
// 3.3 计算沿路径的距离 (现在这些距离将是米)
|
||||
double distanceToCurrentOnRoute = getLinearLengthToPoint(localRouteLine, nearestOnRoute1);
|
||||
double distanceToTargetOnRoute = getLinearLengthToPoint(localRouteLine, nearestOnRoute2);
|
||||
|
||||
return Math.abs(distanceToTargetOnRoute - distanceToCurrentOnRoute);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("计算沿路径距离时出错: currentPos={}, targetPos={}, routeId={}",
|
||||
currentPos, targetPos, route.getRouteId(), e);
|
||||
log.error("计算沿路径距离时出错: currentPos={}, targetPos={}",
|
||||
currentPos, targetPos, e);
|
||||
return -1.0; // 表示错误
|
||||
}
|
||||
}
|
||||
@ -327,11 +359,11 @@ public class PathConflictDetectionService {
|
||||
/**
|
||||
* 生成冲突ID
|
||||
*/
|
||||
private String generateConflictId(String obj1Id, String obj2Id) {
|
||||
private String generateConflictId(String obj1Name, String obj2Name) {
|
||||
// 确保ID的生成是确定性的,与对象的顺序无关
|
||||
String[] ids = {obj1Id, obj2Id};
|
||||
Arrays.sort(ids);
|
||||
return "CONFLICT-" + ids[0] + "-" + ids[1] + "-" + UUID.randomUUID().toString().substring(0, 8);
|
||||
String[] names = {obj1Name, obj2Name};
|
||||
Arrays.sort(names);
|
||||
return "CONFLICT-" + names[0] + "-" + names[1] + "-" + UUID.randomUUID().toString().substring(0, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -340,9 +372,9 @@ public class PathConflictDetectionService {
|
||||
private String generateConflictDescription(
|
||||
MovingObject obj1, MovingObject obj2,
|
||||
TransportRoute route1, TransportRoute route2) {
|
||||
return String.format("对象 %s (%s) 在路径 %s 与对象 %s (%s) 在路径 %s 上可能发生冲突。",
|
||||
obj1.getObjectId(), obj1.getObjectType(), route1.getRouteId(),
|
||||
obj2.getObjectId(), obj2.getObjectType(), route2.getRouteId());
|
||||
return String.format("对象 %s (%s) 在路径上与对象 %s (%s) 可能发生冲突。",
|
||||
obj1.getObjectName(), obj1.getObjectType(),
|
||||
obj2.getObjectName(), obj2.getObjectType());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -387,21 +419,27 @@ public class PathConflictDetectionService {
|
||||
private final int timeToConflict1;
|
||||
private final int timeToConflict2;
|
||||
private final double timeGap;
|
||||
private final ConflictAlertLog.AlertType alertType;
|
||||
private final ConflictAlertLog.AlertLevel alertLevel;
|
||||
private final Optional<ConflictAlertLog.AlertType> alertType; // Changed to Optional
|
||||
private final Optional<ConflictAlertLog.AlertLevel> alertLevel; // Changed to Optional
|
||||
private final MovingObject.ObjectType object1Type; // New
|
||||
private final MovingObject.ObjectType object2Type; // New
|
||||
|
||||
public ConflictCalculationResult(double distance1, double distance2,
|
||||
int timeToConflict1, int timeToConflict2,
|
||||
double timeGap,
|
||||
ConflictAlertLog.AlertType alertType,
|
||||
ConflictAlertLog.AlertLevel alertLevel) {
|
||||
Optional<ConflictAlertLog.AlertType> alertType, // Changed to Optional
|
||||
Optional<ConflictAlertLog.AlertLevel> alertLevel, // Changed to Optional
|
||||
MovingObject.ObjectType object1Type, // New
|
||||
MovingObject.ObjectType object2Type) { // New
|
||||
this.distance1 = distance1;
|
||||
this.distance2 = distance2;
|
||||
this.timeToConflict1 = timeToConflict1;
|
||||
this.timeToConflict2 = timeToConflict2;
|
||||
this.timeGap = timeGap;
|
||||
this.alertType = alertType;
|
||||
this.alertLevel = alertLevel;
|
||||
this.alertType = alertType; // Changed
|
||||
this.alertLevel = alertLevel; // Changed
|
||||
this.object1Type = object1Type; // New
|
||||
this.object2Type = object2Type; // New
|
||||
}
|
||||
|
||||
public double getDistance1() { return distance1; }
|
||||
@ -409,7 +447,9 @@ public class PathConflictDetectionService {
|
||||
public int getTimeToConflict1() { return timeToConflict1; }
|
||||
public int getTimeToConflict2() { return timeToConflict2; }
|
||||
public double getTimeGap() { return timeGap; }
|
||||
public ConflictAlertLog.AlertType getAlertType() { return alertType; }
|
||||
public ConflictAlertLog.AlertLevel getAlertLevel() { return alertLevel; }
|
||||
public Optional<ConflictAlertLog.AlertType> getAlertType() { return alertType; } // Changed
|
||||
public Optional<ConflictAlertLog.AlertLevel> getAlertLevel() { return alertLevel; } // Changed
|
||||
public MovingObject.ObjectType getObject1Type() { return object1Type; } // New
|
||||
public MovingObject.ObjectType getObject2Type() { return object2Type; } // New
|
||||
}
|
||||
}
|
||||
@ -70,7 +70,7 @@ public class PathConflictAlertWebSocketEvent implements WebSocketEvent {
|
||||
/**
|
||||
* 获取冲突ID
|
||||
*/
|
||||
public String getConflictId() {
|
||||
public Long getConflictId() {
|
||||
return getPayload() != null ? ((PathConflictAlertMessage) getPayload()).getConflictId() : null;
|
||||
}
|
||||
|
||||
@ -78,14 +78,16 @@ public class PathConflictAlertWebSocketEvent implements WebSocketEvent {
|
||||
* 获取告警类型
|
||||
*/
|
||||
public String getAlertType() {
|
||||
return getPayload() != null ? ((PathConflictAlertMessage) getPayload()).getAlertType() : null;
|
||||
return getPayload() != null && ((PathConflictAlertMessage) getPayload()).getAlertType() != null ?
|
||||
((PathConflictAlertMessage) getPayload()).getAlertType().name() : (String) null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取告警级别
|
||||
*/
|
||||
public String getAlertLevel() {
|
||||
return getPayload() != null ? ((PathConflictAlertMessage) getPayload()).getAlertLevel() : null;
|
||||
return getPayload() != null && ((PathConflictAlertMessage) getPayload()).getAlertLevel() != null ?
|
||||
((PathConflictAlertMessage) getPayload()).getAlertLevel().name() : (String) null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -7,6 +7,7 @@ import lombok.AllArgsConstructor;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import com.qaup.collision.pathconflict.model.entity.ConflictAlertLog;
|
||||
|
||||
/**
|
||||
* 路径冲突告警WebSocket消息
|
||||
@ -31,17 +32,17 @@ public class PathConflictAlertMessage {
|
||||
/**
|
||||
* 冲突ID
|
||||
*/
|
||||
private String conflictId;
|
||||
private Long conflictId;
|
||||
|
||||
/**
|
||||
* 告警类型:WARNING, ALERT, EMERGENCY
|
||||
*/
|
||||
private String alertType;
|
||||
private ConflictAlertLog.AlertType alertType;
|
||||
|
||||
/**
|
||||
* 告警级别:INFO, WARNING, CRITICAL, EMERGENCY
|
||||
* 告警级别:WARNING, CRITICAL, EMERGENCY
|
||||
*/
|
||||
private String alertLevel;
|
||||
private ConflictAlertLog.AlertLevel alertLevel;
|
||||
|
||||
/**
|
||||
* 告警消息
|
||||
@ -148,20 +149,20 @@ public class PathConflictAlertMessage {
|
||||
* 判断是否为紧急告警
|
||||
*/
|
||||
public boolean isEmergencyAlert() {
|
||||
return "EMERGENCY".equals(alertType) || "CRITICAL".equals(alertLevel);
|
||||
return this.alertLevel == ConflictAlertLog.AlertLevel.EMERGENCY;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为预警
|
||||
*/
|
||||
public boolean isWarning() {
|
||||
return "WARNING".equals(alertType);
|
||||
return this.alertLevel == ConflictAlertLog.AlertLevel.WARNING || this.alertType == ConflictAlertLog.AlertType.CONFLICT_WARNING;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为告警
|
||||
* 判断是否为告警(对应 CRITICAL 级别)
|
||||
*/
|
||||
public boolean isAlert() {
|
||||
return "ALERT".equals(alertType);
|
||||
return this.alertLevel == ConflictAlertLog.AlertLevel.CRITICAL || this.alertLevel == ConflictAlertLog.AlertLevel.EMERGENCY || this.alertType == ConflictAlertLog.AlertType.CONFLICT_ALERT;
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,14 @@
|
||||
-- 删除触发器
|
||||
DROP TRIGGER IF EXISTS trigger_transport_routes_updated_at ON transport_routes;
|
||||
|
||||
-- 删除函数
|
||||
DROP FUNCTION IF EXISTS update_timestamp_column();
|
||||
|
||||
-- 删除表(按依赖顺序)
|
||||
DROP TABLE IF EXISTS object_route_assignments;
|
||||
DROP TABLE IF EXISTS conflict_alert_logs;
|
||||
DROP TABLE IF EXISTS transport_routes;
|
||||
|
||||
-- ============================================
|
||||
-- 基于路径的冲突检测系统数据库表
|
||||
-- 创建时间: 2025-01-17
|
||||
@ -14,9 +25,8 @@ CREATE TABLE IF NOT EXISTS transport_routes (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
|
||||
-- 路径基本信息
|
||||
route_id VARCHAR(50) NOT NULL UNIQUE,
|
||||
route_name VARCHAR(100) NOT NULL,
|
||||
route_type VARCHAR(20) NOT NULL CHECK (route_type IN ('AIRCRAFT', 'UNMANNED_VEHICLE', 'SPECIAL_VEHICLE')),
|
||||
route_type VARCHAR(20) NOT NULL CHECK (route_type IN ('AIRCRAFT', 'UNMANNED_VEHICLE', 'SPECIAL_VEHICLE', 'NORMAL_VEHICLE')),
|
||||
|
||||
-- 路径描述
|
||||
description VARCHAR(500),
|
||||
@ -44,7 +54,6 @@ CREATE TABLE IF NOT EXISTS transport_routes (
|
||||
);
|
||||
|
||||
-- 路径表索引
|
||||
CREATE INDEX idx_transport_routes_route_id ON transport_routes(route_id);
|
||||
CREATE INDEX idx_transport_routes_type ON transport_routes(route_type);
|
||||
CREATE INDEX idx_transport_routes_status ON transport_routes(status);
|
||||
CREATE INDEX idx_transport_routes_geometry_gist ON transport_routes USING GIST(route_geometry);
|
||||
@ -56,22 +65,21 @@ CREATE TABLE IF NOT EXISTS object_route_assignments (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
|
||||
-- 对象信息
|
||||
object_id VARCHAR(50) NOT NULL, -- 车辆ID或航空器标识
|
||||
object_type VARCHAR(20) NOT NULL CHECK (object_type IN ('UNMANNED_VEHICLE', 'AIRCRAFT', 'SPECIAL_VEHICLE')),
|
||||
object_name VARCHAR(100), -- 车牌号或航班号
|
||||
object_type VARCHAR(20) NOT NULL CHECK (object_type IN ('UNMANNED_VEHICLE', 'AIRCRAFT', 'SPECIAL_VEHICLE', 'NORMAL_VEHICLE')),
|
||||
object_name VARCHAR(100) NOT NULL, -- 车牌号或航班号
|
||||
|
||||
-- 路径分配
|
||||
assigned_route_id VARCHAR(50) NOT NULL,
|
||||
assigned_route_id BIGINT NOT NULL,
|
||||
|
||||
-- 时间信息
|
||||
assigned_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- 外键约束
|
||||
FOREIGN KEY (assigned_route_id) REFERENCES transport_routes(route_id)
|
||||
FOREIGN KEY (assigned_route_id) REFERENCES transport_routes(id)
|
||||
);
|
||||
|
||||
-- 对象路径分配表索引
|
||||
CREATE INDEX idx_object_route_assignments_object ON object_route_assignments(object_id, object_type);
|
||||
CREATE INDEX idx_object_route_assignments_object_type ON object_route_assignments(object_type);
|
||||
CREATE INDEX idx_object_route_assignments_route ON object_route_assignments(assigned_route_id);
|
||||
CREATE INDEX idx_object_route_assignments_assigned_at ON object_route_assignments(assigned_at DESC);
|
||||
|
||||
@ -81,12 +89,9 @@ CREATE INDEX idx_object_route_assignments_assigned_at ON object_route_assignment
|
||||
CREATE TABLE IF NOT EXISTS conflict_alert_logs (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
|
||||
-- 关联冲突 (这里假设冲突ID仍然由算法生成并关联,即使path_conflict_detections表被删除)
|
||||
conflict_id VARCHAR(50) NOT NULL, -- 冲突的唯一标识,可能来自实时计算或是一个事件ID
|
||||
|
||||
-- 告警信息
|
||||
alert_type VARCHAR(20) NOT NULL CHECK (alert_type IN ('WARNING', 'ALERT', 'EMERGENCY')),
|
||||
alert_level VARCHAR(20) NOT NULL CHECK (alert_level IN ('INFO', 'WARNING', 'CRITICAL', 'EMERGENCY')),
|
||||
alert_type VARCHAR(20) NOT NULL CHECK (alert_type IN ('CONFLICT_WARNING', 'CONFLICT_ALERT')),
|
||||
alert_level VARCHAR(20) NOT NULL CHECK (alert_level IN ('WARNING', 'CRITICAL', 'EMERGENCY')),
|
||||
alert_message TEXT NOT NULL,
|
||||
|
||||
-- 距离信息
|
||||
@ -99,7 +104,6 @@ CREATE TABLE IF NOT EXISTS conflict_alert_logs (
|
||||
);
|
||||
|
||||
-- 告警记录表索引
|
||||
CREATE INDEX idx_conflict_alerts_conflict_id ON conflict_alert_logs(conflict_id);
|
||||
CREATE INDEX idx_conflict_alerts_type ON conflict_alert_logs(alert_type);
|
||||
CREATE INDEX idx_conflict_alerts_level ON conflict_alert_logs(alert_level);
|
||||
CREATE INDEX idx_conflict_alerts_time ON conflict_alert_logs(alert_time DESC);
|
||||
@ -132,16 +136,32 @@ COMMENT ON TABLE conflict_alert_logs IS '冲突预警告警记录表 - 记录发
|
||||
-- 8. 插入示例数据
|
||||
-- ============================================
|
||||
|
||||
-- 示例航空器滑行路径
|
||||
INSERT INTO transport_routes (route_id, route_name, route_type, description, route_geometry, max_speed_kph, typical_speed_kph) VALUES
|
||||
('TAXI_01', '1号滑行道路径', 'AIRCRAFT', '从停机坪到跑道的标准滑行路径',
|
||||
ST_GeomFromText('LINESTRING(120.084174 36.367162, 120.085 36.368, 120.086 36.369)', 4326),
|
||||
30, 20),
|
||||
('SERVICE_ROAD_A', 'A区服务道路', 'UNMANNED_VEHICLE', '无人车服务区域A的标准路径',
|
||||
ST_GeomFromText('LINESTRING(120.084 36.367, 120.085 36.368, 120.086 36.369, 120.087 36.370)', 4326),
|
||||
25, 15);
|
||||
-- 示例路径数据 (transport_routes)
|
||||
INSERT INTO transport_routes (route_name, route_type, description, route_geometry, max_speed_kph, typical_speed_kph) VALUES
|
||||
('航班CA1234路径', 'AIRCRAFT', 'CA1234航班的预定路径',
|
||||
ST_GeomFromText('LINESTRING(120.086263 36.370484, 120.080996 36.369105)', 4326),
|
||||
50, 50),
|
||||
('航班MU5123路径', 'AIRCRAFT', 'MU5123航班的预定路径',
|
||||
ST_GeomFromText('LINESTRING(120.088076 36.374179, 120.077971 36.371503)', 4326),
|
||||
50, 50),
|
||||
('特勤车鲁B123路径', 'SPECIAL_VEHICLE', '鲁B123特勤车的预定路径',
|
||||
ST_GeomFromText('LINESTRING(120.080801 36.366626, 120.083899 36.367403)', 4326),
|
||||
30, 30),
|
||||
('普通车鲁B234路径', 'UNMANNED_VEHICLE', '鲁B234普通车的预定路径',
|
||||
ST_GeomFromText('LINESTRING(120.087259 36.368299, 120.083899 36.367403)', 4326),
|
||||
30, 30),
|
||||
('无人车鲁B567路径', 'UNMANNED_VEHICLE', '鲁B567无人车的预定路径',
|
||||
ST_GeomFromText('LINESTRING(120.083084 36.369696, 120.084637 36.365617)', 4326),
|
||||
25, 25),
|
||||
('无人车鲁B579路径', 'UNMANNED_VEHICLE', '鲁B579无人车的预定路径',
|
||||
ST_GeomFromText('LINESTRING(120.086965 36.368599, 120.086263 36.370484)', 4326),
|
||||
25, 25);
|
||||
|
||||
-- 示例路径分配
|
||||
INSERT INTO object_route_assignments (object_id, object_type, object_name, assigned_route_id) VALUES
|
||||
('UAV001', 'UNMANNED_VEHICLE', '无人车-001', 'SERVICE_ROAD_A'),
|
||||
('AIRCRAFT001', 'AIRCRAFT', '航班CZ345', 'TAXI_01');
|
||||
-- 示例路径分配数据 (object_route_assignments)
|
||||
INSERT INTO object_route_assignments (object_type, object_name, assigned_route_id) VALUES
|
||||
('AIRCRAFT', 'CA1234', (SELECT id FROM transport_routes WHERE route_name = '航班CA1234路径')),
|
||||
('AIRCRAFT', 'MU5123', (SELECT id FROM transport_routes WHERE route_name = '航班MU5123路径')),
|
||||
('SPECIAL_VEHICLE', '鲁B123', (SELECT id FROM transport_routes WHERE route_name = '特勤车鲁B123路径')),
|
||||
('NORMAL_VEHICLE', '鲁B234', (SELECT id FROM transport_routes WHERE route_name = '普通车鲁B234路径')),
|
||||
('UNMANNED_VEHICLE', '鲁B567', (SELECT id FROM transport_routes WHERE route_name = '无人车鲁B567路径')),
|
||||
('UNMANNED_VEHICLE', '鲁B579', (SELECT id FROM transport_routes WHERE route_name = '无人车鲁B579路径'));
|
||||
Loading…
Reference in New Issue
Block a user