From dc4934d4d6abcf80139634e25efb4ad0bb268c15 Mon Sep 17 00:00:00 2001 From: sladro Date: Fri, 23 Jan 2026 11:08:18 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8E=BB=E6=8E=89=E6=97=A0=E4=BA=BA=E8=BD=A6ws?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E6=96=B9=E5=BC=8F=EF=BC=8C=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?http=E8=8E=B7=E5=8F=96=E6=97=A0=E4=BA=BA=E8=BD=A6=E5=88=97?= =?UTF-8?q?=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/application-dev.yml | 4 + .../src/main/resources/application-prod.yml | 4 + qaup-admin/src/main/resources/application.yml | 4 + .../config/VehicleManagerProperties.java | 23 +- .../datacollector/dao/DataCollectorDao.java | 47 +++ .../service/DataCollectorService.java | 112 +++++-- .../service/VehicleManagerQueryService.java | 290 ++++++------------ .../handler/CollisionWebSocketHandler.java | 57 +--- 8 files changed, 252 insertions(+), 289 deletions(-) diff --git a/qaup-admin/src/main/resources/application-dev.yml b/qaup-admin/src/main/resources/application-dev.yml index f895034..2139106 100644 --- a/qaup-admin/src/main/resources/application-dev.yml +++ b/qaup-admin/src/main/resources/application-dev.yml @@ -117,6 +117,7 @@ data: host: localhost port: 8020 reconnect-delay-millis: 3000 + ws-enabled: false ws: at-manager: /ws/at_manager at-manager-bsm: /ws/at_manager_bsm @@ -124,4 +125,7 @@ data: http: base-url: http://localhost:8020 status: /api/vehicle_manager/v1/vehicles/{vehicleId}/status + vehicle-details: /api/vehicle_details + poll-interval-ms: 1000 + vehicle-details-refresh-ms: 60000 diff --git a/qaup-admin/src/main/resources/application-prod.yml b/qaup-admin/src/main/resources/application-prod.yml index 109acb3..f4e6802 100644 --- a/qaup-admin/src/main/resources/application-prod.yml +++ b/qaup-admin/src/main/resources/application-prod.yml @@ -80,6 +80,7 @@ data: host: ${VEHICLE_MANAGER_HOST:localhost} port: ${VEHICLE_MANAGER_PORT:8020} reconnect-delay-millis: ${VEHICLE_MANAGER_RECONNECT_DELAY:3000} + ws-enabled: false ws: at-manager: /ws/at_manager at-manager-bsm: /ws/at_manager_bsm @@ -87,6 +88,9 @@ data: http: base-url: ${VEHICLE_MANAGER_HTTP_BASE_URL:} status: /api/vehicle_manager/v1/vehicles/{vehicleId}/status + vehicle-details: /api/vehicle_details + poll-interval-ms: 1000 + vehicle-details-refresh-ms: 60000 # ADXP 适配器服务配置(生产环境) adxp-adapter: diff --git a/qaup-admin/src/main/resources/application.yml b/qaup-admin/src/main/resources/application.yml index f349577..38da7b7 100644 --- a/qaup-admin/src/main/resources/application.yml +++ b/qaup-admin/src/main/resources/application.yml @@ -184,6 +184,7 @@ data: host: ${VEHICLE_MANAGER_HOST:} port: ${VEHICLE_MANAGER_PORT:0} reconnect-delay-millis: 3000 + ws-enabled: false ws: at-manager: /ws/at_manager at-manager-bsm: /ws/at_manager_bsm @@ -191,6 +192,9 @@ data: http: base-url: ${VEHICLE_MANAGER_HTTP_BASE_URL:} status: /api/vehicle_manager/v1/vehicles/{vehicleId}/status + vehicle-details: /api/vehicle_details + poll-interval-ms: 1000 + vehicle-details-refresh-ms: 60000 # 无人车数据持久化配置 unmanned-vehicle: diff --git a/qaup-collision/src/main/java/com/qaup/collision/datacollector/config/VehicleManagerProperties.java b/qaup-collision/src/main/java/com/qaup/collision/datacollector/config/VehicleManagerProperties.java index 6c1ae3b..e53db0a 100644 --- a/qaup-collision/src/main/java/com/qaup/collision/datacollector/config/VehicleManagerProperties.java +++ b/qaup-collision/src/main/java/com/qaup/collision/datacollector/config/VehicleManagerProperties.java @@ -13,11 +13,17 @@ public class VehicleManagerProperties { private Integer port; private long reconnectDelayMillis = 3000L; + /** + * 是否启用上游 VehicleManager WebSocket(接口文档1) + */ + private boolean wsEnabled = false; + private Ws ws = new Ws(); private Http http = new Http(); public boolean isWsConfigurationReady() { - return host != null && port != null && port > 0 + return wsEnabled + && host != null && port != null && port > 0 && ws != null && ws.getAtManager() != null && ws.getAtManagerBsm() != null @@ -43,5 +49,20 @@ public class VehicleManagerProperties { public static class Http { private String baseUrl; private String status; + + /** + * 无人车列表接口(GET /api/vehicle_details) + */ + private String vehicleDetails; + + /** + * 轮询无人车状态间隔(ms),用于HTTP拉取位置(接口文档2) + */ + private long pollIntervalMs = 1000L; + + /** + * 刷新无人车列表间隔(ms) + */ + private long vehicleDetailsRefreshMs = 60000L; } } diff --git a/qaup-collision/src/main/java/com/qaup/collision/datacollector/dao/DataCollectorDao.java b/qaup-collision/src/main/java/com/qaup/collision/datacollector/dao/DataCollectorDao.java index 6d9cb68..1ad2d40 100644 --- a/qaup-collision/src/main/java/com/qaup/collision/datacollector/dao/DataCollectorDao.java +++ b/qaup-collision/src/main/java/com/qaup/collision/datacollector/dao/DataCollectorDao.java @@ -59,6 +59,9 @@ public class DataCollectorDao { @Value("${data.collector.vehicle-manager.http.status:}") private String vehicleManagerStatusEndpoint; + @Value("${data.collector.vehicle-manager.http.vehicle-details:}") + private String vehicleManagerVehicleDetailsEndpoint; + private final RestTemplate restTemplate; private final AuthService authService; private final GeometryFactory geometryFactory; @@ -587,4 +590,48 @@ public class DataCollectorDao { return null; } + + /** + * 获取无人车列表(上游接口:GET /api/vehicle_details) + */ + public List getVehicleManagerVehicleDetails() { + if (vehicleManagerBaseUrl == null || vehicleManagerBaseUrl.isBlank() + || vehicleManagerVehicleDetailsEndpoint == null || vehicleManagerVehicleDetailsEndpoint.isBlank()) { + log.warn("车辆管理 vehicle_details HTTP 配置未设置,无法获取无人车列表"); + return Collections.emptyList(); + } + + String url = UriComponentsBuilder.fromUriString(vehicleManagerBaseUrl) + .path(vehicleManagerVehicleDetailsEndpoint) + .toUriString(); + + try { + HttpHeaders headers = new HttpHeaders(); + headers.setAccept(List.of(MediaType.APPLICATION_JSON)); + HttpEntity entity = new HttpEntity<>(headers); + + ResponseEntity>> response = + restTemplate.exchange( + url, + HttpMethod.GET, + entity, + new ParameterizedTypeReference>>() {} + ); + + if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) { + com.qaup.collision.datacollector.model.dto.UniversalApiResponse> apiResponse = response.getBody(); + if (apiResponse.getCode() != null && apiResponse.getCode() == 200 && apiResponse.getData() != null) { + return apiResponse.getData(); + } + log.warn("车辆管理 vehicle_details 返回异常: code={}, message={}", apiResponse.getCode(), apiResponse.getMessage()); + return Collections.emptyList(); + } + + log.warn("车辆管理 vehicle_details 请求失败: status={}", response.getStatusCode()); + return Collections.emptyList(); + } catch (Exception e) { + log.error("调用车辆管理 vehicle_details HTTP 接口失败", e); + return Collections.emptyList(); + } + } } 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 70f3419..3235621 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 @@ -29,6 +29,7 @@ import java.time.LocalDateTime; import java.time.ZoneId; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.locationtech.jts.geom.GeometryFactory; @@ -65,6 +66,9 @@ public class DataCollectorService { @Value("${data.collector.detection.interval:1000}") private long detectionInterval; + @Value("${data.collector.vehicle-manager.http.polling-enabled:true}") + private boolean vehicleManagerHttpPollingEnabled; + @Value("${data.collector.route.periodic-collection-enabled:false}") private boolean periodicRouteCollectionEnabled; @@ -96,6 +100,9 @@ public class DataCollectorService { @Autowired(required = false) private com.qaup.collision.datacollector.websocket.VehicleManagerWebSocketClient vehicleManagerWebSocketClient; + @Autowired + private com.qaup.collision.datacollector.config.VehicleManagerProperties vehicleManagerProperties; + @Autowired private VehicleManagerCacheService vehicleManagerCacheService; @@ -108,6 +115,20 @@ public class DataCollectorService { // 用于缓存所有活跃的MovingObject的最新状态 private final Map activeMovingObjectsCache = new ConcurrentHashMap<>(); + // 无人车ID列表(来自上游 HTTP:GET /api/vehicle_details) + private final Set unmannedVehicleIds = ConcurrentHashMap.newKeySet(); + + // 无人车最新HTTP状态缓存(用于前端HTTP接口/统计展示),key=vehicleId + private final Map unmannedVehicleHttpStatusCache = new ConcurrentHashMap<>(); + + public Set getUnmannedVehicleIdsSnapshot() { + return Set.copyOf(unmannedVehicleIds); + } + + public UniversalVehicleStatusCacheEntry getUnmannedVehicleHttpStatus(String vehicleId) { + return unmannedVehicleHttpStatusCache.get(vehicleId); + } + /** * 获取活跃的移动对象缓存(供外部访问) */ @@ -153,6 +174,10 @@ public class DataCollectorService { } private void initVehicleManagerWebSocketClient() { + if (vehicleManagerProperties == null || !vehicleManagerProperties.isWsEnabled()) { + log.info("车辆管理 WebSocket 已禁用(仅使用HTTP拉取无人车数据)"); + return; + } if (vehicleManagerWebSocketClient != null) { try { vehicleManagerWebSocketClient.addMessageListener(this::handleVehicleManagerMessage); @@ -165,6 +190,31 @@ public class DataCollectorService { log.info("车辆管理 WebSocket 客户端未配置"); } } + + /** + * 每分钟刷新无人车列表(上游:GET /api/vehicle_details) + */ + @Scheduled(fixedRateString = "${data.collector.vehicle-manager.http.vehicle-details-refresh-ms:60000}") + public void refreshUnmannedVehicleList() { + if (collectorDisabled) { + return; + } + + List ids = dataCollectorDao.getVehicleManagerVehicleDetails(); + if (ids == null || ids.isEmpty()) { + log.debug("无人车列表刷新为空,保留当前列表,当前数量: {}", unmannedVehicleIds.size()); + return; + } + + unmannedVehicleIds.clear(); + unmannedVehicleHttpStatusCache.keySet().retainAll(ids); + for (String id : ids) { + if (id != null && !id.isBlank()) { + unmannedVehicleIds.add(id.trim()); + } + } + log.info("无人车列表已刷新,数量: {}", unmannedVehicleIds.size()); + } /** * 处理通过WebSocket接收到的航班通知 @@ -501,7 +551,7 @@ public class DataCollectorService { * - 包含missionContext任务信息,支持里程和路径点数据 * - 数据仅用于实时处理,不存储到数据库 */ - @Scheduled(fixedRateString = "${data.collector.interval}") + @Scheduled(fixedRateString = "${data.collector.vehicle-manager.http.poll-interval-ms:1000}") @Async // 异步执行 public void collectUnmannedVehicleData() { if (collectorDisabled) { @@ -509,22 +559,29 @@ public class DataCollectorService { } try { - // 获取当前活跃的无人车列表(从之前的缓存或配置中获取) - java.util.Set knownVehicleIds = getKnownUnmannedVehicleIds(); + if (unmannedVehicleIds.isEmpty()) { + // 启动时可能尚未刷新到列表,这里做一次快速拉取作为兜底 + List ids = dataCollectorDao.getVehicleManagerVehicleDetails(); + if (ids != null && !ids.isEmpty()) { + unmannedVehicleIds.clear(); + ids.stream().filter(java.util.Objects::nonNull).map(String::trim).filter(s -> !s.isEmpty()) + .forEach(unmannedVehicleIds::add); + } + } - if (knownVehicleIds.isEmpty()) { - log.debug("当前没有已知的无人车ID,跳过数据采集"); + if (unmannedVehicleIds.isEmpty()) { + log.debug("当前无人车列表为空,跳过数据采集"); return; } - log.info("开始采集 {} 辆无人车的完整状态数据", knownVehicleIds.size()); + log.debug("开始采集 {} 辆无人车的状态数据(HTTP轮询)", unmannedVehicleIds.size()); int successCount = 0; - for (String vehicleId : knownVehicleIds) { + for (String vehicleId : unmannedVehicleIds) { try { - // 调用通用状态API获取完整数据 + // 只使用接口文档2(车辆管理HTTP状态接口)获取状态数据 com.qaup.collision.datacollector.model.dto.UniversalVehicleStatusDTO statusData = - resolveVehicleStatus(vehicleId); + dataCollectorDao.getVehicleManagerStatus(vehicleId); if (statusData == null || statusData.getMotionStatus() == null) { log.debug("无人车 {} 未返回有效状态数据,跳过处理", vehicleId); @@ -608,6 +665,17 @@ public class DataCollectorService { // 将最新数据更新到缓存 activeMovingObjectsCache.put(enhancedUnmannedVehicle.getObjectId(), enhancedUnmannedVehicle); + + // 更新无人车HTTP状态缓存(供HTTP接口查询) + unmannedVehicleHttpStatusCache.put(vehicleId, UniversalVehicleStatusCacheEntry.builder() + .vehicleId(vehicleId) + .statusData(statusData) + .timestamp(System.currentTimeMillis()) + .dataSource("VEHICLE_MANAGER_HTTP") + .build()); + + // 缓存通用状态数据,供后续DataProcessingService推送 vehicle_status_update(如前端需要) + cacheUniversalVehicleStatus(vehicleId, statusData); successCount++; log.debug("处理无人车完整状态数据并更新缓存: (车辆ID: {}, 位置: {}, {}, 任务ID: {}, 里程: {}米, 电量: {}%)", @@ -623,7 +691,7 @@ public class DataCollectorService { } } - log.info("无人车完整状态数据采集完成,成功处理: {}/{}", successCount, knownVehicleIds.size()); + log.debug("无人车状态数据采集完成,成功处理: {}/{}", successCount, unmannedVehicleIds.size()); } catch (Exception e) { log.error("采集无人车数据异常", e); @@ -635,24 +703,7 @@ public class DataCollectorService { * 可以从配置、缓存或其他数据源获取 */ private java.util.Set getKnownUnmannedVehicleIds() { - java.util.Set vehicleIds = new java.util.HashSet<>(); - - // 从当前缓存中获取已存在的无人车ID - activeMovingObjectsCache.values().stream() - .filter(obj -> obj.getObjectType() == MovingObjectType.UNMANNED_VEHICLE) - .forEach(obj -> vehicleIds.add(obj.getObjectId())); - - // 如果缓存为空,使用默认的无人车ID(与mock服务对应) - if (vehicleIds.isEmpty()) { - vehicleIds.add("鲁B567"); - vehicleIds.add("鲁B579"); - } - - if (vehicleManagerCacheService != null) { - vehicleIds.addAll(vehicleManagerCacheService.getKnownVehicleIds()); - } - - return vehicleIds; + return java.util.Set.copyOf(unmannedVehicleIds); } /** @@ -668,6 +719,11 @@ public class DataCollectorService { @Scheduled(fixedRateString = "${data.collector.interval}") @Async // 异步执行 public void collectUniversalVehicleStatus() { + // 无人车状态已在 collectUnmannedVehicleData() 中通过 HTTP 拉取并缓存,无需重复采集 + if (vehicleManagerHttpPollingEnabled) { + return; + } + if (collectorDisabled) { return; } diff --git a/qaup-collision/src/main/java/com/qaup/collision/datacollector/service/VehicleManagerQueryService.java b/qaup-collision/src/main/java/com/qaup/collision/datacollector/service/VehicleManagerQueryService.java index 5031f69..7a4cae1 100644 --- a/qaup-collision/src/main/java/com/qaup/collision/datacollector/service/VehicleManagerQueryService.java +++ b/qaup-collision/src/main/java/com/qaup/collision/datacollector/service/VehicleManagerQueryService.java @@ -1,18 +1,22 @@ package com.qaup.collision.datacollector.service; -import com.fasterxml.jackson.databind.JsonNode; +import com.qaup.collision.datacollector.model.dto.UniversalVehicleStatusDTO; import lombok.Builder; import lombok.Data; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; @Service @RequiredArgsConstructor public class VehicleManagerQueryService { - private final VehicleManagerCacheService cacheService; + private final DataCollectorService dataCollectorService; @Data @Builder @@ -29,7 +33,7 @@ public class VehicleManagerQueryService { public VehicleSummary getVehicleSummary(long staleMillis) { long now = System.currentTimeMillis(); - Set vehicleIds = cacheService.getKnownVehicleIds(); + Set vehicleIds = dataCollectorService.getUnmannedVehicleIdsSnapshot(); int online = 0; int offline = 0; @@ -38,40 +42,38 @@ public class VehicleManagerQueryService { int unknown = 0; for (String vehicleId : vehicleIds) { - VehicleManagerCacheService.CacheEntry loginEntry = cacheService.getVehicleLoginStatus(vehicleId); - boolean loginStale = isStale(loginEntry, now, staleMillis); + DataCollectorService.UniversalVehicleStatusCacheEntry entry = + dataCollectorService.getUnmannedVehicleHttpStatus(vehicleId); - if (loginEntry == null) { - long lastSeenAt = getLastSeenAt(vehicleId); - if (now - lastSeenAt > staleMillis) { - offline++; - } else { - unknown++; - } + boolean stale = isStale(entry, now, staleMillis); + UniversalVehicleStatusDTO statusData = entry != null ? entry.getStatusData() : null; + + if (statusData == null) { + offline++; + continue; + } + + if (stale) { + offline++; } else { - if (loginStale) { - offline++; + String powerStatus = statusData.getOperationalStatus() != null ? statusData.getOperationalStatus().getPowerStatus() : null; + if (powerStatus == null) { + unknown++; + } else if ("ON".equalsIgnoreCase(powerStatus)) { + online++; } else { - String loginStatus = getText(loginEntry.getData(), "loginStatus"); - if ("login".equalsIgnoreCase(loginStatus)) { - online++; - } else { - offline++; - } + offline++; } } - VehicleManagerCacheService.CacheEntry suspendEntry = cacheService.getVehicleSuspend(vehicleId); - if (!isStale(suspendEntry, now, staleMillis)) { - Integer suspendStatus = getInt(suspendEntry != null ? suspendEntry.getData() : null, "suspendStatus"); - if (suspendStatus != null && suspendStatus != 0) { + if (!stale) { + String emergencyStatus = statusData.getOperationalStatus() != null ? statusData.getOperationalStatus().getEmergencyStatus() : null; + if (emergencyStatus != null && !"NORMAL".equalsIgnoreCase(emergencyStatus)) { emergency++; } - } - VehicleManagerCacheService.CacheEntry fmsEntry = cacheService.getVehicleFmsMessage(vehicleId); - if (!isStale(fmsEntry, now, staleMillis)) { - if (isFault(fmsEntry != null ? fmsEntry.getData() : null)) { + String systemHealth = statusData.getOperationalStatus() != null ? statusData.getOperationalStatus().getSystemHealth() : null; + if (systemHealth != null && !"HEALTHY".equalsIgnoreCase(systemHealth)) { fault++; } } @@ -93,99 +95,14 @@ public class VehicleManagerQueryService { Map data = new HashMap<>(); data.put("vehicleId", vehicleId); - VehicleManagerCacheService.CacheEntry details = cacheService.getVehicleDetails(vehicleId); - VehicleManagerCacheService.CacheEntry login = cacheService.getVehicleLoginStatus(vehicleId); - VehicleManagerCacheService.CacheEntry chassis = cacheService.getVehicleChassis(vehicleId); - VehicleManagerCacheService.CacheEntry order = cacheService.getVehicleOrder(vehicleId); - VehicleManagerCacheService.CacheEntry suspend = cacheService.getVehicleSuspend(vehicleId); - VehicleManagerCacheService.CacheEntry tailer = cacheService.getVehicleTailer(vehicleId); - VehicleManagerCacheService.CacheEntry position = cacheService.getVehiclePosition(vehicleId); - VehicleManagerCacheService.CacheEntry path = cacheService.getVehiclePath(vehicleId); - VehicleManagerCacheService.CacheEntry fms = cacheService.getVehicleFmsMessage(vehicleId); - - data.put("details", details != null ? details.getData() : null); - data.put("loginStatus", login != null ? login.getData() : null); - data.put("chassis", chassis != null ? chassis.getData() : null); - data.put("order", order != null ? order.getData() : null); - data.put("suspend", suspend != null ? suspend.getData() : null); - data.put("tailer", tailer != null ? tailer.getData() : null); - data.put("position", position != null ? position.getData() : null); - data.put("path", path != null ? path.getData() : null); - data.put("fmsMessage", fms != null ? fms.getData() : null); - data.put("lastSeenAt", getLastSeenAt(vehicleId)); + DataCollectorService.UniversalVehicleStatusCacheEntry entry = + dataCollectorService.getUnmannedVehicleHttpStatus(vehicleId); + data.put("statusData", entry != null ? entry.getStatusData() : null); + data.put("lastSeenAt", entry != null && entry.getTimestamp() != null ? entry.getTimestamp() : 0L); return data; } - private long getLastSeenAt(String vehicleId) { - long last = 0L; - last = Math.max(last, getTimestamp(cacheService.getVehicleDetails(vehicleId))); - last = Math.max(last, getTimestamp(cacheService.getVehicleLoginStatus(vehicleId))); - last = Math.max(last, getTimestamp(cacheService.getVehicleChassis(vehicleId))); - last = Math.max(last, getTimestamp(cacheService.getVehicleOrder(vehicleId))); - last = Math.max(last, getTimestamp(cacheService.getVehicleSuspend(vehicleId))); - last = Math.max(last, getTimestamp(cacheService.getVehicleTailer(vehicleId))); - last = Math.max(last, getTimestamp(cacheService.getVehiclePosition(vehicleId))); - last = Math.max(last, getTimestamp(cacheService.getVehiclePath(vehicleId))); - last = Math.max(last, getTimestamp(cacheService.getVehicleFmsMessage(vehicleId))); - return last; - } - - private long getTimestamp(VehicleManagerCacheService.CacheEntry entry) { - return entry == null ? 0L : entry.getTimestamp(); - } - - private boolean isStale(VehicleManagerCacheService.CacheEntry entry, long now, long staleMillis) { - if (entry == null) { - return true; - } - return now - entry.getTimestamp() > staleMillis; - } - - private boolean isFault(JsonNode node) { - if (node == null || node.isNull()) { - return false; - } - Integer isActive = getInt(node, "isActive"); - if (isActive != null && isActive == 1) { - return true; - } - Integer level = getInt(node, "level"); - return level != null && level >= 4; - } - - private Integer getInt(JsonNode node, String field) { - if (node == null || node.isMissingNode()) { - return null; - } - JsonNode value = node.get(field); - if (value == null || value.isNull()) { - return null; - } - if (value.isInt() || value.isLong()) { - return value.asInt(); - } - if (value.isTextual()) { - try { - return Integer.parseInt(value.asText()); - } catch (NumberFormatException ignored) { - return null; - } - } - return null; - } - - private String getText(JsonNode node, String field) { - if (node == null || node.isMissingNode()) { - return null; - } - JsonNode value = node.get(field); - if (value == null || value.isNull()) { - return null; - } - return value.asText(); - } - @Data @Builder public static class VehicleListItemDTO { @@ -213,98 +130,80 @@ public class VehicleManagerQueryService { public VehicleListResponse getVehicleList(int pageNum, int pageSize, String statusFilter, long staleMillis) { long now = System.currentTimeMillis(); - Set vehicleIds = cacheService.getKnownVehicleIds(); + Set vehicleIds = dataCollectorService.getUnmannedVehicleIdsSnapshot(); List allVehicles = new ArrayList<>(); for (String vehicleId : vehicleIds) { - VehicleManagerCacheService.CacheEntry detailsEntry = cacheService.getVehicleDetails(vehicleId); - VehicleManagerCacheService.CacheEntry loginEntry = cacheService.getVehicleLoginStatus(vehicleId); - VehicleManagerCacheService.CacheEntry positionEntry = cacheService.getVehiclePosition(vehicleId); - VehicleManagerCacheService.CacheEntry chassisEntry = cacheService.getVehicleChassis(vehicleId); - VehicleManagerCacheService.CacheEntry suspendEntry = cacheService.getVehicleSuspend(vehicleId); - VehicleManagerCacheService.CacheEntry fmsEntry = cacheService.getVehicleFmsMessage(vehicleId); - VehicleManagerCacheService.CacheEntry orderEntry = cacheService.getVehicleOrder(vehicleId); + DataCollectorService.UniversalVehicleStatusCacheEntry entry = + dataCollectorService.getUnmannedVehicleHttpStatus(vehicleId); - String vehicleType = getText(detailsEntry != null ? detailsEntry.getData() : null, "vehicleType"); - String loginStatus = getText(loginEntry != null ? loginEntry.getData() : null, "loginStatus"); - boolean loginStale = isStale(loginEntry, now, staleMillis); - - String status; - if (loginEntry == null || loginStale) { - status = "offline"; - } else { - status = "login".equalsIgnoreCase(loginStatus) ? "online" : "offline"; - } + boolean stale = isStale(entry, now, staleMillis); + UniversalVehicleStatusDTO statusData = entry != null ? entry.getStatusData() : null; + String status = "offline"; boolean isEmergency = false; - if (!isStale(suspendEntry, now, staleMillis)) { - Integer suspendStatus = getInt(suspendEntry != null ? suspendEntry.getData() : null, "suspendStatus"); - if (suspendStatus != null && suspendStatus != 0) { + boolean isFault = false; + Map position = null; + Double speed = null; + Double batteryLevel = null; + String currentTask = null; + long lastSeenAt = entry != null && entry.getTimestamp() != null ? entry.getTimestamp() : 0L; + + if (statusData != null && !stale) { + String powerStatus = statusData.getOperationalStatus() != null ? statusData.getOperationalStatus().getPowerStatus() : null; + if (powerStatus == null) { + status = "unknown"; + } else { + status = "ON".equalsIgnoreCase(powerStatus) ? "online" : "offline"; + } + + String emergencyStatus = statusData.getOperationalStatus() != null ? statusData.getOperationalStatus().getEmergencyStatus() : null; + if (emergencyStatus != null && !"NORMAL".equalsIgnoreCase(emergencyStatus)) { isEmergency = true; status = "emergency"; } - } - boolean isFault = false; - if (!isStale(fmsEntry, now, staleMillis)) { - if (isFault(fmsEntry != null ? fmsEntry.getData() : null)) { + String systemHealth = statusData.getOperationalStatus() != null ? statusData.getOperationalStatus().getSystemHealth() : null; + if (systemHealth != null && !"HEALTHY".equalsIgnoreCase(systemHealth)) { isFault = true; status = "fault"; } + + if (statusData.getMotionStatus() != null && statusData.getMotionStatus().getPosition() != null) { + position = new HashMap<>(); + position.put("longitude", statusData.getMotionStatus().getPosition().getLongitude()); + position.put("latitude", statusData.getMotionStatus().getPosition().getLatitude()); + } + + if (statusData.getMotionStatus() != null && statusData.getMotionStatus().getVelocity() != null) { + speed = statusData.getMotionStatus().getVelocity().getSpeed(); + } + + if (statusData.getBatteryStatus() != null && statusData.getBatteryStatus().getMainBattery() != null) { + batteryLevel = statusData.getBatteryStatus().getMainBattery().getChargeLevel(); + } + + if (statusData.getMissionContext() != null && statusData.getMissionContext().getCurrentMission() != null) { + currentTask = statusData.getMissionContext().getCurrentMission().getMissionId(); + } } if (statusFilter != null && !statusFilter.isEmpty() && !status.equalsIgnoreCase(statusFilter)) { continue; } - Map position = null; - if (positionEntry != null && positionEntry.getData() != null) { - JsonNode posData = positionEntry.getData(); - position = new HashMap<>(); - position.put("x", getDouble(posData, "x")); - position.put("y", getDouble(posData, "y")); - position.put("theta", getDouble(posData, "theta")); - } - - Double speed = null; - if (chassisEntry != null && chassisEntry.getData() != null) { - JsonNode chassisData = chassisEntry.getData(); - JsonNode stateInfo = chassisData.path("sys_info").path("state_info"); - speed = getDouble(stateInfo, "d_speed_kmph"); - } - - Double batteryLevel = null; - if (chassisEntry != null && chassisEntry.getData() != null) { - JsonNode chassisData = chassisEntry.getData(); - JsonNode stateInfo = chassisData.path("sys_info").path("state_info"); - String batteryStr = getText(stateInfo, "d_battery_available"); - if (batteryStr != null) { - try { - batteryLevel = Double.parseDouble(batteryStr); - } catch (NumberFormatException ignored) { - } - } - } - - String currentTask = null; - if (orderEntry != null && orderEntry.getData() != null) { - currentTask = getText(orderEntry.getData(), "businessKey"); - } - - VehicleListItemDTO item = VehicleListItemDTO.builder() + allVehicles.add(VehicleListItemDTO.builder() .vehicleId(vehicleId) - .vehicleType(vehicleType) + .vehicleType(null) .status(status) .position(position) .speed(speed) .batteryLevel(batteryLevel) - .lastSeenAt(getLastSeenAt(vehicleId)) + .lastSeenAt(lastSeenAt) .isEmergency(isEmergency) .isFault(isFault) .currentTask(currentTask) - .build(); - - allVehicles.add(item); + .build()); } int total = allVehicles.size(); @@ -324,27 +223,10 @@ public class VehicleManagerQueryService { .build(); } - private Double getDouble(JsonNode node, String field) { - if (node == null || node.isMissingNode()) { - return null; + private boolean isStale(DataCollectorService.UniversalVehicleStatusCacheEntry entry, long now, long staleMillis) { + if (entry == null || entry.getTimestamp() == null) { + return true; } - JsonNode value = node.get(field); - if (value == null || value.isNull()) { - return null; - } - if (value.isDouble() || value.isFloat()) { - return value.asDouble(); - } - if (value.isInt() || value.isLong()) { - return (double) value.asLong(); - } - if (value.isTextual()) { - try { - return Double.parseDouble(value.asText()); - } catch (NumberFormatException ignored) { - return null; - } - } - return null; + return now - entry.getTimestamp() > staleMillis; } } diff --git a/qaup-collision/src/main/java/com/qaup/collision/websocket/handler/CollisionWebSocketHandler.java b/qaup-collision/src/main/java/com/qaup/collision/websocket/handler/CollisionWebSocketHandler.java index 802fe86..710e5b2 100644 --- a/qaup-collision/src/main/java/com/qaup/collision/websocket/handler/CollisionWebSocketHandler.java +++ b/qaup-collision/src/main/java/com/qaup/collision/websocket/handler/CollisionWebSocketHandler.java @@ -1,9 +1,6 @@ package com.qaup.collision.websocket.handler; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.qaup.collision.datacollector.service.VehicleManagerCacheService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; @@ -11,10 +8,8 @@ import org.springframework.lang.NonNull; import org.springframework.stereotype.Component; import org.springframework.web.socket.*; -import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -33,12 +28,9 @@ public class CollisionWebSocketHandler implements WebSocketHandler { private final Map sessions = new ConcurrentHashMap<>(); private final ObjectMapper objectMapper; - private final VehicleManagerCacheService vehicleManagerCacheService; - public CollisionWebSocketHandler(@Qualifier("websocketObjectMapper") ObjectMapper objectMapper, - VehicleManagerCacheService vehicleManagerCacheService) { + public CollisionWebSocketHandler(@Qualifier("websocketObjectMapper") ObjectMapper objectMapper) { this.objectMapper = objectMapper; - this.vehicleManagerCacheService = vehicleManagerCacheService; } @Override @@ -73,8 +65,6 @@ public class CollisionWebSocketHandler implements WebSocketHandler { System.currentTimeMillis() ); sendMessage(session, pongMessage); - } else if (isVehicleListRequest(payload)) { - sendVehicleList(session); } else if (payload.contains("subscribe")) { // 订阅消息处理 String subscribeResponse = String.format( @@ -119,51 +109,6 @@ public class CollisionWebSocketHandler implements WebSocketHandler { return false; } - private boolean isVehicleListRequest(String payload) { - if (payload == null || payload.isBlank()) { - return false; - } - - if (payload.contains("vehicle_list") || payload.contains("vehicleList") || payload.contains("getVehicleList")) { - return true; - } - - try { - JsonNode node = objectMapper.readTree(payload); - String type = node.has("type") ? node.get("type").asText() : null; - String action = node.has("action") ? node.get("action").asText() : null; - return "vehicle_list".equalsIgnoreCase(type) - || "vehicle_list_request".equalsIgnoreCase(type) - || "get_vehicle_list".equalsIgnoreCase(type) - || "vehicleList".equalsIgnoreCase(type) - || "vehicle_list".equalsIgnoreCase(action) - || "getVehicleList".equalsIgnoreCase(action); - } catch (Exception e) { - return false; - } - } - - private void sendVehicleList(WebSocketSession session) throws Exception { - List details = new ArrayList<>(vehicleManagerCacheService.getVehicleDetailsSnapshot()); - - if (details.isEmpty()) { - Set knownVehicleIds = vehicleManagerCacheService.getKnownVehicleIds(); - for (String vehicleId : knownVehicleIds) { - ObjectNode node = objectMapper.createObjectNode(); - node.put("vehicleId", vehicleId); - details.add(node); - } - } - - ObjectNode response = objectMapper.createObjectNode(); - response.put("type", "vehicle_list"); - response.put("count", details.size()); - response.put("timestamp", System.currentTimeMillis()); - response.set("payload", objectMapper.valueToTree(details)); - - sendMessage(session, objectMapper.writeValueAsString(response)); - } - /** * 发送消息给指定会话(线程安全)- 添加发送失败跳过机制 */