优化任务接口数据源并修复路径点坐标映射

- 将任务接口数据源从 activeMovingObjectsCache 改为从 HTTP 缓存获取
- 新增路径点 x/y 坐标字段支持,兼容不同数据源格式
- 修复路径点状态字段空指针异常,添加安全处理逻辑
- 优化路径点坐标映射逻辑(x 对应 latitude,y 对应 longitude)
- 添加详细的调试日志输出,便于问题排查
- 更新生产环境配置,设置默认的无人车服务地址
- 新增接口文档3.md,记录相关接口信息
This commit is contained in:
shan 2026-01-23 19:41:35 +08:00
parent dc4934d4d6
commit 85d0472f91
6 changed files with 236 additions and 55 deletions

View File

@ -86,7 +86,7 @@ data:
at-manager-bsm: /ws/at_manager_bsm
at-manager-path: /ws/at_manager_path
http:
base-url: ${VEHICLE_MANAGER_HTTP_BASE_URL:}
base-url: ${VEHICLE_MANAGER_HTTP_BASE_URL:http://localhost:8020}
status: /api/vehicle_manager/v1/vehicles/{vehicleId}/status
vehicle-details: /api/vehicle_details
poll-interval-ms: 1000

View File

@ -116,6 +116,7 @@ public class UnmannedVehicle extends MovingObject {
@lombok.Builder
@lombok.NoArgsConstructor
@lombok.AllArgsConstructor
@com.fasterxml.jackson.annotation.JsonIgnoreProperties(ignoreUnknown = true)
public static class WaypointInfo {
private String waypointId;
private Double latitude;

View File

@ -51,67 +51,113 @@ public class VehicleTaskController {
private String status;
}
@GetMapping
@GetMapping
public AjaxResult list(@RequestParam(required = false, defaultValue = "1") int pageNum,
@RequestParam(required = false, defaultValue = "10") int pageSize,
@RequestParam(required = false) String status) {
Map<String, MovingObject> activeMovingObjectsCache = dataCollectorService.getActiveMovingObjectsCache();
// HTTP 缓存获取车辆状态数据
Map<String, DataCollectorService.UniversalVehicleStatusCacheEntry> httpCache = dataCollectorService.getUniversalStatusCache();
List<VehicleTaskDTO> allTasks = new ArrayList<>();
for (MovingObject movingObject : activeMovingObjectsCache.values()) {
if (!(movingObject instanceof UnmannedVehicle vehicle)) {
System.out.println("[DEBUG] ========== 任务接口调试信息 ==========");
System.out.println("[DEBUG] httpCache size: " + httpCache.size());
System.out.println("[DEBUG] httpCache keys: " + httpCache.keySet());
for (Map.Entry<String, DataCollectorService.UniversalVehicleStatusCacheEntry> entry : httpCache.entrySet()) {
String cacheKey = entry.getKey();
DataCollectorService.UniversalVehicleStatusCacheEntry cacheEntry = entry.getValue();
System.out.println("[DEBUG] Processing cacheKey: " + cacheKey);
if (cacheEntry.getStatusData() == null) {
System.out.println("[DEBUG] - statusData is null, skipping");
continue;
}
String vehicleId = vehicle.getObjectId();
String missionId = vehicle.getMissionId();
// cacheKey 中提取 vehicleId格式universal_status_vehicleId
String vehicleId = cacheKey.replace("universal_status_", "");
System.out.println("[DEBUG] - vehicleId: " + vehicleId);
com.qaup.collision.datacollector.model.dto.UniversalVehicleStatusDTO statusData = cacheEntry.getStatusData();
com.qaup.collision.datacollector.model.dto.MissionContextDTO missionContext = statusData.getMissionContext();
System.out.println("[DEBUG] - missionContext: " + (missionContext != null ? missionContext.getClass().getSimpleName() : "null"));
if (missionContext != null) {
System.out.println("[DEBUG] - currentMission: " + (missionContext.getCurrentMission() != null ? missionContext.getCurrentMission().getClass().getSimpleName() : "null"));
}
if (missionContext == null || missionContext.getCurrentMission() == null) {
System.out.println("[DEBUG] - Skipping (no missionContext)");
continue;
}
com.qaup.collision.datacollector.model.dto.MissionContextDTO.CurrentMissionDTO currentMission = missionContext.getCurrentMission();
String missionId = currentMission.getMissionId();
System.out.println("[DEBUG] - missionId: " + missionId);
// 如果没有任务ID跳过
if (missionId == null || missionId.isBlank()) {
System.out.println("[DEBUG] - Skipping (no missionId)");
continue;
}
String missionStatus = vehicle.getMissionStatus() != null ? vehicle.getMissionStatus().name() : "UNKNOWN";
// 获取车辆类型VehicleInfoDTO 只有 vehicleId没有 vehicleType使用默认值
String vehicleType = "UNMANNED_VEHICLE";
// 获取任务状态 operationalStatus 获取 operationalMode
String missionStatus = "UNKNOWN";
if (statusData.getOperationalStatus() != null) {
missionStatus = statusData.getOperationalStatus().getOperationalMode();
}
// 状态筛选
if (status != null && !status.isEmpty() && !status.equalsIgnoreCase(missionStatus)) {
System.out.println("[DEBUG] - Skipping (status filter: " + status + " != " + missionStatus + ")");
continue;
}
// 获取车辆类型
String vehicleType = vehicle.getObjectType().name();
System.out.println("[DEBUG] - Adding task: " + vehicleId + ", MissionId: " + missionId);
// 获取路径点
List<WaypointDTO> waypoints = new ArrayList<>();
if (vehicle.getWaypoints() != null) {
for (UnmannedVehicle.WaypointInfo wp : vehicle.getWaypoints()) {
System.out.println("[DEBUG] - missionContext.getWaypoints(): " + missionContext.getWaypoints());
if (missionContext.getWaypoints() != null) {
System.out.println("[DEBUG] - waypoints size: " + missionContext.getWaypoints().size());
for (com.qaup.collision.datacollector.model.dto.MissionContextDTO.WaypointDTO wp : missionContext.getWaypoints()) {
System.out.println("[DEBUG] - waypoint: " + wp.getWaypointId() + ", lat: " + wp.getLatitude() + ", lon: " + wp.getLongitude() + ", status: " + wp.getStatus());
WaypointDTO waypoint = WaypointDTO.builder()
.waypointId(wp.getWaypointId())
.latitude(wp.getLatitude())
.longitude(wp.getLongitude())
.status(wp.getStatus().name())
.status(wp.getStatus())
.build();
waypoints.add(waypoint);
}
} else {
System.out.println("[DEBUG] - waypoints is null");
}
System.out.println("[DEBUG] - final waypoints size: " + waypoints.size());
VehicleTaskDTO task = VehicleTaskDTO.builder()
.vehicleId(vehicleId)
.vehicleType(vehicleType)
.missionId(missionId)
.missionType(vehicle.getMissionType())
.missionType(currentMission.getMissionType())
.missionStatus(missionStatus)
.missionStartTime(vehicle.getMissionStartTime())
.estimatedEndTime(vehicle.getEstimatedEndTime())
.progress(vehicle.getProgress())
.totalMileage(vehicle.getTotalMileage())
.missionStartTime(currentMission.getStartTime())
.estimatedEndTime(currentMission.getEstimatedEndTime())
.progress(currentMission.getProgress())
.totalMileage(currentMission.getTotalMileage())
.waypoints(waypoints)
.lastSeenAt(null) // 暂时设为null后续可以从缓存获取
.lastSeenAt(cacheEntry.getTimestamp())
.build();
allTasks.add(task);
}
System.out.println("[DEBUG] Total tasks found: " + allTasks.size());
System.out.println("[DEBUG] =========================================");
// 按任务开始时间倒序排序
allTasks.sort((a, b) -> {
if (a.getMissionStartTime() == null && b.getMissionStartTime() == null) return 0;
@ -136,29 +182,41 @@ public class VehicleTaskController {
@GetMapping("/{vehicleId}")
public AjaxResult get(@PathVariable String vehicleId) {
Map<String, MovingObject> activeMovingObjectsCache = dataCollectorService.getActiveMovingObjectsCache();
MovingObject movingObject = activeMovingObjectsCache.get(vehicleId);
// HTTP 缓存获取车辆状态数据使用正确的 cacheKey 格式
String cacheKey = "universal_status_" + vehicleId;
DataCollectorService.UniversalVehicleStatusCacheEntry entry = dataCollectorService.getUniversalStatusCache().get(cacheKey);
if (!(movingObject instanceof UnmannedVehicle vehicle)) {
return AjaxResult.error(HttpStatus.NOT_FOUND, "车辆不存在或无任务").put("timestamp", System.currentTimeMillis());
if (entry == null || entry.getStatusData() == null) {
return AjaxResult.error(HttpStatus.NOT_FOUND, "车辆不存在或无数据").put("timestamp", System.currentTimeMillis());
}
String missionId = vehicle.getMissionId();
com.qaup.collision.datacollector.model.dto.UniversalVehicleStatusDTO statusData = entry.getStatusData();
com.qaup.collision.datacollector.model.dto.MissionContextDTO missionContext = statusData.getMissionContext();
if (missionContext == null || missionContext.getCurrentMission() == null) {
return AjaxResult.error(HttpStatus.NOT_FOUND, "车辆无任务").put("timestamp", System.currentTimeMillis());
}
com.qaup.collision.datacollector.model.dto.MissionContextDTO.CurrentMissionDTO currentMission = missionContext.getCurrentMission();
String missionId = currentMission.getMissionId();
if (missionId == null || missionId.isBlank()) {
return AjaxResult.error(HttpStatus.NOT_FOUND, "车辆无任务").put("timestamp", System.currentTimeMillis());
}
String vehicleType = vehicle.getObjectType().name();
String vehicleType = "UNMANNED_VEHICLE";
String missionStatus = statusData.getOperationalStatus() != null
? statusData.getOperationalStatus().getOperationalMode()
: "UNKNOWN";
List<WaypointDTO> waypoints = new ArrayList<>();
if (vehicle.getWaypoints() != null) {
for (UnmannedVehicle.WaypointInfo wp : vehicle.getWaypoints()) {
if (missionContext.getWaypoints() != null) {
for (com.qaup.collision.datacollector.model.dto.MissionContextDTO.WaypointDTO wp : missionContext.getWaypoints()) {
WaypointDTO waypoint = WaypointDTO.builder()
.waypointId(wp.getWaypointId())
.latitude(wp.getLatitude())
.longitude(wp.getLongitude())
.status(wp.getStatus().name())
.status(wp.getStatus())
.build();
waypoints.add(waypoint);
}
@ -168,14 +226,14 @@ public class VehicleTaskController {
.vehicleId(vehicleId)
.vehicleType(vehicleType)
.missionId(missionId)
.missionType(vehicle.getMissionType())
.missionStatus(vehicle.getMissionStatus() != null ? vehicle.getMissionStatus().name() : "UNKNOWN")
.missionStartTime(vehicle.getMissionStartTime())
.estimatedEndTime(vehicle.getEstimatedEndTime())
.progress(vehicle.getProgress())
.totalMileage(vehicle.getTotalMileage())
.missionType(currentMission.getMissionType())
.missionStatus(missionStatus)
.missionStartTime(currentMission.getStartTime())
.estimatedEndTime(currentMission.getEstimatedEndTime())
.progress(currentMission.getProgress())
.totalMileage(currentMission.getTotalMileage())
.waypoints(waypoints)
.lastSeenAt(null) // 暂时设为null
.lastSeenAt(entry.getTimestamp())
.build();
return AjaxResult.success(task).put("timestamp", System.currentTimeMillis());

View File

@ -76,6 +76,7 @@ public class MissionContextDTO {
@NoArgsConstructor
@AllArgsConstructor
@Builder
@com.fasterxml.jackson.annotation.JsonIgnoreProperties(ignoreUnknown = true)
public static class WaypointDTO {
/**
* 路径点ID
@ -96,5 +97,31 @@ public class MissionContextDTO {
* 状态 (PENDING, COMPLETED, SKIPPED)
*/
private String status;
/**
* X坐标 (经度)
*/
@com.fasterxml.jackson.annotation.JsonProperty("x")
private Double x;
/**
* Y坐标 (纬度)
*/
@com.fasterxml.jackson.annotation.JsonProperty("y")
private Double y;
/**
* 获取纬度如果为null则返回x值
*/
public Double getLatitude() {
return latitude != null ? latitude : x;
}
/**
* 获取经度如果为null则返回y值
*/
public Double getLongitude() {
return longitude != null ? longitude : y;
}
}
}

View File

@ -632,12 +632,29 @@ public class DataCollectorService {
// 提取路径点信息
if (missionContext.getWaypoints() != null) {
java.util.List<UnmannedVehicle.WaypointInfo> waypoints = missionContext.getWaypoints().stream()
.map(wp -> UnmannedVehicle.WaypointInfo.builder()
.waypointId(wp.getWaypointId())
.latitude(wp.getLatitude())
.longitude(wp.getLongitude())
.status(UnmannedVehicle.WaypointStatus.valueOf(wp.getStatus()))
.build())
.map(wp -> {
// 安全处理状态字段避免 null 导致 NPE
UnmannedVehicle.WaypointStatus status = UnmannedVehicle.WaypointStatus.PENDING;
if (wp.getStatus() != null && !wp.getStatus().isEmpty()) {
try {
status = UnmannedVehicle.WaypointStatus.valueOf(wp.getStatus());
} catch (IllegalArgumentException e) {
// 如果状态值无效使用默认值
status = UnmannedVehicle.WaypointStatus.PENDING;
}
}
// x 对应 latitude纬度y 对应 longitude经度
Double lat = wp.getX() != null ? wp.getX() : wp.getLatitude();
Double lon = wp.getY() != null ? wp.getY() : wp.getLongitude();
return UnmannedVehicle.WaypointInfo.builder()
.waypointId(wp.getWaypointId())
.latitude(lat)
.longitude(lon)
.status(status)
.build();
})
.collect(java.util.stream.Collectors.toList());
vehicleBuilder.waypoints(waypoints);
}
@ -798,21 +815,47 @@ public class DataCollectorService {
// 如果存在missionContext数据更新到activeMovingObjectsCache中的无人车对象
if (statusData.getMissionContext() != null) {
log.info("========== 开始更新任务上下文 ==========");
log.info("vehicleId: {}", vehicleId);
log.info("missionContext: {}", statusData.getMissionContext());
log.info("waypoints: {}", statusData.getMissionContext().getWaypoints());
updateUnmannedVehicleMissionContext(vehicleId, statusData.getMissionContext());
log.info("========== 任务上下文更新完成 ==========");
}
log.debug("缓存通用车辆状态数据: vehicleId={}, cacheKey={}, 包含任务上下文: {}",
vehicleId, cacheKey, statusData.getMissionContext() != null);
}
/**
* 更新无人车的任务上下文信息
*/
private void updateUnmannedVehicleMissionContext(String vehicleId, MissionContextDTO missionContext) {
MovingObject existingObject = activeMovingObjectsCache.get(vehicleId);
/**
if (existingObject instanceof UnmannedVehicle) {
UnmannedVehicle unmannedVehicle = (UnmannedVehicle) existingObject;
* 更新无人车的任务上下文信息
*/
private void updateUnmannedVehicleMissionContext(String vehicleId, MissionContextDTO missionContext) {
log.info("========== updateUnmannedVehicleMissionContext 开始 ==========");
log.info("vehicleId: {}", vehicleId);
log.info("activeMovingObjectsCache 是否包含 vehicleId: {}", activeMovingObjectsCache.containsKey(vehicleId));
MovingObject existingObject = activeMovingObjectsCache.get(vehicleId);
log.info("existingObject: {}", existingObject);
log.info("existingObject 类型: {}", existingObject != null ? existingObject.getClass().getName() : "null");
if (existingObject instanceof UnmannedVehicle) {
log.info("找到 UnmannedVehicle 对象,开始更新任务上下文");
UnmannedVehicle unmannedVehicle = (UnmannedVehicle) existingObject;
// 更新任务上下文信息
if (missionContext.getCurrentMission() != null) {
@ -827,15 +870,38 @@ public class DataCollectorService {
// 更新路径点信息
if (missionContext.getWaypoints() != null) {
log.info("更新路径点信息: vehicleId={}, waypoints数量={}", vehicleId, missionContext.getWaypoints().size());
java.util.List<UnmannedVehicle.WaypointInfo> waypoints = missionContext.getWaypoints().stream()
.map(wp -> UnmannedVehicle.WaypointInfo.builder()
.waypointId(wp.getWaypointId())
.latitude(wp.getLatitude())
.longitude(wp.getLongitude())
.status(UnmannedVehicle.WaypointStatus.valueOf(wp.getStatus()))
.build())
.map(wp -> {
// 安全处理状态字段避免 null 导致 NPE
UnmannedVehicle.WaypointStatus status = UnmannedVehicle.WaypointStatus.PENDING;
if (wp.getStatus() != null && !wp.getStatus().isEmpty()) {
try {
status = UnmannedVehicle.WaypointStatus.valueOf(wp.getStatus());
} catch (IllegalArgumentException e) {
// 如果状态值无效使用默认值
status = UnmannedVehicle.WaypointStatus.PENDING;
}
}
// x 对应 latitude纬度y 对应 longitude经度
Double lat = wp.getX() != null ? wp.getX() : wp.getLatitude();
Double lon = wp.getY() != null ? wp.getY() : wp.getLongitude();
log.info(" 路径点: waypointId={}, x={}, y={}, latitude={}, longitude={}, status={}",
wp.getWaypointId(), wp.getX(), wp.getY(), lat, lon, wp.getStatus());
return UnmannedVehicle.WaypointInfo.builder()
.waypointId(wp.getWaypointId())
.latitude(lat)
.longitude(lon)
.status(status)
.build();
})
.collect(java.util.stream.Collectors.toList());
unmannedVehicle.setWaypoints(waypoints);
log.info("路径点更新完成: vehicleId={}, 设置的路径点数量={}", vehicleId, waypoints.size());
} else {
log.debug("路径点为空: vehicleId={}", vehicleId);
}
// 更新缓存
@ -849,6 +915,7 @@ public class DataCollectorService {
} else {
log.debug("缓存中未找到无人车对象或类型不匹配,跳过任务上下文更新: vehicleId={}", vehicleId);
}
log.info("========== updateUnmannedVehicleMissionContext 完成 ==========");
}
// 用于存储通用车辆状态数据的缓存

28
接口文档3.md Normal file
View File

@ -0,0 +1,28 @@
车辆管理系统接口文档
1. 查询车辆列表
接口描述
该接口用于查询项目车辆列表数据。
接口详情
协议名称: HTTP
接口地址: /api/vehicle_details
请求方法: GET
请求参数: 无
参数名称,参数类型,描述
code,int,状态码
msg,str,任务信息
data,str,车辆列表
timestamp,float,时间戳
{
"code": 200,
"message": "success",
"data": [
"AET02",
"AET01"
],
"timestamp": 0
}