去掉无人车ws获取方式,增加http获取无人车列表
This commit is contained in:
parent
b23ca99916
commit
dc4934d4d6
@ -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
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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列表(来自上游 HTTP:GET /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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息给指定会话(线程安全)- 添加发送失败跳过机制
|
||||
*/
|
||||
|
||||
Loading…
Reference in New Issue
Block a user