QAUP_Management/tools/test_websocket.html

647 lines
30 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>冲突检测与车辆状态WebSocket测试工具</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}
.container {
max-width: 1920px;
margin: 0 auto;
background: white;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 2px;
}
.test-section {
border: 1px solid #ddd;
border-radius: 8px;
margin: 2px 0;
padding: 10px;
background: #fafafa;
}
.control-group {
margin: 15px 0;
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
.control-group label {
min-width: 120px;
font-weight: bold;
}
select, input, button {
padding: 8px 12px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 14px;
}
button {
background: #007bff;
color: white;
border: none;
cursor: pointer;
transition: background 0.3s;
}
button:hover {
background: #0056b3;
}
button:disabled {
background: #6c757d;
cursor: not-allowed;
}
.status {
padding: 10px;
border-radius: 5px;
margin: 10px 0;
font-weight: bold;
}
.status.connected {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.status.disconnected {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.log-container {
border: 1px solid #ddd;
border-radius: 4px;
height: 960px;
overflow-y: auto;
padding: 10px;
background: white;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 12px;
}
.log-entry {
margin: 2px 0;
padding: 2px 5px;
border-radius: 3px;
}
.log-info { background: #f8f9fa; }
.log-success { background: #d4edda; color: #155724; }
.log-error { background: #f8d7da; color: #721c24; }
.log-warning { background: #fff3cd; color: #856404; }
.message-stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 8px;
width: 100%;
font-size: 12px !important;
}
.message-stats div {
font-size: 12px !important;
}
</style>
</head>
<body>
<div class="container">
<!-- 消息统计 -->
<div class="test-section">
<h3>📊 消息统计</h3>
<div class="control-group">
<div id="messageStats" class="message-stats">
<div><strong>总消息数:</strong> <span id="totalCount">0</span></div>
<div><strong>位置更新:</strong> <span id="positionUpdateCount">0</span></div>
<div><strong>碰撞预警:</strong> <span id="collisionWarningCount">0</span></div>
<div><strong>规则违规:</strong> <span id="ruleViolationCount">0</span></div>
<div><strong>红绿灯状态:</strong> <span id="trafficLightStatusCount">0</span></div>
<div><strong>路口红绿灯:</strong> <span id="intersectionTrafficLightCount">0</span></div>
<div><strong>航空器路由:</strong> <span id="aircraftRouteCount">0</span></div>
<div><strong>事件驱动路由:</strong> <span id="eventDrivenRouteCount">0</span></div>
<div><strong>航班进出港:</strong> <span id="flightNotificationCount">0</span></div>
<div><strong>路径冲突:</strong> <span id="pathConflictCount">0</span></div>
<div><strong>车辆指令:</strong> <span id="vehicleCommandCount">0</span></div>
<div><strong>规则执行状态:</strong> <span id="ruleExecutionCount">0</span></div>
<div><strong>规则状态变更:</strong> <span id="ruleStateChangeCount">0</span></div>
<div><strong>车辆状态更新:</strong> <span id="vehicleStatusUpdateCount">0</span></div>
<div><strong>心跳响应:</strong> <span id="pongCount">0</span></div>
<div><strong>连接确认:</strong> <span id="connectionCount">0</span></div>
<div><strong>未知消息:</strong> <span id="unknownCount">0</span></div>
</div>
</div>
</div>
<!-- 冲突检测WebSocket测试 -->
<div class="test-section">
<h3>🚗 冲突检测与车辆状态WebSocket</h3>
<div class="control-group">
<label>服务器地址:</label>
<input type="text" id="collisionServerIP" placeholder="IP地址" value="localhost" />
<span>:</span>
<input type="number" id="collisionServerPort" placeholder="端口" value="8080" min="1" max="65535" />
<input type="text" id="collisionServerPath" placeholder="路径" value="/collision" />
<button onclick="updateServerAddress()">更新地址</button>
</div>
<div class="control-group">
<label>完整地址:</label>
<select id="collisionServerSelect">
<option value="ws://localhost:8080/collision">ws://localhost:8080/collision</option>
</select>
</div>
<div class="control-group">
<button id="collisionConnectBtn" onclick="connectCollisionWebSocket()">连接</button>
<button id="collisionDisconnectBtn" onclick="disconnectCollisionWebSocket()" disabled>断开</button>
<button onclick="sendCollisionPing()">发送心跳</button>
<button onclick="sendCollisionSubscribe()">订阅消息</button>
</div>
<div class="status disconnected" id="collisionStatus">状态:未连接</div>
<div>
<h4>日志:</h4>
<div class="log-container" id="collisionLog"></div>
</div>
</div>
<!-- 控制面板 -->
<div class="test-section">
<h3>🎛️ 控制面板</h3>
<div class="control-group">
<button onclick="clearLogs()">清空日志</button>
<button onclick="showConnectionStatus()">显示连接状态</button>
<button onclick="performStressTest()">压力测试</button>
<button onclick="resetMessageStats()">重新统计</button>
</div>
</div>
</div>
<script>
// 全局变量
let collisionWebSocket = null;
let reconnectAttempts = 0;
const maxReconnectAttempts = 5;
// 更新服务器地址
function updateServerAddress() {
const ip = document.getElementById('collisionServerIP').value || 'localhost';
const port = document.getElementById('collisionServerPort').value || '8080';
const path = document.getElementById('collisionServerPath').value || '/collision';
// 确保路径以 / 开头
const formattedPath = path.startsWith('/') ? path : '/' + path;
const serverUrl = `ws://${ip}:${port}${formattedPath}`;
// 更新下拉框
const select = document.getElementById('collisionServerSelect');
select.innerHTML = `<option value="${serverUrl}">${serverUrl}</option>`;
log('collisionLog', `服务器地址已更新为: ${serverUrl}`, 'info');
}
// 消息统计
let messageStats = {
total: 0,
position_update: 0,
collision_warning: 0,
rule_violation: 0,
traffic_light_status: 0,
intersection_traffic_light_status: 0,
aircraftRouteUpdate: 0,
eventDrivenRoute: 0, // 事件驱动的路由更新
flight_notification: 0,
path_conflict_alert: 0,
vehicle_command: 0,
rule_execution_status: 0,
rule_state_change: 0,
vehicle_status_update: 0,
pong: 0,
connection: 0,
unknown: 0
};
// 记录未知消息类型以便调试
let unknownMessageTypes = new Set();
// 通用日志函数
function log(containerId, message, type = 'info') {
const container = document.getElementById(containerId);
const timestamp = new Date().toLocaleTimeString();
const entry = document.createElement('div');
entry.className = `log-entry log-${type}`;
entry.innerHTML = `[${timestamp}] ${message}`;
container.appendChild(entry);
container.scrollTop = container.scrollHeight;
}
// 更新连接状态
function updateStatus(statusId, connected, message = '') {
const statusDiv = document.getElementById(statusId);
statusDiv.className = `status ${connected ? 'connected' : 'disconnected'}`;
statusDiv.textContent = `状态:${connected ? '已连接' : '未连接'}${message ? ' - ' + message : ''}`;
}
// 更新消息统计
function updateMessageStats(messageType) {
messageStats.total++;
// 统一转换为小写以匹配统计键
const normalizedType = messageType?.toLowerCase();
// 处理特殊映射
let statsKey = normalizedType;
if (normalizedType === 'vehicle_status_update' || normalizedType === 'VEHICLE_STATUS_UPDATE') {
statsKey = 'vehicle_status_update';
}
// 处理aircraftRouteUpdate的映射
if (normalizedType === 'aircraftrouteupdate') {
statsKey = 'aircraftRouteUpdate';
}
if (messageStats.hasOwnProperty(statsKey)) {
messageStats[statsKey]++;
} else {
messageStats.unknown++;
// 调试:记录未知的消息类型
unknownMessageTypes.add(messageType);
console.log('Unknown message type:', messageType, 'normalized:', normalizedType);
}
// 更新页面显示
document.getElementById('totalCount').textContent = messageStats.total;
document.getElementById('positionUpdateCount').textContent = messageStats.position_update;
document.getElementById('collisionWarningCount').textContent = messageStats.collision_warning;
document.getElementById('ruleViolationCount').textContent = messageStats.rule_violation;
document.getElementById('trafficLightStatusCount').textContent = messageStats.traffic_light_status;
document.getElementById('intersectionTrafficLightCount').textContent = messageStats.intersection_traffic_light_status;
document.getElementById('aircraftRouteCount').textContent = messageStats.aircraftRouteUpdate;
document.getElementById('eventDrivenRouteCount').textContent = messageStats.eventDrivenRoute;
document.getElementById('flightNotificationCount').textContent = messageStats.flight_notification;
document.getElementById('pathConflictCount').textContent = messageStats.path_conflict_alert;
document.getElementById('vehicleCommandCount').textContent = messageStats.vehicle_command;
document.getElementById('ruleExecutionCount').textContent = messageStats.rule_execution_status;
document.getElementById('ruleStateChangeCount').textContent = messageStats.rule_state_change;
document.getElementById('vehicleStatusUpdateCount').textContent = messageStats.vehicle_status_update;
document.getElementById('pongCount').textContent = messageStats.pong;
document.getElementById('connectionCount').textContent = messageStats.connection;
document.getElementById('unknownCount').textContent = messageStats.unknown;
}
// =================== 冲突检测WebSocket ===================
function connectCollisionWebSocket() {
if (collisionWebSocket && collisionWebSocket.readyState === WebSocket.OPEN) {
log('collisionLog', 'WebSocket已经连接', 'warning');
return;
}
const serverUrl = document.getElementById('collisionServerSelect').value;
log('collisionLog', `连接到: ${serverUrl}`, 'info');
try {
collisionWebSocket = new WebSocket(serverUrl);
collisionWebSocket.onopen = function(event) {
log('collisionLog', '冲突检测WebSocket连接成功!', 'success');
updateStatus('collisionStatus', true, '冲突检测');
document.getElementById('collisionConnectBtn').disabled = true;
document.getElementById('collisionDisconnectBtn').disabled = false;
reconnectAttempts = 0;
};
collisionWebSocket.onmessage = function(event) {
log('collisionLog', `收到消息: ${event.data}`, 'success');
try {
const message = JSON.parse(event.data);
handleCollisionMessage(message);
} catch (e) {
log('collisionLog', `非JSON消息: ${event.data}`, 'info');
}
};
collisionWebSocket.onerror = function(error) {
log('collisionLog', `连接错误: ${error}`, 'error');
updateStatus('collisionStatus', false);
};
collisionWebSocket.onclose = function(event) {
log('collisionLog', `连接关闭: ${event.code} - ${event.reason}`, 'warning');
updateStatus('collisionStatus', false);
document.getElementById('collisionConnectBtn').disabled = false;
document.getElementById('collisionDisconnectBtn').disabled = true;
// 自动重连
if (reconnectAttempts < maxReconnectAttempts) {
reconnectAttempts++;
log('collisionLog', `尝试重连 (${reconnectAttempts}/${maxReconnectAttempts})`, 'info');
setTimeout(connectCollisionWebSocket, 3000);
}
};
} catch (error) {
log('collisionLog', `连接失败: ${error.message}`, 'error');
updateStatus('collisionStatus', false);
}
}
function disconnectCollisionWebSocket() {
if (collisionWebSocket) {
collisionWebSocket.close();
collisionWebSocket = null;
log('collisionLog', '主动断开连接', 'info');
reconnectAttempts = maxReconnectAttempts;
}
}
function sendCollisionPing() {
if (collisionWebSocket && collisionWebSocket.readyState === WebSocket.OPEN) {
collisionWebSocket.send('ping');
log('collisionLog', '发送心跳: ping', 'info');
} else {
log('collisionLog', 'WebSocket未连接', 'error');
}
}
function sendCollisionSubscribe() {
if (collisionWebSocket && collisionWebSocket.readyState === WebSocket.OPEN) {
const message = JSON.stringify({
type: 'subscribe',
topics: [
'position_update',
'collision_warning',
'rule_violation',
'vehicle_status_update'
],
timestamp: Date.now()
});
collisionWebSocket.send(message);
log('collisionLog', '发送订阅请求(包含车辆状态更新)', 'info');
} else {
log('collisionLog', 'WebSocket未连接', 'error');
}
}
function handleCollisionMessage(message) {
// 更新消息统计
updateMessageStats(message.type);
switch (message.type) {
case 'connection':
log('collisionLog', `连接确认: ${message.message}`, 'success');
break;
case 'pong':
log('collisionLog', '心跳响应', 'success');
break;
case 'position_update':
// 修复:使用正确的字段名 object_id 而不是 vehicleId
const objectId = message.payload?.object_id || '未知';
const objectType = message.payload?.object_type || '';
log('collisionLog', `位置更新: ${objectId} (${objectType})`, 'info');
break;
case 'collision_warning':
log('collisionLog', `碰撞预警: ${message.payload?.message || JSON.stringify(message.payload)}`, 'error');
break;
case 'rule_violation':
const vehicleLicense = message.payload?.vehicleLicense || '未知车辆';
const ruleName = message.payload?.ruleName || '未知规则';
const violationType = message.payload?.violationType || '未知类型';
const alertLevel = message.payload?.alertLevel || '未知级别';
const description = message.payload?.description || '无描述';
log('collisionLog', `规则违规: ${vehicleLicense} - ${ruleName} (${violationType}/${alertLevel}): ${description}`, 'warning');
break;
case 'rule_execution_status':
log('collisionLog', `规则执行状态: ${message.payload?.status || '未知状态'}`, 'info');
break;
case 'rule_state_change':
log('collisionLog', `规则状态变更: ${message.payload?.newState || '未知状态'}`, 'info');
break;
case 'vehicle_command':
log('collisionLog', `车辆指令: ${message.payload?.commandType || '未知指令'}`, 'info');
break;
case 'traffic_light_status':
log('collisionLog', `红绿灯状态: ${message.payload?.intersection_id || '未知路口'}`, 'info');
break;
case 'intersection_traffic_light_status':
const intersectionId = message.payload?.intersection_id || '未知路口';
const deviceId = message.payload?.device_id || '未知设备';
const nsStatus = message.payload?.ns_status || '未知';
const ewStatus = message.payload?.ew_status || '未知';
log('collisionLog', `🚦 路口红绿灯状态: ${intersectionId} - 设备${deviceId} - 南北:${nsStatus} 东西:${ewStatus}`, 'info');
break;
case 'aircraftRouteUpdate':
const flightNo = message.data?.flightNo || '未知航班';
const routeType = message.data?.routeType || '未知路由';
const routeStatus = message.data?.routeStatus || '未知状态';
const aircraftStatus = message.data?.aircraftStatus || '未知状态';
const routeCodes = message.data?.routeCodes || '无编码';
const eventSource = message.data?.eventSource || '未知来源';
const routeGeometry = message.data?.routeGeometry ? '已提供几何数据' : '无几何数据';
// 根据事件来源更新分类统计
if (eventSource === 'FLIGHT_NOTIFICATION') {
messageStats.eventDrivenRoute++;
}
// 根据事件来源设置不同的日志类型和图标
let logType = 'info';
let icon = '🛫';
if (eventSource === 'FLIGHT_NOTIFICATION') {
logType = 'success';
icon = '🚀'; // 事件驱动的路由更新用火箭图标
}
log('collisionLog', `${icon} 航空器路由更新: ${flightNo} | 路由:${routeType} 状态:${routeStatus} | 航空器:${aircraftStatus} | 编码:${routeCodes} | 来源:${eventSource} | ${routeGeometry}`, logType);
break;
case 'flight_notification':
case 'FLIGHT_NOTIFICATION':
const notificationFlightNo = message.payload?.flightNo || message.flightNo || '未知航班';
const notificationFlightType = message.payload?.flightType || message.flightType || '未知类型';
const notificationEventType = message.payload?.eventType || message.eventType || '未知事件';
const notificationRunway = message.payload?.runway || message.runway || '未知跑道';
const notificationSeat = message.payload?.seat || message.seat || '未知机位';
const notificationLevel = message.payload?.notificationLevel || message.notificationLevel || 'INFO';
const notificationDescription = message.payload?.eventDescription || message.eventDescription || '无描述';
// 根据通知级别设置日志类型
const notificationLogType = notificationLevel === 'IMPORTANT' ? 'warning' : 'info';
log('collisionLog', `✈️ 航班进出港通知: ${notificationFlightNo} - ${notificationEventType} (${notificationFlightType}) | 跑道:${notificationRunway} 机位:${notificationSeat} | ${notificationDescription}`, notificationLogType);
break;
case 'path_conflict_alert':
const object1 = message.payload?.object1Name || '未知对象1';
const object2 = message.payload?.object2Name || '未知对象2';
const conflictType = message.payload?.alertType || '未知类型';
const conflictLevel = message.payload?.alertLevel || '未知级别';
log('collisionLog', `路径冲突告警: ${object1} vs ${object2} (${conflictType}/${conflictLevel})`, 'error');
break;
case 'vehicle_status_update':
case 'VEHICLE_STATUS_UPDATE': // 可能是大写
handleVehicleStatusUpdate(message);
break;
default:
// 打印完整消息内容以便调试
log('collisionLog', `未知消息类型: ${message.type} | 完整消息: ${JSON.stringify(message)}`, 'info');
}
}
// 处理车辆状态更新消息
function handleVehicleStatusUpdate(message) {
const payload = message.payload;
if (!payload) {
log('collisionLog', '🚗 车辆状态更新: 无载荷数据', 'warning');
return;
}
const vehicleId = payload.vehicleId || '未知车辆';
const timestamp = payload.timestamp || Date.now();
const updateType = payload.updateType || '未知';
const dataSource = payload.dataSource || '未知';
// 解析状态数据
const statusData = payload.statusData;
let statusInfo = [];
if (statusData) {
// 车辆信息
if (statusData.vehicleInfo) {
statusInfo.push(`ID:${statusData.vehicleInfo.vehicleId || '未知'}`);
}
// 运行状态
if (statusData.operationalStatus) {
const ops = statusData.operationalStatus;
statusInfo.push(`电源:${ops.powerStatus || '未知'}`);
statusInfo.push(`模式:${ops.operationalMode || '未知'}`);
statusInfo.push(`健康:${ops.systemHealth || '未知'}`);
}
// 控制状态
if (statusData.controlStatus) {
const ctrl = statusData.controlStatus;
statusInfo.push(`控制:${ctrl.controlMode || '未知'}`);
statusInfo.push(`权限:${ctrl.controlAuthority || '未知'}`);
if (ctrl.remoteControlActive !== undefined) {
statusInfo.push(`远程:${ctrl.remoteControlActive ? '是' : '否'}`);
}
}
// 运动状态
if (statusData.motionStatus) {
const motion = statusData.motionStatus;
if (motion.position) {
statusInfo.push(`位置:(${motion.position.latitude?.toFixed(6)},${motion.position.longitude?.toFixed(6)})`);
}
if (motion.velocity) {
statusInfo.push(`速度:${motion.velocity.speed?.toFixed(2)}km/h`);
statusInfo.push(`方向:${motion.velocity.direction?.toFixed(1)}°`);
}
}
// 安全状态
if (statusData.safetyStatus) {
const safety = statusData.safetyStatus;
statusInfo.push(`避障:${safety.collisionAvoidanceActive ? '启用' : '禁用'}`);
statusInfo.push(`急停:${safety.emergencyBrakingReady ? '就绪' : '未就绪'}`);
}
// 电池状态
if (statusData.batteryStatus?.mainBattery) {
const battery = statusData.batteryStatus.mainBattery;
statusInfo.push(`电量:${battery.chargeLevel}%`);
statusInfo.push(`电压:${battery.voltage}V`);
statusInfo.push(`充电:${battery.chargingStatus || '未知'}`);
}
// 传感器状态
if (statusData.sensorStatus?.gps) {
const gps = statusData.sensorStatus.gps;
statusInfo.push(`GPS:${gps.status}(精度:${gps.accuracy}m)`);
}
// 通信状态
if (statusData.communicationStatus) {
const comm = statusData.communicationStatus;
statusInfo.push(`V2X:${comm.v2xStatus || '未知'}`);
statusInfo.push(`信号:${comm.cellularSignalStrength || '未知'}`);
}
}
// 格式化日志消息
const statusStr = statusInfo.length > 0 ? statusInfo.join(' | ') : '无状态数据';
const timeStr = new Date(timestamp).toLocaleTimeString();
log('collisionLog', `🚗 车辆状态更新: ${vehicleId} [${updateType}/${dataSource}] @ ${timeStr} - ${statusStr}`, 'success');
}
// =================== 控制面板 ===================
function clearLogs() {
document.getElementById('collisionLog').innerHTML = '';
log('collisionLog', '日志已清空', 'info');
}
function showConnectionStatus() {
const collisionStatus = collisionWebSocket ?
(collisionWebSocket.readyState === WebSocket.OPEN ? '已连接' : '未连接') : '未初始化';
log('collisionLog', `冲突检测WebSocket: ${collisionStatus}`, 'info');
}
function performStressTest() {
log('collisionLog', '开始压力测试...', 'info');
for (let i = 0; i < 10; i++) {
setTimeout(() => {
if (collisionWebSocket && collisionWebSocket.readyState === WebSocket.OPEN) {
collisionWebSocket.send(`ping_${i}`);
}
}, i * 100);
}
}
function resetMessageStats() {
// 重置所有统计数据
for (let key in messageStats) {
messageStats[key] = 0;
}
// 更新页面显示
document.getElementById('totalCount').textContent = '0';
document.getElementById('positionUpdateCount').textContent = '0';
document.getElementById('collisionWarningCount').textContent = '0';
document.getElementById('ruleViolationCount').textContent = '0';
document.getElementById('trafficLightStatusCount').textContent = '0';
document.getElementById('intersectionTrafficLightCount').textContent = '0';
document.getElementById('aircraftRouteCount').textContent = '0';
document.getElementById('eventDrivenRouteCount').textContent = '0';
document.getElementById('flightNotificationCount').textContent = '0';
document.getElementById('pathConflictCount').textContent = '0';
document.getElementById('vehicleCommandCount').textContent = '0';
document.getElementById('ruleExecutionCount').textContent = '0';
document.getElementById('ruleStateChangeCount').textContent = '0';
document.getElementById('vehicleStatusUpdateCount').textContent = '0';
document.getElementById('pongCount').textContent = '0';
document.getElementById('connectionCount').textContent = '0';
document.getElementById('unknownCount').textContent = '0';
log('collisionLog', '消息统计已重置', 'info');
}
// 页面加载完成
window.onload = function() {
// 初始化服务器地址
updateServerAddress();
log('collisionLog', '冲突检测WebSocket测试就绪', 'info');
};
// 页面关闭时清理连接
window.onbeforeunload = function() {
if (collisionWebSocket) collisionWebSocket.close();
};
</script>
</body>
</html>