airport-qingdao-vue3/src/components/map/controls/VehicleMovementControlRefactored.vue
2025-07-14 11:05:40 +08:00

1097 lines
38 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<!-- 气象监测站弹窗 -->
<WeatherStationPopup
:visible="weatherStationVisible"
@close="weatherStationVisible = false"
/>
<!-- 车辆详情弹窗 -->
<VehicleDetailPopup
:visible="vehicleDetailVisible"
:detail="vehicleDetail"
:popup-style="detailPopupStyle"
@close="vehicleDetailVisible = false"
/>
<!-- 告警/预警提示框 -->
<!-- <AlertNotificationSystem
:alert-message="alertMessage"
:alert-type="alertType"
@show="handleAlertShow"
/>
-->
<!-- 车辆动画系统 -->
<VehicleAnimationSystem
ref="animationSystem"
:map="map"
:vehicle-source="vehicleSource"
:vehicles="vehicles"
:get-vehicle-style="styleManager?.value?.getVehicleStyle || defaultGetVehicleStyle"
/>
<!-- 车辆标签系统 -->
<VehicleLabelSystem
ref="labelSystem"
:map="map"
:vehicles="vehicles"
/>
<!-- 车辆样式管理器 -->
<VehicleStyleManager
ref="styleManager"
:vehicles="vehicles"
/>
</template>
<script setup>
import { ref, onMounted, onUnmounted, watch, onActivated, onDeactivated } from 'vue';
import { Vector as VectorSource } from 'ol/source';
import { Vector as VectorLayer } from 'ol/layer';
import { Style, Icon } from 'ol/style';
import Feature from 'ol/Feature';
import Point from 'ol/geom/Point';
import { transform } from 'ol/proj';
import WebSocketService, { createWebSocket, resetWebSocketInstance } from '../../../utils/websocket.js';
// 导入子组件
import VehicleAnimationSystem from './VehicleAnimationSystem.vue';
import VehicleLabelSystem from './VehicleLabelSystem.vue';
import VehicleDetailPopup from './VehicleDetailPopup.vue';
import WeatherStationPopup from './WeatherStationPopup.vue';
// import AlertNotificationSystem from './AlertNotificationSystem.vue';
import VehicleStyleManager from './VehicleStyleManager.vue';
// 导入默认图标
import carIcon from '../../../assets/images/noPeopleCar.png';
import aircraftInIcon from '../../../assets/images/Aircraft1.png'; // 滑入航空器使用黄色图标
import aircraftOutIcon from '../../../assets/images/Aircraft.png'; // 滑出航空器使用蓝色图标
// 为SockJS提供polyfill
if (typeof window !== 'undefined' && !window.global) {
window.global = window;
}
// 定义props接收地图实例
const props = defineProps({
map: Object
});
// 子组件引用
const animationSystem = ref(null);
const labelSystem = ref(null);
const styleManager = ref(null);
// 气象监测站数据
const weatherStationVisible = ref(true);
// 车辆详情数据
const vehicleDetailVisible = ref(false);
const vehicleDetail = ref({
id: 'QN001',
type: '驱鸟车',
status: '任务中',
startTime: '11-19 11:30',
currentLocation: 'A区T3点',
startLocation: 'T1航站楼',
endLocation: 'T3航站楼',
totalDistance: '1.3km',
battery: '60%',
manager: '张三',
phone: '18661910988'
});
// 弹窗位置样式
const detailPopupStyle = ref({
left: '0px',
top: '0px'
});
// WebSocket连接状态
const wsConnected = ref(false);
let wsService = null;
let reconnectTimer = null;
// 车辆数据存储
const vehicles = ref({});
let vehicleLayer = null;
let vehicleSource = null;
// 车辆超速状态超时计时器存储
const speedViolationTimers = {};
// 动态收集所有车辆/航空器类别
const vehicleCategories = ref({
'AIRCRAFT_IN': { visible: true, showLabel: true, name: '滑入航空器' },
'AIRCRAFT_OUT': { visible: true, showLabel: true, name: '滑出航空器' },
'AIRCRAFT': { visible: true, showLabel: true, name: '其他航空器' },
'UNMANNED_VEHICLE': { visible: true, showLabel: true, name: '无人车' },
'AIRPORT_VEHICLE': { visible: true, showLabel: true, name: '特勤车' },
'SHUTTLE_VEHICLE': { visible: true, showLabel: true, name: '摆渡车' }
});
// 告警/预警状态
const alertMessage = ref('');
const alertType = ref('');
let alertTimer = null;
// 违规状态计时器
const violationStatusTimers = ref({});
// 处理告警显示
function handleAlertShow({ message, type, duration = 5000 }) {
if (alertTimer) {
clearTimeout(alertTimer);
}
alertMessage.value = message;
alertType.value = type;
alertTimer = setTimeout(() => {
alertMessage.value = '';
alertType.value = '';
}, duration);
}
// 显示告警/预警消息
function showAlert(message, type, duration = 5000) {
handleAlertShow({ message, type, duration });
}
// 创建车辆图层
function createVehicleLayer() {
if (!props.map) return;
vehicleSource = new VectorSource();
vehicleLayer = new VectorLayer({
source: vehicleSource,
zIndex: 20,
});
props.map.addLayer(vehicleLayer);
setupMapListeners();
props.map.on('click', handleMapClick);
}
// 处理地图点击事件
function handleMapClick(event) {
const feature = props.map.forEachFeatureAtPixel(event.pixel, function(feature) {
return feature;
});
if (feature) {
const objectId = feature.getId();
if (objectId && vehicles.value[objectId]) {
const vehicle = vehicles.value[objectId];
vehicleDetail.value = {
id: objectId,
type: vehicle.type,
status: '任务中',
startTime: '11-19 11:30',
currentLocation: '当前位置',
startLocation: 'T1航站楼',
endLocation: 'T3航站楼',
totalDistance: '1.3km',
battery: '60%',
manager: '张三',
phone: '18661910988'
};
const pixel = event.pixel;
const viewportPosition = props.map.getViewport().getBoundingClientRect();
detailPopupStyle.value = {
left: (viewportPosition.left + pixel[0] + 10) + 'px',
top: (viewportPosition.top + pixel[1] + 10) + 'px'
};
vehicleDetailVisible.value = true;
} else {
vehicleDetailVisible.value = false;
}
} else {
vehicleDetailVisible.value = false;
}
}
// 监听地图变化,确保标签位置正确更新
function setupMapListeners() {
if (!props.map) return;
// 监听地图移动结束事件
props.map.on('moveend', () => {
if (labelSystem.value) {
labelSystem.value.updateAllLabels();
}
});
}
// 更新车辆位置
function updateVehiclePosition(vehicleData) {
if (!vehicleSource || !props.map) return;
const { object_id, object_type, position, heading, speed } = vehicleData;
let coordinates;
coordinates = transform(
[position.longitude, position.latitude],
'EPSG:4326',
props.map.getView().getProjection()
);
let feature = vehicleSource.getFeatureById(object_id);
// 根据object_type正确分类车辆
let vehicleType = object_type.toUpperCase();
// 判断航空器类型
// CA开头的航班是滑入航空器使用黄色Aircraft1.png图标和airport_out.png文本背景
// MU开头的航班是滑出航空器使用蓝色Aircraft.png图标和airport_bg.png文本背景
const isAircraftOut = vehicleType === 'AIRCRAFT' && object_id.toLowerCase().includes('ca');
const isAircraftIn = vehicleType === 'AIRCRAFT' && object_id.toLowerCase().includes('mu');
const isAircraft = vehicleType === 'AIRCRAFT';
// 判断地面车辆类型
const isUnmannedVehicle = vehicleType === 'UNMANNED_VEHICLE'; // 无人车使用noPeopleCar.png图标和label_bg.png文本背景
const isSpecialVehicle = vehicleType === 'AIRPORT_VEHICLE'; // 特勤车
const isShuttleVehicle = vehicleType === 'SHUTTLE_VEHICLE'; // 摆渡车
if (!feature) {
feature = new Feature({
geometry: new Point(coordinates),
name: `${object_type} ${object_id}`,
type: object_type,
speed: speed,
isAircraftIn: isAircraftIn,
isAircraftOut: isAircraftOut,
isAircraft: isAircraft,
isUnmannedVehicle: isUnmannedVehicle,
isSpecialVehicle: isSpecialVehicle,
isShuttleVehicle: isShuttleVehicle
});
feature.setId(object_id);
// 使用默认样式确保styleManager.value存在时才使用它的方法
if (styleManager.value) {
feature.setStyle(styleManager.value.getVehicleStyle(object_id, speed, heading));
} else {
// 提供一个默认样式,根据车辆类型选择图标
feature.setStyle(defaultGetVehicleStyle(object_id, speed, heading));
}
vehicleSource.addFeature(feature);
vehicles.value[object_id] = {
id: object_id,
type: object_type,
position: coordinates,
heading: heading,
speed: speed,
feature: feature,
isAircraftIn: isAircraftIn,
isAircraftOut: isAircraftOut,
isAircraft: isAircraft,
isUnmannedVehicle: isUnmannedVehicle,
isSpecialVehicle: isSpecialVehicle,
isShuttleVehicle: isShuttleVehicle
};
// 初始化动画数据
if (animationSystem.value) {
animationSystem.value.initVehicleAnimation(object_id, coordinates, heading, speed);
}
// 创建标签
if (labelSystem.value) {
labelSystem.value.updateVehicleLabel(object_id, coordinates, speed);
}
} else {
// 更新动画目标
if (animationSystem.value) {
animationSystem.value.updateVehicleAnimationTarget(object_id, coordinates, heading, speed);
}
vehicles.value[object_id] = {
...vehicles.value[object_id],
position: coordinates,
heading: heading,
speed: speed
};
// 更新标签位置,确保标签始终跟随图标
// 但是如果车辆处于超速状态,不在这里更新标签,避免与超速状态标签冲突
if (labelSystem.value && !vehicles.value[object_id].speedViolation) {
labelSystem.value.updateVehicleLabel(object_id, coordinates, speed);
}
}
}
// 连接WebSocket
function connectWebSocket() {
// 重置WebSocket实例以强制创建新实例
resetWebSocketInstance();
try {
// 使用WebSocketService创建连接
const wsUrl = import.meta.env.VITE_APP_WEBSOCKET_URL;
console.log(`正在连接WebSocket: ${wsUrl}`);
wsService = createWebSocket(wsUrl, {
reconnectInterval: 3000,
maxReconnectAttempts: 5
});
// 注册事件监听
wsService.on('open', (event) => {
console.log('WebSocket连接成功!');
wsConnected.value = true;
// 连接成功后自动订阅消息
setTimeout(() => {
sendSubscribe();
}, 1000); // 延迟1秒发送订阅确保连接稳定
});
wsService.on('message', (data) => {
handleWsMessage(data);
});
wsService.on('error', (event) => {
console.error('WebSocket错误:', event);
wsConnected.value = false;
});
wsService.on('close', (event) => {
console.log(`WebSocket连接关闭: ${event.code} - ${event.reason}`);
wsConnected.value = false;
});
wsService.on('reconnect_failed', () => {
console.error('WebSocket重连失败已达到最大重试次数');
wsConnected.value = false;
});
} catch (error) {
console.error('创建WebSocket连接失败:', error);
}
}
// 处理WebSocket消息
function handleWsMessage(message) {
try {
const data = JSON.parse(message);
// 根据消息类型处理
switch (data.type) {
case 'connection':
console.log(`连接确认: ${data.message}`);
break;
case 'position_update':
// 确保payload存在
if (data.payload && data.payload.object_id) {
// 检查是否需要清除车辆的告警状态
clearVehicleAlertStatus(data.payload);
// 更新车辆位置
updateVehiclePosition(data.payload);
} else {
console.error('位置更新消息格式错误:', data);
}
break;
case 'pong':
console.log('收到心跳响应');
break;
case 'collision_warning':
console.log('收到碰撞预警:', data.payload);
// 显示预警信息
if (data.payload) {
// 获取车辆ID和预警信息
const vehicleId = data.payload.object_id || '未知车辆';
const distance = data.payload.distance || 0;
const message = `预警:${vehicleId} 与其他车辆距离${distance.toFixed(1)}米,请注意避让!`;
showAlert(message, 'warning', 8000);
// 如果需要在地图上标记该车辆的告警状态
if (vehicles.value[vehicleId]) {
vehicles.value[vehicleId].warning = true;
// 如果车辆已有位置信息,更新标签显示
if (vehicles.value[vehicleId].position) {
labelSystem.value.updateVehicleLabel(vehicleId, vehicles.value[vehicleId].position, vehicles.value[vehicleId].speed);
}
}
}
break;
case 'rule_violation':
console.log('收到规则违规:', data.payload);
if (data.payload) {
// 获取车辆ID和告警信息
const vehicleId = data.payload.object_id || data.payload.vehicleId || data.payload.vehicleLicense || '未知车辆';
const violationType = data.payload.violationType || '未知违规';
const alertLevel = data.payload.alertLevel || 'INFO';
const description = data.payload.description || '';
const limitValue = data.payload.limitValue;
const actualValue = data.payload.actualValue;
const ruleName = data.payload.ruleName || '交通规则';
// 检查是否为超速违规
const isSpeedViolation = violationType === 'SPEED_VIOLATION';
// 检查是否为超速结束
const isSpeedViolationEnd = violationType === 'SPEED_VIOLATION_END';
// 如果是超速违规,特殊处理
if (isSpeedViolation && alertLevel === 'WARNING') {
console.log(`检测到超速违规: ${vehicleId}, 实际速度: ${actualValue}, 限速: ${limitValue}`);
// 在地图上标记该车辆的超速状态
if (vehicles.value[vehicleId]) {
// 避免重复设置超速状态导致闪烁
const alreadyInSpeedViolation = vehicles.value[vehicleId].speedViolation;
// 强制设置超速状态,确保图标正确显示
vehicles.value[vehicleId].info = false;
vehicles.value[vehicleId].alarm = false;
vehicles.value[vehicleId].warning = false; // 清除warning状态使用speedViolation状态
vehicles.value[vehicleId].critical = false;
vehicles.value[vehicleId].speedViolation = true; // 标记为超速状态
vehicles.value[vehicleId].limitValue = limitValue;
vehicles.value[vehicleId].actualValue = actualValue;
vehicles.value[vehicleId].description = description;
vehicles.value[vehicleId].ruleName = ruleName;
vehicles.value[vehicleId].lastSpeedViolationTime = Date.now(); // 记录最后一次超速时间
// 设置状态锁定,确保一段时间内不会改变状态
if (!vehicles.value[vehicleId].statusLock) {
vehicles.value[vehicleId].statusLock = {
active: false,
type: null,
until: 0
};
}
// 锁定超速状态20秒确保图标不会来回切换
vehicles.value[vehicleId].statusLock = {
active: true,
type: 'speedViolation',
until: Date.now() + 20000 // 20秒锁定
};
// 清除任何可能存在的缓存图标,确保使用警告图标
vehicles.value[vehicleId].cachedIconSrc = null;
// 更新车辆图标为超速警告图标(只在首次设置或状态改变时更新)
if (!alreadyInSpeedViolation) {
if (vehicles.value[vehicleId].feature) {
vehicles.value[vehicleId].feature.setStyle(styleManager.value.getVehicleStyle(vehicleId, vehicles.value[vehicleId].speed, vehicles.value[vehicleId].heading));
vehicles.value[vehicleId].lastIconUpdateTime = Date.now();
}
}
// 更新标签显示为超速状态(只显示超速相关信息)
if (vehicles.value[vehicleId].position && labelSystem.value) {
// 先移除可能存在的旧标签,避免重复显示
labelSystem.value.removeVehicleLabel(vehicleId);
// 然后创建新的超速状态标签
labelSystem.value.updateVehicleLabel(vehicleId, vehicles.value[vehicleId].position, actualValue || vehicles.value[vehicleId].speed, {
description: description || `超速违规`,
limitValue: limitValue,
actualValue: actualValue,
ruleName: ruleName,
isSpeedViolation: true // 标记为超速状态,避免显示默认信息
});
}
// 设置或重置超速状态自动清除计时器
if (speedViolationTimers[vehicleId]) {
clearTimeout(speedViolationTimers[vehicleId]);
}
// 设置新的超时计时器如果在一定时间内如20秒没有再次接收到超速消息则自动清除超速状态
speedViolationTimers[vehicleId] = setTimeout(() => {
// 确保车辆仍然存在
if (vehicles.value[vehicleId]) {
console.log(`超速状态超时: ${vehicleId}, 自动清除超速状态`);
// 检查是否可以清除状态锁定
if (!vehicles.value[vehicleId].statusLock ||
!vehicles.value[vehicleId].statusLock.active ||
Date.now() > vehicles.value[vehicleId].statusLock.until) {
// 清除超速状态
clearSpeedViolationStatus(vehicleId);
// 清除计时器引用
delete speedViolationTimers[vehicleId];
} else {
// 状态仍然锁定,延迟清除
console.log(`车辆${vehicleId}状态仍然锁定,延迟清除超速状态`);
// 重新设置计时器,等待锁定结束
speedViolationTimers[vehicleId] = setTimeout(() => {
clearSpeedViolationStatus(vehicleId);
delete speedViolationTimers[vehicleId];
}, vehicles.value[vehicleId].statusLock.until - Date.now());
}
}
}, 20000); // 20秒超时确保状态稳定
}
return; // 已处理超速情况不再进入下面的alertLevel处理
}
// 如果是超速结束,清除超速状态
if (isSpeedViolationEnd) {
console.log(`检测到超速结束: ${vehicleId}`);
// 在地图上清除该车辆的超速状态
if (vehicles.value[vehicleId]) {
// 检查是否可以清除状态锁定
if (!vehicles.value[vehicleId].statusLock ||
!vehicles.value[vehicleId].statusLock.active ||
Date.now() > vehicles.value[vehicleId].statusLock.until) {
// 清除超速状态,使用专门的函数
clearSpeedViolationStatus(vehicleId);
} else {
// 状态仍然锁定,延迟清除
console.log(`车辆${vehicleId}状态仍然锁定,延迟清除超速状态`);
// 设置计时器,等待锁定结束
if (speedViolationTimers[vehicleId]) {
clearTimeout(speedViolationTimers[vehicleId]);
}
speedViolationTimers[vehicleId] = setTimeout(() => {
clearSpeedViolationStatus(vehicleId);
delete speedViolationTimers[vehicleId];
}, vehicles.value[vehicleId].statusLock.until - Date.now());
}
}
return; // 已处理超速结束情况不再进入下面的alertLevel处理
}
// 根据alertLevel处理其他类型的告警
switch (alertLevel.toUpperCase()) {
case 'Info': // 预警
// 显示大的预警提示框
showAlert(`预警:${vehicleId} ${description}`, 'warning', 10000);
// 在地图上标记该车辆的预警状态
if (vehicles.value[vehicleId]) {
vehicles.value[vehicleId].warning = true;
vehicles.value[vehicleId].alarm = false;
vehicles.value[vehicleId].critical = false;
vehicles.value[vehicleId].info = false;
// 更新车辆图标为警告图标
if (vehicles.value[vehicleId].feature) {
vehicles.value[vehicleId].feature.setStyle(styleManager.value.getVehicleStyle(vehicleId, vehicles.value[vehicleId].speed, vehicles.value[vehicleId].heading));
}
// 更新标签显示
if (vehicles.value[vehicleId].position) {
labelSystem.value.updateVehicleLabel(vehicleId, vehicles.value[vehicleId].position, vehicles.value[vehicleId].speed, {
description: description,
limitValue: limitValue,
actualValue: actualValue
});
}
}
break;
case 'ALERT': // 告警
// 显示大的告警提示框
showAlert(`⚠️ 告警:${vehicleId} ${description}`, 'alarm', 10000);
// 在地图上标记该车辆的告警状态
if (vehicles.value[vehicleId]) {
vehicles.value[vehicleId].alarm = true;
vehicles.value[vehicleId].warning = false;
vehicles.value[vehicleId].critical = false;
vehicles.value[vehicleId].info = false;
// 更新车辆图标为告警图标
if (vehicles.value[vehicleId].feature) {
vehicles.value[vehicleId].feature.setStyle(styleManager.value.getVehicleStyle(vehicleId, vehicles.value[vehicleId].speed, vehicles.value[vehicleId].heading));
}
// 更新标签显示
if (vehicles.value[vehicleId].position) {
labelSystem.value.updateVehicleLabel(vehicleId, vehicles.value[vehicleId].position, vehicles.value[vehicleId].speed, {
description: description,
limitValue: limitValue,
actualValue: actualValue
});
}
}
break;
case 'CRITICAL': // 越界
// 不显示大的提示框,仅改变车辆图标颜色和标签背景
if (vehicles.value[vehicleId]) {
vehicles.value[vehicleId].critical = true;
vehicles.value[vehicleId].alarm = false;
vehicles.value[vehicleId].warning = false;
vehicles.value[vehicleId].info = false;
// 更新车辆图标为警告图标
if (vehicles.value[vehicleId].feature) {
vehicles.value[vehicleId].feature.setStyle(styleManager.value.getVehicleStyle(vehicleId, vehicles.value[vehicleId].speed, vehicles.value[vehicleId].heading));
}
// 更新标签显示
if (vehicles.value[vehicleId].position) {
labelSystem.value.updateVehicleLabel(vehicleId, vehicles.value[vehicleId].position, vehicles.value[vehicleId].speed, {
description: description,
limitValue: limitValue,
actualValue: actualValue
});
}
}
break;
case 'WARNING': // 超速 - 这个case已经在上面的SPEED_VIOLATION中处理了这里不再重复处理
console.log(`收到WARNING级别的超速消息但已在SPEED_VIOLATION中处理: ${vehicleId}`);
break;
default:
console.log(`未处理的告警级别: ${alertLevel}`);
}
}
break;
case 'vehicle_command':
console.log('收到车辆控制指令:', data.payload);
break;
default:
// 其他类型的消息可以根据需要处理
console.log(`收到其他类型消息: ${data.type}`, data);
break;
}
} catch (e) {
console.error('处理WebSocket消息出错:', e, message);
}
}
// 清除车辆超速状态的专门函数
function clearSpeedViolationStatus(vehicleId) {
const vehicle = vehicles.value[vehicleId];
if (!vehicle) return;
console.log(`开始清除车辆${vehicleId}的超速状态`);
// 检查是否可以清除状态锁定
if (vehicle.statusLock && vehicle.statusLock.active && Date.now() < vehicle.statusLock.until) {
console.log(`车辆${vehicleId}状态锁定中,暂不清除超速状态`);
return;
}
// 清除所有告警状态
vehicle.info = false;
vehicle.alarm = false;
vehicle.warning = false;
vehicle.critical = false;
vehicle.speedViolation = false;
vehicle.limitValue = undefined;
vehicle.actualValue = undefined;
vehicle.description = undefined;
vehicle.ruleName = undefined;
// 清除状态锁定
if (vehicle.statusLock) {
vehicle.statusLock.active = false;
vehicle.statusLock.type = null;
vehicle.statusLock.until = 0;
}
// 清除缓存的图标,确保使用默认图标
vehicle.cachedIconSrc = null;
vehicle.lastIconUpdateTime = Date.now();
// 更新车辆图标为默认图标
if (vehicle.feature && styleManager.value) {
vehicle.feature.setStyle(styleManager.value.getVehicleStyle(vehicleId, vehicle.speed, vehicle.heading));
}
// 更新标签显示为正常状态只显示车辆ID不显示0.00km/h
if (vehicle.position && labelSystem.value) {
labelSystem.value.updateVehicleLabel(vehicleId, vehicle.position, vehicle.speed > 0.1 ? vehicle.speed : 0);
}
console.log(`已成功清除车辆${vehicleId}的超速状态`);
}
// 清除车辆告警状态(如果需要)
function clearVehicleAlertStatus(vehicleData) {
const { object_id, speed } = vehicleData;
// 检查该车辆是否已存在且有告警状态
const existingVehicle = vehicles.value[object_id];
if (!existingVehicle) return;
// 检查是否有状态锁定
if (existingVehicle.statusLock &&
existingVehicle.statusLock.active &&
Date.now() < existingVehicle.statusLock.until) {
// 状态锁定中,不清除状态
return;
}
// 检查是否有超速状态 - 更严格的清除条件,避免图标来回切换
if (existingVehicle.speedViolation && existingVehicle.limitValue) {
// 如果当前速度低于限速值,清除超速状态
if (speed < existingVehicle.limitValue) {
// 添加更大的缓冲区,避免在临界值附近频繁切换状态
// 只有当速度低于限速值的70%时才清除超速状态,确保状态稳定
const speedBuffer = existingVehicle.limitValue * 0.7;
if (speed < speedBuffer) {
console.log(`检测到车辆${object_id}速度降低到${speed.toFixed(1)}km/h低于限速${existingVehicle.limitValue}km/h的70%,考虑清除超速状态`);
// 检查是否应该清除超速状态
// 如果最后一次超速时间在15秒内不清除状态以避免闪烁
const now = Date.now();
if (!existingVehicle.lastSpeedViolationTime || now - existingVehicle.lastSpeedViolationTime > 15000) {
// 检查状态锁定
if (!existingVehicle.statusLock ||
!existingVehicle.statusLock.active ||
now > existingVehicle.statusLock.until) {
// 清除超速状态
clearSpeedViolationStatus(object_id);
console.log(`已清除${object_id}的超速状态,恢复为普通状态`);
} else {
console.log(`车辆${object_id}状态锁定中,暂不清除超速状态`);
}
} else {
console.log(`车辆${object_id}刚刚处于超速状态,暂不清除状态以避免闪烁`);
}
}
}
}
// 检查是否有任何其他告警状态
const hasAlertStatus = existingVehicle.info || existingVehicle.warning ||
existingVehicle.alarm || existingVehicle.critical;
// 如果有告警状态,需要检查是否应该清除
if (hasAlertStatus) {
// 只处理超速状态(info)的自动恢复
// 1. 判断是否为超速状态
const isSpeedViolation = existingVehicle.info;
// 2. 判断当前速度是否已降低到安全值
// 这里使用一个默认阈值判断,实际应用中最好使用服务端返回的限速值
// 假设超速阈值为50km/h
const SPEED_THRESHOLD = 5; // 超速阈值单位km/h
const isBelowThreshold = speed < SPEED_THRESHOLD;
// 3. 如果是超速状态且当前速度已降低到阈值以下,清除超速状态
if (isSpeedViolation && isBelowThreshold) {
console.log(`检测到车辆${object_id}速度降低到${speed.toFixed(1)}km/h低于阈值${SPEED_THRESHOLD}km/h自动清除超速状态`);
// 清除超速状态
existingVehicle.info = false;
// 更新车辆图标为默认图标
if (existingVehicle.feature) {
existingVehicle.feature.setStyle(styleManager.value.getVehicleStyle(object_id, speed, existingVehicle.heading));
}
// 更新标签显示为正常状态
if (existingVehicle.position) {
labelSystem.value.updateVehicleLabel(object_id, existingVehicle.position, speed);
}
console.log(`已清除${object_id}的超速状态,恢复为普通状态`);
} else {
console.log(`车辆${object_id}有告警状态,但不符合自动清除条件,保持该状态`);
}
}
}
// 发送心跳
function sendPing() {
if (wsService) {
wsService.send('ping');
console.log('发送心跳: ping');
}
}
// 订阅消息
function sendSubscribe() {
if (wsService) {
const message = JSON.stringify({
type: 'subscribe',
topics: ['position_update', 'collision_warning', 'rule_violation'],
timestamp: Date.now()
});
wsService.send(message);
console.log('发送订阅请求');
}
}
// 清理资源
function cleanup() {
// 断开WebSocket连接
if (wsService) {
wsService.close();
wsService = null;
}
if (reconnectTimer) {
clearTimeout(reconnectTimer);
reconnectTimer = null;
}
// 清理所有超速状态计时器
Object.keys(speedViolationTimers).forEach(vehicleId => {
clearTimeout(speedViolationTimers[vehicleId]);
delete speedViolationTimers[vehicleId];
});
// 移除图层
if (vehicleLayer && props.map) {
props.map.removeLayer(vehicleLayer);
vehicleLayer = null;
}
// 移除标签
if (props.map && labelSystem.value) {
Object.values(vehicles.value).forEach(vehicle => {
if (vehicle.overlay) {
console.log(`移除标签: ${vehicle.id}`);
labelSystem.value.removeVehicleLabel(vehicle.id);
}
});
}
// 清空车辆数据
vehicles.value = {};
}
// 组件挂载时初始化
onMounted(() => {
// 创建图层并连接WebSocket
if (props.map) {
createVehicleLayer();
}
// 连接WebSocket
connectWebSocket();
// 设置定时发送心跳
const pingInterval = setInterval(() => {
if (wsService && wsConnected.value) {
sendPing();
}
}, 30000); // 每30秒发送一次心跳
// 组件卸载时清理资源
onUnmounted(() => {
clearInterval(pingInterval);
if (reconnectTimer) {
clearTimeout(reconnectTimer);
reconnectTimer = null;
}
cleanup();
});
});
// 组件被激活时(切换回该路由时)
onActivated(() => {
console.log('VehicleMovementControl组件被激活');
// 如果WebSocket未连接重新连接
if (!wsConnected.value && !wsService) {
console.log('组件激活WebSocket未连接正在重新连接...');
// 延迟一点时间再连接确保DOM已完全渲染
reconnectTimer = setTimeout(() => {
connectWebSocket();
}, 500);
} else {
console.log('组件激活WebSocket已连接');
}
});
// 组件被停用时(切换到其他路由时)
onDeactivated(() => {
console.log('VehicleMovementControl组件被停用');
// 可以选择在这里关闭WebSocket也可以保持连接
// 如果希望在组件隐藏时断开连接,可以取消下面的注释
/*
if (wsService) {
console.log('组件停用关闭WebSocket连接');
wsService.close();
wsService = null;
wsConnected.value = false;
}
*/
});
// 监听地图实例变化
watch(() => props.map, (newMap) => {
if (newMap) {
createVehicleLayer();
}
});
// 向外暴露方法
defineExpose({
updateVehiclePosition,
wsConnected,
sendPing,
sendSubscribe,
vehicleCategories,
weatherStationVisible,
vehicleDetailVisible,
vehicleDetail,
toggleWeatherStationVisibility() {
weatherStationVisible.value = !weatherStationVisible.value;
},
toggleVehicleDetailVisibility() {
vehicleDetailVisible.value = !vehicleDetailVisible.value;
},
updateVehicleDetail(data) {
if (data) {
vehicleDetail.value = {...vehicleDetail.value, ...data};
vehicleDetailVisible.value = true;
}
},
showVehicleDetail(vehicleId, pixelPosition) {
if (vehicles.value[vehicleId]) {
// 更新车辆详情
vehicleDetail.value = {
id: vehicleId,
type: vehicles.value[vehicleId].type,
status: '任务中',
startTime: '11-19 11:30',
currentLocation: '当前位置',
startLocation: 'T1航站楼',
endLocation: 'T3航站楼',
totalDistance: '1.3km',
battery: '60%',
manager: '张三',
phone: '18661910988'
};
// 设置弹窗位置
if (pixelPosition) {
const viewportPosition = props.map.getViewport().getBoundingClientRect();
detailPopupStyle.value = {
left: (viewportPosition.left + pixelPosition[0] + 10) + 'px',
top: (viewportPosition.top + pixelPosition[1] + 10) + 'px'
};
}
// 显示详情弹窗
vehicleDetailVisible.value = true;
}
},
setCategoryVisibility(type, { visible, showLabel }) {
if (vehicleCategories.value[type]) {
// 更新分类设置
vehicleCategories.value[type].visible = visible;
vehicleCategories.value[type].showLabel = showLabel;
// 立即应用更改 - 更新所有属于此类别的车辆
Object.values(vehicles.value).forEach(vehicle => {
let vehicleTypeKey = vehicle.type;
// 根据车辆特征确定类别
if (vehicle.isAircraftIn) vehicleTypeKey = 'AIRCRAFT_IN';
else if (vehicle.isAircraftOut) vehicleTypeKey = 'AIRCRAFT_OUT';
else if (vehicle.isUnmannedVehicle) vehicleTypeKey = 'UNMANNED_VEHICLE';
else if (vehicle.isSpecialVehicle) vehicleTypeKey = 'AIRPORT_VEHICLE';
else if (vehicle.isShuttleVehicle) vehicleTypeKey = 'SHUTTLE_VEHICLE';
// 如果车辆属于当前修改的类别
if (vehicleTypeKey === type) {
// 更新图标可见性 - 如果不可见,则完全移除图标样式
if (vehicle.feature) {
if (visible) {
// 显示图标 - 确保styleManager.value存在
if (styleManager.value) {
vehicle.feature.setStyle(styleManager.value.getVehicleStyle(vehicle.id, vehicle.speed, vehicle.heading));
} else {
// 提供一个默认样式,根据车辆类型选择图标
vehicle.feature.setStyle(defaultGetVehicleStyle(vehicle.id, vehicle.speed, vehicle.heading));
}
} else {
// 完全隐藏图标使用空样式而非null
vehicle.feature.setStyle(new Style({}));
}
}
// 单独控制文本标签可见性,与图标显示状态无关
if (labelSystem.value) {
labelSystem.value.setLabelVisibility(vehicle.id, showLabel);
}
}
});
}
},
reconnectWebSocket() {
console.log('手动重连WebSocket');
connectWebSocket();
},
// 添加平滑动画相关方法
startVehicleSmoothing() {
if (animationSystem.value) {
animationSystem.value.startAnimationLoop();
}
},
stopVehicleSmoothing() {
if (animationSystem.value) {
animationSystem.value.stopAnimationLoop();
}
},
resetVehicleAnimations() {
if (animationSystem.value) {
animationSystem.value.resetAnimations();
}
}
});
// 添加一个默认的getVehicleStyle函数
function defaultGetVehicleStyle(id, _speed, heading) {
// 检查车辆类型
const vehicle = vehicles.value[id];
if (!vehicle) return createDefaultStyle(carIcon, heading);
// 根据车辆类型选择图标
if (vehicle.isAircraftIn) {
// 滑入航空器(CA开头)使用黄色Aircraft1.png图标
return createDefaultStyle(aircraftInIcon, heading);
} else if (vehicle.isAircraftOut) {
// 滑出航空器(MU开头)使用蓝色Aircraft.png图标
return createDefaultStyle(aircraftOutIcon, heading);
} else if (vehicle.isUnmannedVehicle) {
// 无人车使用noPeopleCar.png图标
return createDefaultStyle(carIcon, heading);
} else if (vehicle.isSpecialVehicle) {
// 特勤车
return createDefaultStyle(carIcon, heading);
} else if (vehicle.isShuttleVehicle) {
// 摆渡车
return createDefaultStyle(carIcon, heading);
} else {
// 默认车辆图标
return createDefaultStyle(carIcon, heading);
}
}
// 创建默认样式的辅助函数
function createDefaultStyle(iconSrc, heading) {
return new Style({
image: new Icon({
src: iconSrc,
scale: 1.5,
anchor: [0.5, 0.5],
rotation: ((heading - 72) * Math.PI) / 180,
})
});
}
</script>