From 85d0472f91c1ccbb9df66b8da165d537ddd706ca Mon Sep 17 00:00:00 2001 From: shan <1653261938@qq.com> Date: Fri, 23 Jan 2026 19:41:35 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=BB=E5=8A=A1=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E6=95=B0=E6=8D=AE=E6=BA=90=E5=B9=B6=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E7=82=B9=E5=9D=90=E6=A0=87=E6=98=A0=E5=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将任务接口数据源从 activeMovingObjectsCache 改为从 HTTP 缓存获取 - 新增路径点 x/y 坐标字段支持,兼容不同数据源格式 - 修复路径点状态字段空指针异常,添加安全处理逻辑 - 优化路径点坐标映射逻辑(x 对应 latitude,y 对应 longitude) - 添加详细的调试日志输出,便于问题排查 - 更新生产环境配置,设置默认的无人车服务地址 - 新增接口文档3.md,记录相关接口信息 --- .../src/main/resources/application-prod.yml | 2 +- .../common/model/UnmannedVehicle.java | 1 + .../controller/VehicleTaskController.java | 128 +++++++++++++----- .../model/dto/MissionContextDTO.java | 27 ++++ .../service/DataCollectorService.java | 105 +++++++++++--- 接口文档3.md | 28 ++++ 6 files changed, 236 insertions(+), 55 deletions(-) create mode 100644 接口文档3.md diff --git a/qaup-admin/src/main/resources/application-prod.yml b/qaup-admin/src/main/resources/application-prod.yml index f4e6802..75bb76d 100644 --- a/qaup-admin/src/main/resources/application-prod.yml +++ b/qaup-admin/src/main/resources/application-prod.yml @@ -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 diff --git a/qaup-collision/src/main/java/com/qaup/collision/common/model/UnmannedVehicle.java b/qaup-collision/src/main/java/com/qaup/collision/common/model/UnmannedVehicle.java index d74ea7e..ab8a231 100644 --- a/qaup-collision/src/main/java/com/qaup/collision/common/model/UnmannedVehicle.java +++ b/qaup-collision/src/main/java/com/qaup/collision/common/model/UnmannedVehicle.java @@ -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; diff --git a/qaup-collision/src/main/java/com/qaup/collision/controller/VehicleTaskController.java b/qaup-collision/src/main/java/com/qaup/collision/controller/VehicleTaskController.java index 6b3dd2c..2772e51 100644 --- a/qaup-collision/src/main/java/com/qaup/collision/controller/VehicleTaskController.java +++ b/qaup-collision/src/main/java/com/qaup/collision/controller/VehicleTaskController.java @@ -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 activeMovingObjectsCache = dataCollectorService.getActiveMovingObjectsCache(); + // 从 HTTP 缓存获取车辆状态数据 + Map httpCache = dataCollectorService.getUniversalStatusCache(); List 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 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 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 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 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()); diff --git a/qaup-collision/src/main/java/com/qaup/collision/datacollector/model/dto/MissionContextDTO.java b/qaup-collision/src/main/java/com/qaup/collision/datacollector/model/dto/MissionContextDTO.java index 3b75b33..5ae8e12 100644 --- a/qaup-collision/src/main/java/com/qaup/collision/datacollector/model/dto/MissionContextDTO.java +++ b/qaup-collision/src/main/java/com/qaup/collision/datacollector/model/dto/MissionContextDTO.java @@ -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; + } } } \ No newline at end of file diff --git a/qaup-collision/src/main/java/com/qaup/collision/datacollector/service/DataCollectorService.java b/qaup-collision/src/main/java/com/qaup/collision/datacollector/service/DataCollectorService.java index 3235621..46c5e61 100644 --- a/qaup-collision/src/main/java/com/qaup/collision/datacollector/service/DataCollectorService.java +++ b/qaup-collision/src/main/java/com/qaup/collision/datacollector/service/DataCollectorService.java @@ -632,12 +632,29 @@ public class DataCollectorService { // 提取路径点信息 if (missionContext.getWaypoints() != null) { java.util.List 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 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 完成 =========="); } // 用于存储通用车辆状态数据的缓存 diff --git a/接口文档3.md b/接口文档3.md new file mode 100644 index 0000000..b2cf6f8 --- /dev/null +++ b/接口文档3.md @@ -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 +} \ No newline at end of file