去掉不必要的Websocket消息缓存,完善数据库初始化脚本
This commit is contained in:
parent
cbfb56127f
commit
180dea1845
3
.gitignore
vendored
3
.gitignore
vendored
@ -43,4 +43,5 @@ __pycache__/
|
||||
.venv/
|
||||
|
||||
### 日志 ###
|
||||
logs/
|
||||
logs/
|
||||
*.log
|
||||
@ -9,11 +9,9 @@ import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -23,7 +23,7 @@ public class Response<T> {
|
||||
/**
|
||||
* 响应消息
|
||||
*/
|
||||
private String message;
|
||||
private String msg;
|
||||
|
||||
/**
|
||||
* 响应数据
|
||||
@ -36,7 +36,7 @@ public class Response<T> {
|
||||
public static <T> Response<T> success(T data) {
|
||||
return Response.<T>builder()
|
||||
.status(200)
|
||||
.message("操作成功")
|
||||
.msg("操作成功")
|
||||
.data(data)
|
||||
.build();
|
||||
}
|
||||
@ -47,7 +47,7 @@ public class Response<T> {
|
||||
public static <T> Response<T> success(String message, T data) {
|
||||
return Response.<T>builder()
|
||||
.status(200)
|
||||
.message(message)
|
||||
.msg(message)
|
||||
.data(data)
|
||||
.build();
|
||||
}
|
||||
@ -58,7 +58,7 @@ public class Response<T> {
|
||||
public static <T> Response<T> error(Integer status, String message) {
|
||||
return Response.<T>builder()
|
||||
.status(status)
|
||||
.message(message)
|
||||
.msg(message)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@ package com.dongni.collisionavoidance.common.service;
|
||||
|
||||
import com.dongni.collisionavoidance.common.model.spatial.AirportArea;
|
||||
import com.dongni.collisionavoidance.common.model.repository.AirportAreaRepository;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.locationtech.jts.geom.Coordinate;
|
||||
@ -31,8 +30,6 @@ public class AirportAreaService {
|
||||
private final AirportAreaRepository airportAreaRepository;
|
||||
private final GeometryFactory geometryFactory = new GeometryFactory();
|
||||
private final WKTReader wktReader = new WKTReader(geometryFactory);
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* 保存机场区域配置
|
||||
*/
|
||||
|
||||
@ -5,7 +5,6 @@ import com.dongni.collisionavoidance.common.model.spatial.VehicleLocation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.locationtech.jts.geom.*;
|
||||
import org.locationtech.jts.io.WKTReader;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
@ -23,9 +22,6 @@ public class SpatialQueryService {
|
||||
|
||||
private final VehicleLocationService vehicleLocationService;
|
||||
private final AirportAreaService airportAreaService;
|
||||
private final GeometryFactory geometryFactory = new GeometryFactory();
|
||||
private final WKTReader wktReader = new WKTReader(geometryFactory);
|
||||
|
||||
/**
|
||||
* 综合空间查询结果
|
||||
*/
|
||||
|
||||
@ -117,7 +117,10 @@ public class VehicleLocationCacheService {
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<VehicleLocation> locations = vehicleLocationRedisTemplate.opsForValue().multiGet(keys);
|
||||
|
||||
if (locations == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
Map<String, VehicleLocation> resultMap = new HashMap<>();
|
||||
for (int i = 0; i < vehicleIds.size(); i++) {
|
||||
VehicleLocation location = locations.get(i);
|
||||
|
||||
@ -6,9 +6,6 @@ import com.dongni.collisionavoidance.common.model.spatial.AirportArea;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 车辆权限管理服务
|
||||
* 处理特勤车辆的特殊权限和业务规则
|
||||
@ -223,25 +220,4 @@ public class VehiclePermissionService {
|
||||
|
||||
return "未知权限";
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析允许的车辆类型JSON数组
|
||||
*/
|
||||
private List<String> parseAllowedVehicleTypes(String allowedVehicleTypesJson) {
|
||||
if (allowedVehicleTypesJson == null || allowedVehicleTypesJson.trim().isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
try {
|
||||
// 简单的JSON数组解析,实际项目中可以使用Jackson
|
||||
String cleaned = allowedVehicleTypesJson.replaceAll("[\\[\\]\"\\s]", "");
|
||||
if (cleaned.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
return Arrays.asList(cleaned.split(","));
|
||||
} catch (Exception e) {
|
||||
log.warn("解析允许车辆类型失败: {}", allowedVehicleTypesJson, e);
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -115,7 +115,6 @@ public class DataCollectorDao {
|
||||
* @return 无人车位置信息列表
|
||||
*/
|
||||
public List<UnmannedVehicle> getVehicleLocationInfo() {
|
||||
System.out.println("接口被调用");
|
||||
try {
|
||||
String url = UriComponentsBuilder
|
||||
.fromUriString(vehicleBaseUrl)
|
||||
@ -137,12 +136,12 @@ public class DataCollectorDao {
|
||||
if (response.getStatusCode().is2xxSuccessful()) {
|
||||
List<UnmannedVehicle> responseBody = response.getBody();
|
||||
if (responseBody != null) {
|
||||
log.info("成功获取车辆定位信息,数量: {}", responseBody.size());
|
||||
log.info("成功获取无人车定位信息,数量: {}", responseBody.size());
|
||||
return responseBody;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("获取车辆定位信息时发生异常", e);
|
||||
log.error("获取无人车定位信息时发生异常", e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.emptyList();
|
||||
|
||||
@ -208,6 +208,32 @@ public class DataCollectorService {
|
||||
|
||||
log.info("开始处理 {} 条无人车数据", unmannedVehicles.size());
|
||||
|
||||
// 先推送WebSocket位置更新事件
|
||||
for (UnmannedVehicle vehicle : unmannedVehicles) {
|
||||
try {
|
||||
PositionUpdatePayload.Position position = PositionUpdatePayload.Position.builder()
|
||||
.latitude(vehicle.getCurrentPosition().getLatitude())
|
||||
.longitude(vehicle.getCurrentPosition().getLongitude())
|
||||
.build();
|
||||
PositionUpdatePayload payload = PositionUpdatePayload.builder()
|
||||
.objectId(vehicle.getVehicleId())
|
||||
.objectType(MovingObjectType.UNMANNED_VEHICLE.name())
|
||||
.position(position)
|
||||
.heading(vehicle.getHeading())
|
||||
.speed(vehicle.getVelocity() != null ? vehicle.getVelocity().getSpeed() : null)
|
||||
.timestamp(System.currentTimeMillis() * 1000) // 微秒级时间戳
|
||||
.build();
|
||||
eventPublisher.publishEvent(new PositionUpdateEvent(payload));
|
||||
log.debug("处理无人车数据并发布事件: {} (车辆ID: {}, 位置: {}, {})",
|
||||
vehicle.getVehicleId(),
|
||||
vehicle.getVehicleId(),
|
||||
vehicle.getCurrentPosition().getLongitude(),
|
||||
vehicle.getCurrentPosition().getLatitude());
|
||||
} catch (Exception e) {
|
||||
log.error("处理无人车数据异常: vehicleId={}", vehicle.getVehicleId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// 转换为VehicleLocation对象列表
|
||||
List<com.dongni.collisionavoidance.common.model.spatial.VehicleLocation> vehicleLocations =
|
||||
unmannedVehicles.stream()
|
||||
|
||||
@ -254,13 +254,20 @@ public class UnmannedVehicleControlService {
|
||||
|
||||
ResponseEntity<VehicleStateInfo[]> response = restTemplate.exchange(
|
||||
url, HttpMethod.POST, requestEntity, VehicleStateInfo[].class);
|
||||
|
||||
|
||||
if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null && response.getBody().length > 0) {
|
||||
VehicleStateInfo stateInfo = response.getBody()[0];
|
||||
logger.info("从外部系统获取车辆状态成功: vehicleId={}", vehicleId);
|
||||
return stateInfo;
|
||||
if (response != null && response.getStatusCode().is2xxSuccessful()) {
|
||||
VehicleStateInfo[] body = response.getBody();
|
||||
if (body != null && body.length > 0) {
|
||||
VehicleStateInfo stateInfo = body[0];
|
||||
logger.info("从外部系统获取车辆状态成功: vehicleId={}", vehicleId);
|
||||
return stateInfo;
|
||||
} else {
|
||||
logger.warn("外部系统未返回车辆状态: vehicleId={}", vehicleId);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
logger.warn("外部系统未返回车辆状态: vehicleId={}", vehicleId);
|
||||
logger.warn("外部系统调用失败: vehicleId={}, status={}", vehicleId, response != null ? response.getStatusCode() : "无响应");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@ -4,8 +4,6 @@ import com.dongni.collisionavoidance.common.model.MovingObjectType;
|
||||
import com.dongni.collisionavoidance.rule.model.entity.SpatialRule;
|
||||
import com.dongni.collisionavoidance.rule.model.enums.RuleCategory;
|
||||
|
||||
import org.locationtech.jts.geom.Point;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@ -22,7 +22,6 @@ import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@ -39,10 +38,6 @@ public class WebSocketMessageBroadcaster {
|
||||
// 统一推送主题,控制台接收所有消息
|
||||
private static final String UNIFIED_TOPIC = "/topic/realtime";
|
||||
|
||||
// 是否启用消息缓存(测试环境中禁用以避免序列化问题)
|
||||
@Value("${websocket.message.cache.enabled:true}")
|
||||
private boolean messageCacheEnabled;
|
||||
|
||||
@Autowired
|
||||
public WebSocketMessageBroadcaster(
|
||||
SimpMessagingTemplate messagingTemplate,
|
||||
@ -174,10 +169,8 @@ public class WebSocketMessageBroadcaster {
|
||||
// 立即推送到控制台
|
||||
messagingTemplate.convertAndSend(UNIFIED_TOPIC, message);
|
||||
|
||||
// 缓存消息用于重连恢复(测试环境中可能禁用)
|
||||
if (messageCacheEnabled) {
|
||||
messageCacheService.cacheMessage(message);
|
||||
}
|
||||
// 缓存消息用于重连恢复
|
||||
messageCacheService.cacheMessage(message);
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to broadcast message: " + e.getMessage());
|
||||
|
||||
@ -57,7 +57,6 @@ public class MessageCacheService {
|
||||
* @param count 获取消息数量
|
||||
* @return 最近的消息列表
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<UniversalMessage<?>> getRecentMessages(String messageType, int count) {
|
||||
try {
|
||||
String key = CACHE_KEY_PREFIX + messageType;
|
||||
|
||||
@ -0,0 +1,281 @@
|
||||
-- ============================================
|
||||
-- ⚠️ 脚本执行前警告:本操作将删除并重建整个数据库,所有原有数据将丢失!
|
||||
-- 如仅需初始化结构,请注释掉下方 DROP/CREATE DATABASE 语句。
|
||||
-- ============================================
|
||||
|
||||
-- 0. 删除并重建数据库(请根据实际数据库名修改)
|
||||
-- 需以超级用户身份连接到postgres或其他管理数据库后执行:
|
||||
--
|
||||
-- DROP DATABASE IF EXISTS collision_avoidance;
|
||||
-- CREATE DATABASE collision_avoidance WITH ENCODING='UTF8' TEMPLATE=template0;
|
||||
-- \c collision_avoidance
|
||||
|
||||
-- ============================================
|
||||
-- 碰撞避免系统数据库重建脚本(PostgreSQL 17 + PostGIS)
|
||||
-- 适用场景:全新初始化/重建,确保所有表结构、索引、触发器、注释、权限一次性到位
|
||||
-- 依赖扩展:postgis, postgis_topology
|
||||
-- 执行权限:需超级用户权限(启用扩展),后续表操作可用普通DBA权限
|
||||
-- ============================================
|
||||
|
||||
-- 1. 启用PostGIS扩展
|
||||
CREATE EXTENSION IF NOT EXISTS postgis;
|
||||
CREATE EXTENSION IF NOT EXISTS postgis_topology;
|
||||
|
||||
-- 2. 通用函数与触发器
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION cleanup_old_vehicle_locations()
|
||||
RETURNS INTEGER AS $$
|
||||
DECLARE
|
||||
deleted_count INTEGER;
|
||||
BEGIN
|
||||
DELETE FROM vehicle_locations
|
||||
WHERE created_at < CURRENT_TIMESTAMP - INTERVAL '30 days';
|
||||
GET DIAGNOSTICS deleted_count = ROW_COUNT;
|
||||
RAISE NOTICE 'Cleaned up % old vehicle location records', deleted_count;
|
||||
RETURN deleted_count;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- 3. 车辆位置表
|
||||
CREATE TABLE vehicle_locations (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
vehicle_id VARCHAR(50) NOT NULL,
|
||||
vehicle_type VARCHAR(20) NOT NULL CHECK (vehicle_type IN ('AIRCRAFT', 'AIRPORT_VEHICLE', 'UNMANNED_VEHICLE')),
|
||||
location GEOMETRY(POINT, 4326) NOT NULL,
|
||||
altitude DOUBLE PRECISION,
|
||||
heading DOUBLE PRECISION CHECK (heading >= 0 AND heading < 360),
|
||||
speed DOUBLE PRECISION CHECK (speed >= 0),
|
||||
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
data_quality VARCHAR(20),
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX idx_vehicle_locations_vehicle_id ON vehicle_locations(vehicle_id);
|
||||
CREATE INDEX idx_vehicle_locations_vehicle_type ON vehicle_locations(vehicle_type);
|
||||
CREATE INDEX idx_vehicle_locations_timestamp ON vehicle_locations(timestamp DESC);
|
||||
CREATE INDEX idx_vehicle_locations_created_at ON vehicle_locations(created_at);
|
||||
CREATE INDEX idx_vehicle_locations_location_gist ON vehicle_locations USING GIST(location);
|
||||
CREATE INDEX idx_vehicle_locations_compound ON vehicle_locations(vehicle_id, timestamp DESC);
|
||||
CREATE INDEX idx_vehicle_locations_type_time ON vehicle_locations(vehicle_type, timestamp DESC);
|
||||
CREATE TRIGGER trigger_vehicle_locations_updated_at
|
||||
BEFORE UPDATE ON vehicle_locations
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- 4. 机场区域表(纯地理属性)
|
||||
CREATE TABLE airport_areas (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
area_id VARCHAR(50) NOT NULL UNIQUE,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
type VARCHAR(30) NOT NULL,
|
||||
description VARCHAR(500),
|
||||
boundary GEOMETRY(POLYGON, 4326) NOT NULL,
|
||||
active_time TIMESTAMP WITH TIME ZONE,
|
||||
expiry_time TIMESTAMP WITH TIME ZONE,
|
||||
enabled BOOLEAN NOT NULL DEFAULT true,
|
||||
priority INTEGER NOT NULL DEFAULT 1 CHECK (priority >= 1),
|
||||
version BIGINT NOT NULL DEFAULT 1,
|
||||
area_sqm DOUBLE PRECISION,
|
||||
functional_category VARCHAR(50),
|
||||
parent_area_id VARCHAR(50),
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX idx_airport_areas_area_id ON airport_areas(area_id);
|
||||
CREATE INDEX idx_airport_areas_name ON airport_areas(name);
|
||||
CREATE INDEX idx_airport_areas_type ON airport_areas(type);
|
||||
CREATE INDEX idx_airport_areas_priority ON airport_areas(priority DESC);
|
||||
CREATE INDEX idx_airport_areas_enabled ON airport_areas(enabled);
|
||||
CREATE INDEX idx_airport_areas_version ON airport_areas(version);
|
||||
CREATE INDEX idx_airport_areas_boundary_gist ON airport_areas USING GIST(boundary);
|
||||
CREATE INDEX idx_airport_areas_type_enabled ON airport_areas(type, enabled);
|
||||
CREATE INDEX idx_airport_areas_enabled_priority ON airport_areas(enabled, priority DESC);
|
||||
CREATE INDEX idx_airport_areas_functional_category ON airport_areas(functional_category);
|
||||
CREATE INDEX idx_airport_areas_parent_area_id ON airport_areas(parent_area_id);
|
||||
CREATE TRIGGER trigger_airport_areas_updated_at
|
||||
BEFORE UPDATE ON airport_areas
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_updated_at_column();
|
||||
COMMENT ON TABLE airport_areas IS '机场区域表 - 纯地理功能概念,定义机场内各个区域的地理边界和功能属性。安全控制规则在geofences表中配置。';
|
||||
|
||||
-- 5. 车辆轨迹表
|
||||
CREATE TABLE vehicle_trajectories (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
vehicle_id VARCHAR(50) NOT NULL,
|
||||
trajectory_date DATE NOT NULL,
|
||||
trajectory_line GEOMETRY(LINESTRING, 4326),
|
||||
total_distance DOUBLE PRECISION,
|
||||
max_speed DOUBLE PRECISION,
|
||||
avg_speed DOUBLE PRECISION,
|
||||
duration_seconds INTEGER,
|
||||
point_count INTEGER,
|
||||
start_time TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
end_time TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX idx_vehicle_trajectories_vehicle_id ON vehicle_trajectories(vehicle_id);
|
||||
CREATE INDEX idx_vehicle_trajectories_date ON vehicle_trajectories(trajectory_date DESC);
|
||||
CREATE INDEX idx_vehicle_trajectories_start_time ON vehicle_trajectories(start_time DESC);
|
||||
CREATE INDEX idx_vehicle_trajectories_line_gist ON vehicle_trajectories USING GIST(trajectory_line);
|
||||
CREATE INDEX idx_vehicle_trajectories_compound ON vehicle_trajectories(vehicle_id, trajectory_date DESC);
|
||||
|
||||
-- 6. 无人车控制指令表
|
||||
CREATE TABLE vehicle_commands (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
trans_id VARCHAR(100) NOT NULL,
|
||||
timestamp TIMESTAMP NOT NULL,
|
||||
vehicle_id VARCHAR(50) NOT NULL,
|
||||
command_type VARCHAR(20) NOT NULL CHECK (command_type IN ('ALERT', 'SIGNAL', 'WARNING', 'RESUME', 'PARKING')),
|
||||
command_reason VARCHAR(30) NOT NULL CHECK (command_reason IN ('TRAFFIC_LIGHT', 'AIRCRAFT_CROSSING', 'SPECIAL_VEHICLE', 'AIRCRAFT_PUSH', 'RESUME_TRAFFIC', 'PARKING_SIDE')),
|
||||
signal_state VARCHAR(10) CHECK (signal_state IN ('RED', 'YELLOW', 'GREEN')),
|
||||
intersection_id VARCHAR(50),
|
||||
target_location GEOMETRY(POINT, 4326) NOT NULL,
|
||||
relative_speed DOUBLE PRECISION,
|
||||
relative_motion_x DOUBLE PRECISION,
|
||||
relative_motion_y DOUBLE PRECISION,
|
||||
min_distance DOUBLE PRECISION,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX idx_vehicle_commands_vehicle_id ON vehicle_commands(vehicle_id);
|
||||
CREATE INDEX idx_vehicle_commands_timestamp ON vehicle_commands(timestamp DESC);
|
||||
CREATE INDEX idx_vehicle_commands_trans_id ON vehicle_commands(trans_id);
|
||||
CREATE INDEX idx_vehicle_commands_command_type ON vehicle_commands(command_type);
|
||||
CREATE INDEX idx_vehicle_commands_created_at ON vehicle_commands(created_at);
|
||||
CREATE INDEX idx_vehicle_commands_location_gist ON vehicle_commands USING GIST(target_location);
|
||||
CREATE INDEX idx_vehicle_commands_vehicle_time ON vehicle_commands(vehicle_id, timestamp DESC);
|
||||
CREATE INDEX idx_vehicle_commands_type_time ON vehicle_commands(command_type, timestamp DESC);
|
||||
COMMENT ON TABLE vehicle_commands IS '无人车控制指令表,用于存储发送给无人车的控制指令,支持轨迹回放和日志审计';
|
||||
|
||||
-- 7. 空间规则表(以V009为准)
|
||||
CREATE TABLE spatial_rules (
|
||||
rule_id VARCHAR(50) PRIMARY KEY,
|
||||
rule_name VARCHAR(200) NOT NULL,
|
||||
description TEXT,
|
||||
rule_category VARCHAR(50) NOT NULL CHECK (rule_category IN (
|
||||
'ACCESS_CONTROL', 'SPEED_LIMIT', 'VEHICLE_RESTRICTION',
|
||||
'HEIGHT_LIMIT', 'WEIGHT_LIMIT', 'TIME_RESTRICTION',
|
||||
'PRIORITY_CONTROL', 'SAFETY_ZONE', 'CUSTOM'
|
||||
)),
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE' CHECK (status IN (
|
||||
'ACTIVE', 'INACTIVE', 'SUSPENDED', 'EXPIRED'
|
||||
)),
|
||||
priority INTEGER NOT NULL DEFAULT 1 CHECK (priority >= 1),
|
||||
spatial_object_type VARCHAR(30) NOT NULL CHECK (spatial_object_type IN (
|
||||
'AREA', 'ROAD', 'INTERSECTION', 'CUSTOM_GEOMETRY'
|
||||
)),
|
||||
spatial_object_id VARCHAR(50),
|
||||
custom_geometry GEOMETRY(POLYGON, 4326),
|
||||
rule_parameters JSONB DEFAULT '{}',
|
||||
alert_level VARCHAR(20) NOT NULL DEFAULT 'WARNING' CHECK (alert_level IN (
|
||||
'INFO', 'WARNING', 'CRITICAL', 'EMERGENCY'
|
||||
)),
|
||||
alert_message VARCHAR(500),
|
||||
effective_start_time TIMESTAMP,
|
||||
effective_end_time TIMESTAMP,
|
||||
daily_start_time TIME,
|
||||
daily_end_time TIME,
|
||||
weekday_pattern INTEGER,
|
||||
time_patterns JSONB,
|
||||
created_by VARCHAR(100),
|
||||
updated_by VARCHAR(100),
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
version BIGINT NOT NULL DEFAULT 1
|
||||
);
|
||||
CREATE INDEX idx_spatial_rules_status ON spatial_rules(status);
|
||||
CREATE INDEX idx_spatial_rules_category ON spatial_rules(rule_category);
|
||||
CREATE INDEX idx_spatial_rules_object_type ON spatial_rules(spatial_object_type);
|
||||
CREATE INDEX idx_spatial_rules_priority ON spatial_rules(priority DESC);
|
||||
CREATE INDEX idx_spatial_rules_spatial_object ON spatial_rules(spatial_object_type, spatial_object_id);
|
||||
CREATE INDEX idx_spatial_rules_effective_time ON spatial_rules(effective_start_time, effective_end_time);
|
||||
CREATE INDEX idx_spatial_rules_daily_time ON spatial_rules(daily_start_time, daily_end_time);
|
||||
CREATE INDEX idx_spatial_rules_custom_geometry_gist ON spatial_rules USING GIST(custom_geometry);
|
||||
CREATE INDEX idx_spatial_rules_parameters_gin ON spatial_rules USING GIN(rule_parameters);
|
||||
CREATE INDEX idx_spatial_rules_time_patterns_gin ON spatial_rules USING GIN(time_patterns);
|
||||
CREATE TRIGGER trigger_spatial_rules_updated_at
|
||||
BEFORE UPDATE ON spatial_rules
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_updated_at_column();
|
||||
COMMENT ON TABLE spatial_rules IS '空间规则表 - 统一管理所有空间安全规则,支持多层次时间控制';
|
||||
|
||||
-- 8. 空间规则允许车辆类型关联表
|
||||
CREATE TABLE spatial_rule_vehicle_types (
|
||||
rule_id VARCHAR(50) NOT NULL,
|
||||
vehicle_type VARCHAR(30) NOT NULL CHECK (vehicle_type IN (
|
||||
'AIRCRAFT', 'AIRPORT_VEHICLE', 'UNMANNED_VEHICLE', 'SPECIAL_VEHICLE'
|
||||
)),
|
||||
PRIMARY KEY (rule_id, vehicle_type),
|
||||
FOREIGN KEY (rule_id) REFERENCES spatial_rules(rule_id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE INDEX idx_spatial_rule_vehicle_types_rule_id ON spatial_rule_vehicle_types(rule_id);
|
||||
CREATE INDEX idx_spatial_rule_vehicle_types_vehicle_type ON spatial_rule_vehicle_types(vehicle_type);
|
||||
COMMENT ON TABLE spatial_rule_vehicle_types IS '空间规则允许车辆类型关联表';
|
||||
|
||||
-- 9. 规则违规事件表
|
||||
CREATE TABLE rule_violation_events (
|
||||
event_id VARCHAR(36) PRIMARY KEY,
|
||||
rule_id VARCHAR(50) NOT NULL,
|
||||
vehicle_id VARCHAR(50) NOT NULL,
|
||||
vehicle_type VARCHAR(30) NOT NULL CHECK (vehicle_type IN (
|
||||
'AIRCRAFT', 'AIRPORT_VEHICLE', 'UNMANNED_VEHICLE', 'SPECIAL_VEHICLE'
|
||||
)),
|
||||
violation_type VARCHAR(50) NOT NULL,
|
||||
alert_level VARCHAR(20) NOT NULL CHECK (alert_level IN (
|
||||
'INFO', 'WARNING', 'CRITICAL', 'EMERGENCY'
|
||||
)),
|
||||
violation_timestamp TIMESTAMP NOT NULL,
|
||||
violation_location GEOMETRY(POINT, 4326),
|
||||
violation_description TEXT,
|
||||
vehicle_state JSONB,
|
||||
rule_details JSONB,
|
||||
severity_score INTEGER NOT NULL,
|
||||
processed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
processed_at TIMESTAMP,
|
||||
processed_by VARCHAR(100),
|
||||
processing_result TEXT,
|
||||
response_actions JSONB,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
metadata JSONB
|
||||
);
|
||||
CREATE INDEX idx_violation_events_vehicle_id ON rule_violation_events(vehicle_id);
|
||||
CREATE INDEX idx_violation_events_rule_id ON rule_violation_events(rule_id);
|
||||
CREATE INDEX idx_violation_events_violation_type ON rule_violation_events(violation_type);
|
||||
CREATE INDEX idx_violation_events_alert_level ON rule_violation_events(alert_level);
|
||||
CREATE INDEX idx_violation_events_timestamp ON rule_violation_events(violation_timestamp DESC);
|
||||
CREATE INDEX idx_violation_events_processed ON rule_violation_events(processed);
|
||||
CREATE INDEX idx_violation_events_location_gist ON rule_violation_events USING GIST(violation_location);
|
||||
COMMENT ON TABLE rule_violation_events IS '规则违规事件表 - 记录所有违规事件及其处理状态';
|
||||
|
||||
-- 10. 权限设置(可选)
|
||||
-- GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO collision_avoidance_app;
|
||||
-- GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO collision_avoidance_app;
|
||||
|
||||
-- 11. 示例数据(可选,按需取消注释)
|
||||
/*
|
||||
INSERT INTO airport_areas (area_id, name, type, description, boundary, priority, enabled, functional_category) VALUES
|
||||
('runway_01', 'Runway 01', 'RUNWAY', '主跑道01,长度3200米',
|
||||
ST_GeomFromText('POLYGON((116.5 39.8, 116.52 39.8, 116.52 39.81, 116.5 39.81, 116.5 39.8))', 4326),
|
||||
10, true, 'AIRCRAFT_OPERATIONS'),
|
||||
('taxiway_a', 'Taxiway A', 'TAXIWAY', '连接跑道和停机坪的滑行道A',
|
||||
ST_GeomFromText('POLYGON((116.51 39.79, 116.515 39.79, 116.515 39.795, 116.51 39.795, 116.51 39.79))', 4326),
|
||||
5, true, 'AIRCRAFT_MOVEMENT'),
|
||||
('terminal_area', 'Terminal Area', 'TERMINAL', 'T1航站楼及周边区域',
|
||||
ST_GeomFromText('POLYGON((116.48 39.78, 116.50 39.78, 116.50 39.80, 116.48 39.80, 116.48 39.78))', 4326),
|
||||
3, true, 'PASSENGER_SERVICES'),
|
||||
('temp_construction_01', 'Temporary Construction Zone', 'TEMPORARY_CONSTRUCTION', '滑行道扩建临时施工区域',
|
||||
ST_GeomFromText('POLYGON((116.49 39.785, 116.495 39.785, 116.495 39.790, 116.49 39.790, 116.49 39.785))', 4326),
|
||||
1, true, 'CONSTRUCTION');
|
||||
*/
|
||||
|
||||
-- 12. 结尾说明
|
||||
-- ============================================
|
||||
-- 数据库结构初始化完成。请根据实际业务需要补充数据、权限和后续配置。
|
||||
-- ============================================
|
||||
@ -138,7 +138,7 @@ class AirportAreaServiceIntegrationTest {
|
||||
assertThat(area).isNotNull();
|
||||
assertThat(area.getName()).isEqualTo("测试区域");
|
||||
assertThat(area.getType()).isEqualTo("TEST");
|
||||
assertThat(area.getBoundary()).isNotNull();
|
||||
assertThat((Object)area.getBoundary()).isNotNull();
|
||||
assertThat(area.getBoundary().getSRID()).isEqualTo(4326);
|
||||
}
|
||||
|
||||
|
||||
@ -228,7 +228,7 @@ class AirportAreaRepositoryTest {
|
||||
assertThat(area).isPresent();
|
||||
|
||||
Polygon boundary = area.get().getBoundary();
|
||||
assertThat(boundary).isNotNull();
|
||||
assertThat((Object) boundary).isNotNull();
|
||||
assertThat(boundary.getSRID()).isEqualTo(4326); // WGS84坐标系
|
||||
assertThat(boundary.isValid()).isTrue();
|
||||
assertThat(boundary.getArea()).isGreaterThan(0);
|
||||
@ -271,7 +271,7 @@ class AirportAreaRepositoryTest {
|
||||
|
||||
// 测试中心点计算
|
||||
Point centroid = aa.getCentroid();
|
||||
assertThat(centroid).isNotNull();
|
||||
assertThat((Object) centroid).isNotNull();
|
||||
assertThat(centroid.getX()).isBetween(116.0, 117.0);
|
||||
assertThat(centroid.getY()).isBetween(39.0, 40.0);
|
||||
|
||||
|
||||
@ -250,7 +250,7 @@ class VehicleLocationRepositoryTest {
|
||||
assertThat(vehicle).isPresent();
|
||||
|
||||
Point location = vehicle.get().getLocation();
|
||||
assertThat(location).isNotNull();
|
||||
assertThat((Object)location).isNotNull();
|
||||
assertThat(location.getSRID()).isEqualTo(4326); // WGS84坐标系
|
||||
assertThat(location.getX()).isBetween(116.0, 117.0);
|
||||
assertThat(location.getY()).isBetween(39.0, 40.0);
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package com.dongni.collisionavoidance.controller;
|
||||
|
||||
import com.dongni.collisionavoidance.common.model.dto.Response;
|
||||
import com.dongni.collisionavoidance.common.model.spatial.VehicleLocation;
|
||||
import com.dongni.collisionavoidance.datacollector.model.dto.VehicleCommand;
|
||||
import com.dongni.collisionavoidance.datacollector.model.dto.VehicleStateInfo;
|
||||
@ -74,7 +73,7 @@ class UnmannedVehicleControllerTest {
|
||||
.content(objectMapper.writeValueAsString(command)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.status").value(200))
|
||||
.andExpect(jsonPath("$.message").value("控制指令执行成功"));
|
||||
.andExpect(jsonPath("$.msg").value("控制指令执行成功"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -6,8 +6,6 @@ import com.dongni.collisionavoidance.common.service.VehicleLocationService;
|
||||
import com.dongni.collisionavoidance.datacollector.model.entity.VehicleCommandEntity;
|
||||
import com.dongni.collisionavoidance.datacollector.model.enums.CommandReason;
|
||||
import com.dongni.collisionavoidance.datacollector.model.enums.CommandType;
|
||||
import com.dongni.collisionavoidance.datacollector.repository.VehicleCommandRepository;
|
||||
import com.dongni.collisionavoidance.datacollector.service.VehicleDataPersistenceService;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -44,9 +42,6 @@ class VehicleDataPersistenceServiceIntegrationTest {
|
||||
@Autowired
|
||||
private VehicleLocationService vehicleLocationService;
|
||||
|
||||
@Autowired
|
||||
private VehicleCommandRepository vehicleCommandRepository;
|
||||
|
||||
private final GeometryFactory geometryFactory = new GeometryFactory();
|
||||
|
||||
/**
|
||||
|
||||
@ -130,7 +130,7 @@ class RuleExecutionEngineTest {
|
||||
|
||||
// 验证事件发布(只验证开始事件,因为完成事件可能因业务逻辑而不同)
|
||||
verify(ruleEventPublisher).publishRuleExecutionStarted(eq(testRule), eq(vehicleId),
|
||||
any(), any(Map.class));
|
||||
any(), anyMap());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -147,7 +147,7 @@ class RuleExecutionEngineTest {
|
||||
assertNotNull(result);
|
||||
// 验证执行开始事件
|
||||
verify(ruleEventPublisher).publishRuleExecutionStarted(eq(testRule), eq(vehicleId),
|
||||
any(), any(Map.class));
|
||||
any(), anyMap());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -184,7 +184,7 @@ class RuleExecutionEngineTest {
|
||||
|
||||
// 验证每个规则都被执行
|
||||
verify(ruleEventPublisher, times(2)).publishRuleExecutionStarted(
|
||||
any(SpatialRule.class), eq(vehicleId), any(), any(Map.class));
|
||||
any(SpatialRule.class), eq(vehicleId), any(), anyMap());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -16,7 +16,6 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.locationtech.jts.geom.Coordinate;
|
||||
import org.locationtech.jts.geom.GeometryFactory;
|
||||
import org.locationtech.jts.geom.Point;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
@ -126,7 +125,7 @@ public class RuleViolationRealtimePushTest {
|
||||
any(SpatialRule.class),
|
||||
any(VehicleLocation.class),
|
||||
any(RuleExecutionResult.class),
|
||||
any(Map.class)
|
||||
anyMap()
|
||||
)).thenReturn(testViolationEvent);
|
||||
|
||||
// When: 创建违规事件
|
||||
@ -146,7 +145,7 @@ public class RuleViolationRealtimePushTest {
|
||||
any(SpatialRule.class),
|
||||
any(VehicleLocation.class),
|
||||
any(RuleExecutionResult.class),
|
||||
any(Map.class)
|
||||
anyMap()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package com.dongni.collisionavoidance.geofence.service;
|
||||
|
||||
import com.dongni.collisionavoidance.common.model.MovingObjectType;
|
||||
import com.dongni.collisionavoidance.common.service.VehicleLocationService;
|
||||
import com.dongni.collisionavoidance.common.model.spatial.VehicleLocation;
|
||||
import com.dongni.collisionavoidance.geofence.model.enums.*;
|
||||
import com.dongni.collisionavoidance.rule.event.RuleViolationEvent;
|
||||
@ -57,9 +56,6 @@ public class SpatialRuleIntegrationTest {
|
||||
@Autowired
|
||||
private RealTimeViolationDetector realTimeViolationDetector;
|
||||
|
||||
@Autowired
|
||||
private VehicleLocationService vehicleLocationService;
|
||||
|
||||
@Autowired
|
||||
private SpatialRuleRepository spatialRuleRepository;
|
||||
|
||||
|
||||
@ -228,12 +228,4 @@ class RoadNetworkServiceIntegrationTest {
|
||||
assertThat(roadNetworkService.getRoadById("road-invalid-geom")).isEmpty();
|
||||
// Also check spatial index doesn't contain it (harder to test directly)
|
||||
}
|
||||
|
||||
// Helper method example (if roadInfoMap was accessible or we add a getter for tests)
|
||||
private boolean roadInfoMapContainsName(String name) {
|
||||
// This requires access to roadInfoMap or a dedicated getter in the service
|
||||
// return roadNetworkService.getRoadInfoMap().values().stream()
|
||||
// .anyMatch(info -> name.equals(info.getName()));
|
||||
return false; // Placeholder
|
||||
}
|
||||
}
|
||||
@ -2,9 +2,6 @@ package com.dongni.collisionavoidance.websocket.event;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.dongni.collisionavoidance.websocket.event.CollisionWarningEvent;
|
||||
import com.dongni.collisionavoidance.websocket.event.PositionUpdateEvent;
|
||||
import com.dongni.collisionavoidance.websocket.event.TrafficLightStatusEvent;
|
||||
import com.dongni.collisionavoidance.websocket.message.*;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@ -24,29 +24,12 @@ spring:
|
||||
# 禁用Flyway在测试中执行 - 使用JPA自动建表
|
||||
flyway:
|
||||
enabled: false
|
||||
|
||||
# 禁用MongoDB自动配置
|
||||
autoconfigure:
|
||||
exclude:
|
||||
- org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration
|
||||
- org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration
|
||||
|
||||
# 禁用数据服务
|
||||
data:
|
||||
mongodb:
|
||||
auto-index-creation: false
|
||||
redis:
|
||||
repositories:
|
||||
enabled: false
|
||||
|
||||
# 禁用Kafka
|
||||
kafka:
|
||||
bootstrap-servers:
|
||||
producer:
|
||||
bootstrap-servers:
|
||||
consumer:
|
||||
bootstrap-servers:
|
||||
auto-startup: false
|
||||
|
||||
# 禁用调度和异步任务
|
||||
task:
|
||||
@ -64,12 +47,6 @@ spring:
|
||||
# 测试模式标记
|
||||
test-mode: true
|
||||
|
||||
# WebSocket配置
|
||||
websocket:
|
||||
message:
|
||||
cache:
|
||||
enabled: false # 禁用消息缓存以避免序列化问题
|
||||
|
||||
# 数据采集器配置
|
||||
data:
|
||||
collector:
|
||||
@ -86,14 +63,6 @@ data:
|
||||
processor:
|
||||
enabled: false # 禁用数据处理器
|
||||
|
||||
# 规则检测配置
|
||||
rule:
|
||||
detection:
|
||||
async:
|
||||
enabled: false # 禁用异步规则检测,避免测试中的复杂事件链
|
||||
|
||||
|
||||
|
||||
# 日志配置
|
||||
logging:
|
||||
level:
|
||||
|
||||
@ -133,6 +133,22 @@ if qn002_dist > 0:
|
||||
|
||||
# 修改车辆数据中 QN002 的初始位置和方向
|
||||
vehicle_data = [
|
||||
{
|
||||
"vehicleNo": "TQ001", # 特勤车
|
||||
"longitude": POINT_T4["longitude"],
|
||||
"latitude": POINT_T4["latitude"],
|
||||
"time": int(time.time() * 1000),
|
||||
"direction": 270.0, # 向西方向为270度
|
||||
"speed": DEFAULT_VEHICLE_SPEED
|
||||
},
|
||||
{
|
||||
"vehicleNo": "BD001", # 摆渡车
|
||||
"longitude": POINT_T5["longitude"],
|
||||
"latitude": POINT_T5["latitude"],
|
||||
"time": int(time.time() * 1000),
|
||||
"direction": 0.0, # 向北方向为0度
|
||||
"speed": DEFAULT_VEHICLE_SPEED
|
||||
},
|
||||
{
|
||||
"vehicleNo": "QN001", # 无人车1
|
||||
"longitude": POINT_T1["longitude"],
|
||||
@ -148,17 +164,13 @@ vehicle_data = [
|
||||
"time": int(time.time() * 1000),
|
||||
"direction": 90.0, # 向东方向为90度
|
||||
"speed": DEFAULT_VEHICLE_SPEED
|
||||
},
|
||||
{
|
||||
"vehicleNo": "TQ001", # 特勤车
|
||||
"longitude": POINT_T4["longitude"],
|
||||
"latitude": POINT_T4["latitude"],
|
||||
"time": int(time.time() * 1000),
|
||||
"direction": 270.0, # 向西方向为270度
|
||||
"speed": DEFAULT_VEHICLE_SPEED
|
||||
}
|
||||
]
|
||||
|
||||
# 拆分
|
||||
airport_vehicle_data = [v for v in vehicle_data if not v["vehicleNo"].startswith("QN")]
|
||||
unmanned_vehicle_data = [v for v in vehicle_data if v["vehicleNo"].startswith("QN")]
|
||||
|
||||
# 添加车辆状态类
|
||||
class VehicleState:
|
||||
def __init__(self, vehicle_no):
|
||||
@ -872,7 +884,7 @@ def switch_traffic_light_state():
|
||||
|
||||
@app.route('/openApi/getCurrentVehiclePositions', methods=['GET', 'OPTIONS'])
|
||||
def get_vehicle_positions():
|
||||
"""获取当前车辆位置信息"""
|
||||
"""获取当前车辆位置信息(仅机场车辆)"""
|
||||
if request.method == 'OPTIONS':
|
||||
return '', 204
|
||||
if not check_auth():
|
||||
@ -884,24 +896,29 @@ def get_vehicle_positions():
|
||||
global last_vehicle_update_time
|
||||
current_time = time.time()
|
||||
elapsed_time = current_time - last_vehicle_update_time
|
||||
|
||||
try:
|
||||
# 统一处理红绿灯状态切换
|
||||
# switch_traffic_light_state() # 采用模拟的红绿灯脚本,注释掉此处的调用
|
||||
|
||||
# 只在达到更新间隔时更新位置
|
||||
if elapsed_time >= UPDATE_INTERVAL:
|
||||
for vehicle in vehicle_data:
|
||||
for vehicle in airport_vehicle_data:
|
||||
update_vehicle_position(vehicle, UPDATE_INTERVAL)
|
||||
vehicle["time"] = int(current_time * 1000)
|
||||
last_vehicle_update_time = current_time
|
||||
|
||||
response_data = []
|
||||
for v in airport_vehicle_data:
|
||||
v_out = {
|
||||
"vehicleNo": v.get("vehicleNo"),
|
||||
"longitude": v.get("longitude"),
|
||||
"latitude": v.get("latitude"),
|
||||
"time": v.get("time"),
|
||||
"direction": v.get("direction"),
|
||||
"speed": v.get("speed")
|
||||
}
|
||||
response_data.append(v_out)
|
||||
return jsonify({
|
||||
"status": 200,
|
||||
"msg": "当前车辆实时位置数据",
|
||||
"data": vehicle_data
|
||||
"data": response_data
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error in get_vehicle_positions: {str(e)}")
|
||||
return jsonify({
|
||||
@ -1036,18 +1053,17 @@ def generate_unmanned_vehicle_location_data():
|
||||
unmanned_vehicles = []
|
||||
|
||||
# 从现有vehicle_data中筛选无人车(QN开头的车辆)
|
||||
for vehicle in vehicle_data:
|
||||
if vehicle["vehicleNo"].startswith("QN"):
|
||||
location_info = {
|
||||
"transId": str(uuid.uuid4()),
|
||||
"timestamp": int(time.time() * 1000),
|
||||
"vehicleID": vehicle["vehicleNo"],
|
||||
"latitude": vehicle["latitude"],
|
||||
"longitude": vehicle["longitude"],
|
||||
"speed": vehicle["speed"] / 3.6, # 转换为m/s
|
||||
"direction": math.radians(vehicle["direction"]) # 转换为弧度
|
||||
}
|
||||
unmanned_vehicles.append(location_info)
|
||||
for vehicle in unmanned_vehicle_data:
|
||||
location_info = {
|
||||
"transId": str(uuid.uuid4()),
|
||||
"timestamp": int(time.time() * 1000),
|
||||
"vehicleID": vehicle["vehicleNo"],
|
||||
"latitude": vehicle["latitude"],
|
||||
"longitude": vehicle["longitude"],
|
||||
"speed": vehicle["speed"] / 3.6, # 转换为m/s
|
||||
"direction": math.radians(vehicle["direction"]) # 转换为弧度
|
||||
}
|
||||
unmanned_vehicles.append(location_info)
|
||||
|
||||
return unmanned_vehicles
|
||||
|
||||
@ -1081,28 +1097,27 @@ def generate_unmanned_vehicle_state_data(vehicle_id=None, is_single=True):
|
||||
vehicle_states_data.append(state_info)
|
||||
else:
|
||||
# 所有车辆状态查询
|
||||
for vehicle in vehicle_data:
|
||||
if vehicle["vehicleNo"].startswith("QN"):
|
||||
vehicle_state = vehicle_states.get(vehicle["vehicleNo"])
|
||||
state_info = {
|
||||
"transId": str(uuid.uuid4()),
|
||||
"timestamp": int(time.time() * 1000),
|
||||
"vehicleID": vehicle["vehicleNo"],
|
||||
"loginState": True,
|
||||
"faultInfo": [],
|
||||
"activeSafety": not vehicle_state.is_running if vehicle_state else False,
|
||||
"RC": False,
|
||||
"Command": 1 if vehicle_state and vehicle_state.current_command == "ALERT" else 0,
|
||||
"airportInfo": [],
|
||||
"vehicleMode": 2,
|
||||
"gearState": 2,
|
||||
"chassisReady": vehicle_state.is_running if vehicle_state else True,
|
||||
"collisionStatus": False,
|
||||
"clearance": 1 if vehicle_state and vehicle_state.is_running else 0,
|
||||
"turnSignalStstus": 0,
|
||||
"pointCloud": []
|
||||
}
|
||||
vehicle_states_data.append(state_info)
|
||||
for vehicle in unmanned_vehicle_data:
|
||||
vehicle_state = vehicle_states.get(vehicle["vehicleNo"])
|
||||
state_info = {
|
||||
"transId": str(uuid.uuid4()),
|
||||
"timestamp": int(time.time() * 1000),
|
||||
"vehicleID": vehicle["vehicleNo"],
|
||||
"loginState": True,
|
||||
"faultInfo": [],
|
||||
"activeSafety": not vehicle_state.is_running if vehicle_state else False,
|
||||
"RC": False,
|
||||
"Command": 1 if vehicle_state and vehicle_state.current_command == "ALERT" else 0,
|
||||
"airportInfo": [],
|
||||
"vehicleMode": 2,
|
||||
"gearState": 2,
|
||||
"chassisReady": vehicle_state.is_running if vehicle_state else True,
|
||||
"collisionStatus": False,
|
||||
"clearance": 1 if vehicle_state and vehicle_state.is_running else 0,
|
||||
"turnSignalStstus": 0,
|
||||
"pointCloud": []
|
||||
}
|
||||
vehicle_states_data.append(state_info)
|
||||
|
||||
return vehicle_states_data
|
||||
|
||||
@ -1112,25 +1127,19 @@ def get_unmanned_vehicle_location():
|
||||
"""无人车位置上报接口"""
|
||||
if request.method == 'OPTIONS':
|
||||
return '', 204
|
||||
|
||||
try:
|
||||
# 更新车辆位置
|
||||
current_time = time.time()
|
||||
global last_vehicle_update_time
|
||||
elapsed_time = current_time - last_vehicle_update_time
|
||||
|
||||
if elapsed_time >= UPDATE_INTERVAL:
|
||||
for vehicle in vehicle_data:
|
||||
for vehicle in unmanned_vehicle_data:
|
||||
update_vehicle_position(vehicle, UPDATE_INTERVAL)
|
||||
vehicle["time"] = int(current_time * 1000)
|
||||
last_vehicle_update_time = current_time
|
||||
|
||||
# 生成无人车位置数据
|
||||
location_data = generate_unmanned_vehicle_location_data()
|
||||
|
||||
print(f"返回无人车位置数据,数量: {len(location_data)}")
|
||||
return jsonify(location_data)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error in get_unmanned_vehicle_location: {str(e)}")
|
||||
return jsonify([]), 500
|
||||
|
||||
Loading…
Reference in New Issue
Block a user