You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
470 lines
15 KiB
470 lines
15 KiB
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>🛤️ 无人机轨迹模拟测试</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: 'Microsoft YaHei', sans-serif;
|
|
background: linear-gradient(135deg, #1e3c72, #2a5298);
|
|
color: white;
|
|
min-height: 100vh;
|
|
padding: 20px;
|
|
}
|
|
|
|
.container {
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
background: rgba(255, 255, 255, 0.1);
|
|
border-radius: 20px;
|
|
padding: 30px;
|
|
backdrop-filter: blur(10px);
|
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
|
|
}
|
|
|
|
.header {
|
|
text-align: center;
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
.header h1 {
|
|
font-size: 2.5em;
|
|
margin-bottom: 10px;
|
|
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
|
|
}
|
|
|
|
.header p {
|
|
font-size: 1.1em;
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.control-panel {
|
|
background: rgba(255, 255, 255, 0.1);
|
|
border-radius: 15px;
|
|
padding: 25px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.control-group {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.control-group h3 {
|
|
color: #00aaff;
|
|
margin-bottom: 15px;
|
|
font-size: 1.2em;
|
|
border-bottom: 2px solid rgba(0, 170, 255, 0.3);
|
|
padding-bottom: 8px;
|
|
}
|
|
|
|
.button-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
|
gap: 15px;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.btn {
|
|
padding: 12px 20px;
|
|
border: none;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
font-weight: bold;
|
|
color: white;
|
|
transition: all 0.3s ease;
|
|
text-align: center;
|
|
}
|
|
|
|
.btn:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
|
|
}
|
|
|
|
.btn-circle {
|
|
background: linear-gradient(135deg, #9C27B0, #7B1FA2);
|
|
}
|
|
|
|
.btn-line {
|
|
background: linear-gradient(135deg, #FF9800, #F57C00);
|
|
}
|
|
|
|
.btn-random {
|
|
background: linear-gradient(135deg, #607D8B, #455A64);
|
|
}
|
|
|
|
.btn-stop {
|
|
background: linear-gradient(135deg, #F44336, #D32F2F);
|
|
}
|
|
|
|
.btn-clear {
|
|
background: linear-gradient(135deg, #FF5722, #D32F2F);
|
|
}
|
|
|
|
.input-group {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.input-group label {
|
|
min-width: 100px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.input-group input,
|
|
.input-group select {
|
|
flex: 1;
|
|
padding: 8px 12px;
|
|
border: none;
|
|
border-radius: 6px;
|
|
background: rgba(255, 255, 255, 0.2);
|
|
color: white;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.input-group input::placeholder {
|
|
color: rgba(255, 255, 255, 0.6);
|
|
}
|
|
|
|
.status-panel {
|
|
background: rgba(0, 0, 0, 0.3);
|
|
border-radius: 10px;
|
|
padding: 20px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.status-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 8px 0;
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
}
|
|
|
|
.status-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.status-value {
|
|
font-weight: bold;
|
|
color: #4CAF50;
|
|
}
|
|
|
|
.alert {
|
|
padding: 12px 15px;
|
|
border-radius: 8px;
|
|
margin-bottom: 15px;
|
|
display: none;
|
|
}
|
|
|
|
.alert.success {
|
|
background: rgba(76, 175, 80, 0.2);
|
|
border-left: 4px solid #4CAF50;
|
|
}
|
|
|
|
.alert.error {
|
|
background: rgba(244, 67, 54, 0.2);
|
|
border-left: 4px solid #F44336;
|
|
}
|
|
|
|
.alert.info {
|
|
background: rgba(33, 150, 243, 0.2);
|
|
border-left: 4px solid #2196F3;
|
|
}
|
|
|
|
.drone-list {
|
|
max-height: 200px;
|
|
overflow-y: auto;
|
|
background: rgba(0, 0, 0, 0.2);
|
|
border-radius: 8px;
|
|
padding: 10px;
|
|
}
|
|
|
|
.drone-item {
|
|
padding: 8px 10px;
|
|
margin-bottom: 5px;
|
|
background: rgba(255, 255, 255, 0.1);
|
|
border-radius: 6px;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.link-section {
|
|
text-align: center;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.link-section a {
|
|
color: #00aaff;
|
|
text-decoration: none;
|
|
font-weight: bold;
|
|
padding: 10px 20px;
|
|
background: rgba(0, 170, 255, 0.2);
|
|
border-radius: 8px;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.link-section a:hover {
|
|
background: rgba(0, 170, 255, 0.4);
|
|
transform: translateY(-2px);
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div class="container">
|
|
<div class="header">
|
|
<h1>🛤️ 无人机轨迹模拟测试</h1>
|
|
<p>测试无人机轨迹记录和可视化功能</p>
|
|
</div>
|
|
|
|
<div id="alertContainer"></div>
|
|
|
|
<!-- 快速启动面板 -->
|
|
<div class="control-panel">
|
|
<div class="control-group">
|
|
<h3>🚀 快速启动模拟</h3>
|
|
<div class="button-grid">
|
|
<button class="btn btn-circle" onclick="startQuickSimulation('circle')">
|
|
🔄 圆形轨迹
|
|
</button>
|
|
<button class="btn btn-line" onclick="startQuickSimulation('line')">
|
|
↔️ 直线轨迹
|
|
</button>
|
|
<button class="btn btn-random" onclick="startQuickSimulation('random')">
|
|
🎲 随机轨迹
|
|
</button>
|
|
<button class="btn btn-stop" onclick="stopSimulation()">
|
|
⏹️ 停止模拟
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 高级配置面板 -->
|
|
<div class="control-panel">
|
|
<div class="control-group">
|
|
<h3>⚙️ 高级配置</h3>
|
|
<div class="input-group">
|
|
<label>轨迹类型:</label>
|
|
<select id="simulationType">
|
|
<option value="circle">圆形轨迹</option>
|
|
<option value="line">直线往返</option>
|
|
<option value="random">随机游走</option>
|
|
</select>
|
|
</div>
|
|
<div class="input-group">
|
|
<label>无人机数量:</label>
|
|
<input type="number" id="droneCount" value="2" min="1" max="5" placeholder="1-5架">
|
|
</div>
|
|
<div class="input-group">
|
|
<label>更新间隔:</label>
|
|
<input type="number" id="updateSpeed" value="3" min="1" max="10" placeholder="秒">
|
|
</div>
|
|
<div class="button-grid">
|
|
<button class="btn btn-circle" onclick="startCustomSimulation()">
|
|
🛠️ 启动自定义模拟
|
|
</button>
|
|
<button class="btn btn-clear" onclick="clearAllTrajectories()">
|
|
🗑️ 清除所有轨迹
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 状态显示面板 -->
|
|
<div class="status-panel">
|
|
<h3 style="color: #00aaff; margin-bottom: 15px;">📊 模拟状态</h3>
|
|
<div class="status-item">
|
|
<span>模拟状态:</span>
|
|
<span class="status-value" id="simulationStatus">未启动</span>
|
|
</div>
|
|
<div class="status-item">
|
|
<span>轨迹类型:</span>
|
|
<span class="status-value" id="simulationType">无</span>
|
|
</div>
|
|
<div class="status-item">
|
|
<span>无人机数量:</span>
|
|
<span class="status-value" id="droneCountStatus">0</span>
|
|
</div>
|
|
<div class="status-item">
|
|
<span>轨迹记录:</span>
|
|
<span class="status-value" id="trajectoryRecordingStatus">开启</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 无人机列表 -->
|
|
<div class="control-panel">
|
|
<div class="control-group">
|
|
<h3>🚁 模拟无人机列表</h3>
|
|
<div class="drone-list" id="droneList">
|
|
<div style="text-align: center; color: #999;">暂无模拟无人机</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 链接到主系统 -->
|
|
<div class="link-section">
|
|
<a href="/" target="_blank">🎯 打开主系统查看轨迹</a>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// 显示提示信息
|
|
function showAlert(message, type = 'info') {
|
|
const alertContainer = document.getElementById('alertContainer');
|
|
const alert = document.createElement('div');
|
|
alert.className = `alert ${type}`;
|
|
alert.textContent = message;
|
|
|
|
alertContainer.appendChild(alert);
|
|
alert.style.display = 'block';
|
|
|
|
setTimeout(() => {
|
|
alert.style.display = 'none';
|
|
alertContainer.removeChild(alert);
|
|
}, 5000);
|
|
}
|
|
|
|
// 快速启动模拟
|
|
async function startQuickSimulation(type) {
|
|
await startSimulation(type, 2, 3);
|
|
}
|
|
|
|
// 自定义启动模拟
|
|
async function startCustomSimulation() {
|
|
const type = document.getElementById('simulationType').value;
|
|
const droneCount = parseInt(document.getElementById('droneCount').value) || 2;
|
|
const speed = parseInt(document.getElementById('updateSpeed').value) || 3;
|
|
|
|
await startSimulation(type, droneCount, speed);
|
|
}
|
|
|
|
// 启动轨迹模拟
|
|
async function startSimulation(type, droneCount = 2, speed = 3) {
|
|
try {
|
|
showAlert(`🚀 正在启动${droneCount}架无人机的${type}轨迹模拟...`, 'info');
|
|
|
|
const response = await fetch('/api/test/start_simulation', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
type: type,
|
|
drone_count: droneCount,
|
|
speed: speed
|
|
})
|
|
});
|
|
|
|
const result = await response.json();
|
|
if (result.status === 'success') {
|
|
showAlert(`✅ ${result.message}`, 'success');
|
|
updateStatus();
|
|
} else {
|
|
showAlert(`❌ 启动模拟失败: ${result.message}`, 'error');
|
|
}
|
|
} catch (error) {
|
|
showAlert(`❌ 启动模拟失败: ${error.message}`, 'error');
|
|
}
|
|
}
|
|
|
|
// 停止轨迹模拟
|
|
async function stopSimulation() {
|
|
try {
|
|
showAlert('🛑 正在停止轨迹模拟...', 'info');
|
|
|
|
const response = await fetch('/api/test/stop_simulation', {
|
|
method: 'POST'
|
|
});
|
|
|
|
const result = await response.json();
|
|
if (result.status === 'success') {
|
|
showAlert('✅ 轨迹模拟已停止', 'success');
|
|
updateStatus();
|
|
} else {
|
|
showAlert(`❌ 停止模拟失败: ${result.message}`, 'error');
|
|
}
|
|
} catch (error) {
|
|
showAlert(`❌ 停止模拟失败: ${error.message}`, 'error');
|
|
}
|
|
}
|
|
|
|
// 清除所有轨迹
|
|
async function clearAllTrajectories() {
|
|
try {
|
|
// 这个功能需要在主页面中执行
|
|
showAlert('🗑️ 请在主系统页面中点击"清除"按钮来清除轨迹', 'info');
|
|
} catch (error) {
|
|
showAlert(`❌ 清除轨迹失败: ${error.message}`, 'error');
|
|
}
|
|
}
|
|
|
|
// 更新状态显示
|
|
async function updateStatus() {
|
|
try {
|
|
const response = await fetch('/api/test/simulation_status');
|
|
const result = await response.json();
|
|
|
|
if (result.status === 'success') {
|
|
// 更新状态显示
|
|
document.getElementById('simulationStatus').textContent =
|
|
result.simulation_active ? '运行中' : '未启动';
|
|
document.getElementById('simulationStatus').style.color =
|
|
result.simulation_active ? '#4CAF50' : '#F44336';
|
|
|
|
document.getElementById('simulationType').textContent =
|
|
result.simulation_type || '无';
|
|
document.getElementById('droneCountStatus').textContent =
|
|
result.simulated_drones.length || '0';
|
|
|
|
// 更新无人机列表
|
|
updateDroneList(result.simulated_drones);
|
|
}
|
|
} catch (error) {
|
|
console.error('获取状态失败:', error);
|
|
}
|
|
}
|
|
|
|
// 更新无人机列表
|
|
function updateDroneList(drones) {
|
|
const droneList = document.getElementById('droneList');
|
|
|
|
if (!drones || drones.length === 0) {
|
|
droneList.innerHTML = '<div style="text-align: center; color: #999;">暂无模拟无人机</div>';
|
|
return;
|
|
}
|
|
|
|
const html = drones.map((drone, index) => `
|
|
<div class="drone-item">
|
|
🚁 ${drone.name || `无人机-${index + 1}`}
|
|
<br>
|
|
<span style="color: #999;">
|
|
ID: ${drone.device_id.substring(0, 12)}...
|
|
</span>
|
|
</div>
|
|
`).join('');
|
|
|
|
droneList.innerHTML = html;
|
|
}
|
|
|
|
// 页面加载完成后的初始化
|
|
window.addEventListener('load', function () {
|
|
updateStatus();
|
|
|
|
// 定期更新状态
|
|
setInterval(updateStatus, 3000);
|
|
|
|
showAlert('🎯 轨迹模拟测试页面已加载', 'success');
|
|
});
|
|
</script>
|
|
</body>
|
|
|
|
</html> |