去掉无人车ws获取方式,增加http获取无人车列表

This commit is contained in:
sladro 2026-01-23 11:08:18 +08:00
parent b23ca99916
commit dc4934d4d6
8 changed files with 252 additions and 289 deletions

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -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;
}
}

View File

@ -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<String> 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<Void> entity = new HttpEntity<>(headers);
ResponseEntity<com.qaup.collision.datacollector.model.dto.UniversalApiResponse<List<String>>> response =
restTemplate.exchange(
url,
HttpMethod.GET,
entity,
new ParameterizedTypeReference<com.qaup.collision.datacollector.model.dto.UniversalApiResponse<List<String>>>() {}
);
if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
com.qaup.collision.datacollector.model.dto.UniversalApiResponse<List<String>> 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();
}
}
}

View File

@ -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<String, MovingObject> activeMovingObjectsCache = new ConcurrentHashMap<>();
// 无人车ID列表来自上游 HTTPGET /api/vehicle_details
private final Set<String> unmannedVehicleIds = ConcurrentHashMap.newKeySet();
// 无人车最新HTTP状态缓存用于前端HTTP接口/统计展示key=vehicleId
private final Map<String, UniversalVehicleStatusCacheEntry> unmannedVehicleHttpStatusCache = new ConcurrentHashMap<>();
public Set<String> 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);
@ -166,6 +191,31 @@ public class DataCollectorService {
}
}
/**
* 每分钟刷新无人车列表上游GET /api/vehicle_details
*/
@Scheduled(fixedRateString = "${data.collector.vehicle-manager.http.vehicle-details-refresh-ms:60000}")
public void refreshUnmannedVehicleList() {
if (collectorDisabled) {
return;
}
List<String> 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<String> knownVehicleIds = getKnownUnmannedVehicleIds();
if (unmannedVehicleIds.isEmpty()) {
// 启动时可能尚未刷新到列表这里做一次快速拉取作为兜底
List<String> 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<String> getKnownUnmannedVehicleIds() {
java.util.Set<String> 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;
}

View File

@ -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<String> vehicleIds = cacheService.getKnownVehicleIds();
Set<String> 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) {
boolean stale = isStale(entry, now, staleMillis);
UniversalVehicleStatusDTO statusData = entry != null ? entry.getStatusData() : null;
if (statusData == null) {
offline++;
} else {
unknown++;
continue;
}
} else {
if (loginStale) {
if (stale) {
offline++;
} else {
String loginStatus = getText(loginEntry.getData(), "loginStatus");
if ("login".equalsIgnoreCase(loginStatus)) {
String powerStatus = statusData.getOperationalStatus() != null ? statusData.getOperationalStatus().getPowerStatus() : null;
if (powerStatus == null) {
unknown++;
} else if ("ON".equalsIgnoreCase(powerStatus)) {
online++;
} else {
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<String, Object> 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<String> vehicleIds = cacheService.getKnownVehicleIds();
Set<String> vehicleIds = dataCollectorService.getUnmannedVehicleIdsSnapshot();
List<VehicleListItemDTO> 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);
boolean stale = isStale(entry, now, staleMillis);
UniversalVehicleStatusDTO statusData = entry != null ? entry.getStatusData() : null;
String status;
if (loginEntry == null || loginStale) {
status = "offline";
String status = "offline";
boolean isEmergency = false;
boolean isFault = false;
Map<String, Object> 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 = "login".equalsIgnoreCase(loginStatus) ? "online" : "offline";
status = "ON".equalsIgnoreCase(powerStatus) ? "online" : "offline";
}
boolean isEmergency = false;
if (!isStale(suspendEntry, now, staleMillis)) {
Integer suspendStatus = getInt(suspendEntry != null ? suspendEntry.getData() : null, "suspendStatus");
if (suspendStatus != null && suspendStatus != 0) {
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<String, Object> 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;
}
}

View File

@ -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<String, WebSocketSession> 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<JsonNode> details = new ArrayList<>(vehicleManagerCacheService.getVehicleDetailsSnapshot());
if (details.isEmpty()) {
Set<String> 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));
}
/**
* 发送消息给指定会话线程安全- 添加发送失败跳过机制
*/