drone1 / index.html
nkellil's picture
undefined - Follow Up Deployment
119a6c0 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ThermoDrone - Photovoltaic Plant Monitoring</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCGNXE_QanVIz3XUFL0EDgYn2ZQ1QQhG1U&libraries=visualization,geometry&callback=initMap" async defer></script>
<style>
#map {
height: 100vh;
width: 100%;
}
.thermal-gradient {
background: linear-gradient(to right, #000000, #4000ff, #00ffff, #00ff00, #ffff00, #ff0000);
height: 20px;
border-radius: 4px;
}
.telemetry-value {
font-family: 'Courier New', monospace;
font-weight: bold;
}
.sidebar {
transition: all 0.3s ease;
}
.sidebar.collapsed {
width: 60px;
}
.sidebar.collapsed .sidebar-content {
opacity: 0;
pointer-events: none;
}
.defect-marker {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: scale(1); opacity: 0.7; }
50% { transform: scale(1.2); opacity: 1; }
100% { transform: scale(1); opacity: 0.7; }
}
#image-modal {
transition: opacity 0.3s ease;
}
.modal-content {
max-height: 90vh;
}
.active {
background-color: #3b82f6;
color: white;
}
</style>
</head>
<body class="bg-gray-100 h-screen flex overflow-hidden">
<!-- Sidebar -->
<div id="sidebar" class="sidebar bg-gray-800 text-white w-64 flex flex-col">
<div class="p-4 flex items-center justify-between border-b border-gray-700">
<div class="flex items-center space-x-2">
<i class="fas fa-drone-alt text-blue-400 text-2xl"></i>
<h1 class="text-xl font-bold">ThermoDrone</h1>
</div>
<button id="toggle-sidebar" class="text-gray-400 hover:text-white">
<i class="fas fa-chevron-left"></i>
</button>
</div>
<div class="sidebar-content flex-1 overflow-y-auto p-4 space-y-6">
<!-- Connection Panel -->
<div class="bg-gray-700 rounded-lg p-4">
<h2 class="text-lg font-semibold mb-3 flex items-center">
<i class="fas fa-plug mr-2 text-blue-400"></i>
Connection
</h2>
<div class="space-y-3">
<div>
<label class="block text-sm font-medium mb-1">Serial Port</label>
<select id="com-port" class="w-full bg-gray-600 border border-gray-500 rounded px-3 py-2 text-sm">
<option value="">Select COM Port</option>
<!-- Ports will be populated by JavaScript -->
</select>
</div>
<div>
<label class="block text-sm font-medium mb-1">Baud Rate</label>
<select id="baud-rate" class="w-full bg-gray-600 border border-gray-500 rounded px-3 py-2 text-sm">
<option value="57600">57600</option>
<option value="115200" selected>115200</option>
<option value="921600">921600</option>
</select>
</div>
<button id="connect-btn" class="w-full bg-blue-600 hover:bg-blue-700 text-white py-2 rounded flex items-center justify-center">
<i class="fas fa-link mr-2"></i> Connect
</button>
<button id="disconnect-btn" class="w-full bg-gray-600 hover:bg-gray-700 text-white py-2 rounded flex items-center justify-center hidden">
<i class="fas fa-unlink mr-2"></i> Disconnect
</button>
</div>
</div>
<!-- Mission Planning -->
<div class="bg-gray-700 rounded-lg p-4">
<h2 class="text-lg font-semibold mb-3 flex items-center">
<i class="fas fa-map-marked-alt mr-2 text-green-400"></i>
Mission Planning
</h2>
<div class="space-y-3">
<div class="flex space-x-2">
<button id="add-waypoint-btn" class="flex-1 bg-green-600 hover:bg-green-700 text-white py-2 rounded flex items-center justify-center text-sm">
<i class="fas fa-plus mr-1"></i> Add Waypoint
</button>
<button id="clear-waypoints-btn" class="flex-1 bg-red-600 hover:bg-red-700 text-white py-2 rounded flex items-center justify-center text-sm">
<i class="fas fa-trash mr-1"></i> Clear All
</button>
</div>
<div>
<label class="block text-sm font-medium mb-1">Altitude (m)</label>
<input type="number" id="waypoint-altitude" value="50" min="10" max="200" class="w-full bg-gray-600 border border-gray-500 rounded px-3 py-2 text-sm">
</div>
<div>
<label class="block text-sm font-medium mb-1">Speed (m/s)</label>
<input type="number" id="waypoint-speed" value="5" min="1" max="15" class="w-full bg-gray-600 border border-gray-500 rounded px-3 py-2 text-sm">
</div>
<button id="upload-mission-btn" class="w-full bg-blue-600 hover:bg-blue-700 text-white py-2 rounded flex items-center justify-center">
<i class="fas fa-upload mr-2"></i> Upload Mission
</button>
<button id="start-mission-btn" class="w-full bg-green-600 hover:bg-green-700 text-white py-2 rounded flex items-center justify-center hidden">
<i class="fas fa-play mr-2"></i> Start Mission
</button>
<button id="pause-mission-btn" class="w-full bg-yellow-600 hover:bg-yellow-700 text-white py-2 rounded flex items-center justify-center hidden">
<i class="fas fa-pause mr-2"></i> Pause Mission
</button>
</div>
</div>
<!-- Thermal Analysis -->
<div class="bg-gray-700 rounded-lg p-4">
<h2 class="text-lg font-semibold mb-3 flex items-center">
<i class="fas fa-thermometer-half mr-2 text-red-400"></i>
Thermal Analysis
</h2>
<div class="space-y-3">
<div class="thermal-gradient rounded-full mb-2"></div>
<div class="flex justify-between text-xs">
<span>20°C</span>
<span>40°C</span>
<span>60°C</span>
<span>80°C</span>
<span>100°C</span>
</div>
<div>
<label class="block text-sm font-medium mb-1">Threshold (°C)</label>
<input type="number" id="thermal-threshold" value="70" min="30" max="120" class="w-full bg-gray-600 border border-gray-500 rounded px-3 py-2 text-sm">
</div>
<div class="flex items-center">
<input type="checkbox" id="enable-yolo" class="mr-2" checked>
<label for="enable-yolo" class="text-sm">Enable Defect Detection</label>
</div>
<button id="analyze-btn" class="w-full bg-purple-600 hover:bg-purple-700 text-white py-2 rounded flex items-center justify-center">
<i class="fas fa-search mr-2"></i> Analyze Images
</button>
</div>
</div>
</div>
<div class="p-4 border-t border-gray-700">
<div class="text-xs text-gray-400">
<p>MAVLink Connected: <span id="mavlink-status" class="text-red-500">No</span></p>
<p>Drone Status: <span id="drone-status" class="text-gray-300">Disconnected</span></p>
</div>
</div>
</div>
<!-- Main Content -->
<div class="flex-1 flex flex-col overflow-hidden">
<!-- Top Bar -->
<div class="bg-gray-800 text-white p-3 flex items-center justify-between">
<div class="flex items-center space-x-4">
<button id="mobile-menu-btn" class="md:hidden text-gray-300 hover:text-white">
<i class="fas fa-bars"></i>
</button>
<h2 class="text-lg font-semibold">Photovoltaic Plant Monitoring</h2>
</div>
<div class="flex items-center space-x-4">
<div class="flex items-center space-x-2">
<i class="fas fa-battery-three-quarters text-green-400"></i>
<span id="battery-level" class="text-sm">--%</span>
</div>
<div class="flex items-center space-x-2">
<i class="fas fa-satellite-dish text-blue-400"></i>
<span id="satellites" class="text-sm">--</span>
</div>
<div class="flex items-center space-x-2">
<i class="fas fa-map-marker-alt text-yellow-400"></i>
<span id="gps-coords" class="text-sm">--, --</span>
</div>
</div>
</div>
<!-- Map and Telemetry Area -->
<div class="flex-1 flex flex-col md:flex-row overflow-hidden">
<!-- Map Container -->
<div class="flex-1 relative">
<div id="map" class="absolute inset-0"></div>
<!-- Map Controls -->
<div class="absolute top-4 right-4 space-y-2 z-10">
<button id="satellite-btn" class="bg-white p-2 rounded-full shadow hover:bg-gray-100" title="Satellite View">
<i class="fas fa-satellite text-gray-800"></i>
</button>
<button id="terrain-btn" class="bg-white p-2 rounded-full shadow hover:bg-gray-100" title="Terrain View">
<i class="fas fa-mountain text-gray-800"></i>
</button>
<button id="thermal-overlay-btn" class="bg-white p-2 rounded-full shadow hover:bg-gray-100" title="Thermal Overlay">
<i class="fas fa-fire text-gray-800"></i>
</button>
</div>
<!-- Mission Progress -->
<div class="absolute bottom-4 left-4 bg-white bg-opacity-90 p-3 rounded shadow z-10">
<h3 class="font-semibold text-sm mb-1">Mission Progress</h3>
<div class="w-full bg-gray-200 rounded-full h-2.5">
<div id="mission-progress" class="bg-blue-600 h-2.5 rounded-full" style="width: 0%"></div>
</div>
<div class="flex justify-between text-xs mt-1">
<span>0%</span>
<span id="mission-percentage">0%</span>
</div>
</div>
</div>
<!-- Telemetry Panel -->
<div class="w-full md:w-64 bg-gray-700 text-white overflow-y-auto border-t md:border-t-0 md:border-l border-gray-600">
<div class="p-4 space-y-6">
<div>
<h3 class="font-semibold mb-2 flex items-center">
<i class="fas fa-heartbeat mr-2 text-red-400"></i>
Drone Telemetry
</h3>
<div class="space-y-2">
<div class="flex justify-between">
<span class="text-sm text-gray-300">Mode:</span>
<span id="flight-mode" class="telemetry-value">--</span>
</div>
<div class="flex justify-between">
<span class="text-sm text-gray-300">Altitude:</span>
<span id="altitude" class="telemetry-value">-- m</span>
</div>
<div class="flex justify-between">
<span class="text-sm text-gray-300">Speed:</span>
<span id="speed" class="telemetry-value">-- m/s</span>
</div>
<div class="flex justify-between">
<span class="text-sm text-gray-300">Heading:</span>
<span id="heading" class="telemetry-value">--°</span>
</div>
<div class="flex justify-between">
<span class="text-sm text-gray-300">Distance:</span>
<span id="distance" class="telemetry-value">-- m</span>
</div>
</div>
</div>
<div>
<h3 class="font-semibold mb-2 flex items-center">
<i class="fas fa-bolt mr-2 text-yellow-400"></i>
Power System
</h3>
<div class="space-y-2">
<div class="flex justify-between">
<span class="text-sm text-gray-300">Voltage:</span>
<span id="voltage" class="telemetry-value">-- V</span>
</div>
<div class="flex justify-between">
<span class="text-sm text-gray-300">Current:</span>
<span id="current" class="telemetry-value">-- A</span>
</div>
<div class="flex justify-between">
<span class="text-sm text-gray-300">Remaining:</span>
<span id="battery-remaining" class="telemetry-value">--%</span>
</div>
<div class="flex justify-between">
<span class="text-sm text-gray-300">Flight Time:</span>
<span id="flight-time" class="telemetry-value">--:--</span>
</div>
</div>
</div>
<div>
<h3 class="font-semibold mb-2 flex items-center">
<i class="fas fa-search mr-2 text-purple-400"></i>
Thermal Defects
</h3>
<div class="space-y-2">
<div class="flex justify-between">
<span class="text-sm text-gray-300">Detected:</span>
<span id="defects-count" class="telemetry-value">0</span>
</div>
<div class="flex justify-between">
<span class="text-sm text-gray-300">Max Temp:</span>
<span id="max-temp" class="telemetry-value">--°C</span>
</div>
<div class="flex justify-between">
<span class="text-sm text-gray-300">Avg Temp:</span>
<span id="avg-temp" class="telemetry-value">--°C</span>
</div>
</div>
</div>
<div class="bg-gray-600 rounded p-3">
<h3 class="font-semibold mb-2 text-sm">Quick Actions</h3>
<div class="grid grid-cols-2 gap-2">
<button id="rtl-btn" class="bg-yellow-600 hover:bg-yellow-700 text-white py-1 px-2 rounded text-xs">
<i class="fas fa-home mr-1"></i> RTL
</button>
<button id="loiter-btn" class="bg-blue-600 hover:bg-blue-700 text-white py-1 px-2 rounded text-xs">
<i class="fas fa-circle-notch mr-1"></i> Loiter
</button>
<button id="land-btn" class="bg-red-600 hover:bg-red-700 text-white py-1 px-2 rounded text-xs">
<i class="fas fa-arrow-down mr-1"></i> Land
</button>
<button id="takeoff-btn" class="bg-green-600 hover:bg-green-700 text-white py-1 px-2 rounded text-xs">
<i class="fas fa-arrow-up mr-1"></i> Takeoff
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Image Preview Modal -->
<div id="image-modal" class="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50 hidden">
<div class="bg-gray-800 rounded-lg max-w-4xl w-full modal-content overflow-hidden">
<div class="flex justify-between items-center p-4 border-b border-gray-700">
<h3 class="text-lg font-semibold">Thermal Image Analysis</h3>
<button id="close-modal" class="text-gray-400 hover:text-white">
<i class="fas fa-times"></i>
</button>
</div>
<div class="p-4 flex flex-col md:flex-row overflow-auto">
<div class="w-full md:w-2/3 mb-4 md:mb-0 md:pr-4">
<img id="modal-image" src="" alt="Thermal Image" class="w-full h-auto rounded max-h-[60vh] object-contain">
</div>
<div class="w-full md:w-1/3">
<h4 class="font-semibold mb-2">Defect Details</h4>
<div class="space-y-3">
<div>
<label class="block text-sm text-gray-300">Module ID:</label>
<p id="module-id" class="font-mono">--</p>
</div>
<div>
<label class="block text-sm text-gray-300">Temperature:</label>
<p id="module-temp" class="font-mono">--°C</p>
</div>
<div>
<label class="block text-sm text-gray-300">Location:</label>
<p id="module-location" class="font-mono">--</p>
</div>
<div>
<label class="block text-sm text-gray-300">Defect Type:</label>
<p id="defect-type" class="font-mono">--</p>
</div>
<div>
<label class="block text-sm text-gray-300">Confidence:</label>
<p id="defect-confidence" class="font-mono">--%</p>
</div>
</div>
<div class="mt-4">
<button id="export-report-btn" class="w-full bg-blue-600 hover:bg-blue-700 text-white py-2 rounded">
<i class="fas fa-file-export mr-2"></i> Export Report
</button>
</div>
</div>
</div>
</div>
</div>
<script>
// Initialize Google Maps
let map;
let droneMarker;
let waypoints = [];
let defectMarkers = [];
let flightPath = null;
let telemetrySocket = null;
function initMap() {
try {
// Default to a solar farm location (example: Topaz Solar Farm, California)
const solarFarm = { lat: 36.6625, lng: 2.71986 };
map = new google.maps.Map(document.getElementById('map'), {
center: solarFarm,
zoom: 17,
mapTypeId: 'satellite',
disableDefaultUI: true,
mapTypeControlOptions: {
mapTypeIds: ['roadmap', 'satellite', 'hybrid', 'terrain']
}
});
// Add drone marker
droneMarker = new google.maps.Marker({
position: solarFarm,
map: map,
icon: {
path: google.maps.SymbolPath.CIRCLE,
fillColor: '#ef4444',
fillOpacity: 1,
strokeColor: 'white',
strokeWeight: 2,
scale: 10
},
title: 'Drone',
zIndex: 999
});
// Add click listener for waypoints
map.addListener('click', (event) => {
const addWaypointBtn = document.getElementById('add-waypoint-btn');
if (addWaypointBtn.classList.contains('active')) {
addWaypoint(event.latLng);
}
});
console.log("Map initialized successfully");
} catch (error) {
console.error("Error loading map:", error);
alert("Unable to load map. Please check your internet connection and API key.");
}
}
function addWaypoint(location) {
const waypointNumber = waypoints.length + 1;
const altitude = document.getElementById('waypoint-altitude').value;
const marker = new google.maps.Marker({
position: location,
map: map,
icon: {
path: google.maps.SymbolPath.CIRCLE,
fillColor: '#3b82f6',
fillOpacity: 1,
strokeColor: 'white',
strokeWeight: 2,
scale: 8
},
label: {
text: waypointNumber.toString(),
color: 'white',
fontSize: '10px'
},
title: `Waypoint ${waypointNumber} (${altitude}m)`
});
// Store additional waypoint data
marker.altitude = altitude;
marker.speed = document.getElementById('waypoint-speed').value;
waypoints.push(marker);
// Draw flight path
drawFlightPath();
}
function drawFlightPath() {
// Clear previous path if exists
if (flightPath) {
flightPath.setMap(null);
}
if (waypoints.length > 1) {
flightPath = new google.maps.Polyline({
path: waypoints.map(wp => wp.getPosition()),
geodesic: true,
strokeColor: '#3b82f6',
strokeOpacity: 1.0,
strokeWeight: 3,
zIndex: 1
});
flightPath.setMap(map);
}
}
function clearWaypoints() {
waypoints.forEach(marker => marker.setMap(null));
waypoints = [];
if (flightPath) {
flightPath.setMap(null);
flightPath = null;
}
document.getElementById('mission-percentage').textContent = '0%';
document.getElementById('mission-progress').style.width = '0%';
}
function connectToTelemetry() {
const wsUrl = 'ws://localhost:8080/telemetry'; // Replace with your WebSocket endpoint
telemetrySocket = new WebSocket(wsUrl);
telemetrySocket.onopen = () => {
console.log('Connected to telemetry server');
document.getElementById('mavlink-status').textContent = 'Yes';
document.getElementById('mavlink-status').classList.remove('text-red-500');
document.getElementById('mavlink-status').classList.add('text-green-500');
document.getElementById('drone-status').textContent = 'Connected';
document.getElementById('drone-status').classList.remove('text-gray-300');
document.getElementById('drone-status').classList.add('text-green-400');
document.getElementById('start-mission-btn').classList.remove('hidden');
};
telemetrySocket.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
updateTelemetry(data);
} catch (error) {
console.error('Error parsing telemetry data:', error);
}
};
telemetrySocket.onerror = (error) => {
console.error('WebSocket error:', error);
disconnectFromTelemetry();
};
telemetrySocket.onclose = () => {
console.log('Disconnected from telemetry server');
disconnectFromTelemetry();
};
}
function disconnectFromTelemetry() {
if (telemetrySocket) {
telemetrySocket.close();
telemetrySocket = null;
}
document.getElementById('mavlink-status').textContent = 'No';
document.getElementById('mavlink-status').classList.remove('text-green-500');
document.getElementById('mavlink-status').classList.add('text-red-500');
document.getElementById('drone-status').textContent = 'Disconnected';
document.getElementById('drone-status').classList.remove('text-green-400');
document.getElementById('drone-status').classList.add('text-gray-300');
document.getElementById('start-mission-btn').classList.add('hidden');
document.getElementById('pause-mission-btn').classList.add('hidden');
// Reset telemetry values
document.getElementById('gps-coords').textContent = '--, --';
document.getElementById('altitude').textContent = '-- m';
document.getElementById('battery-level').textContent = '--%';
document.getElementById('satellites').textContent = '--';
document.getElementById('flight-mode').textContent = '--';
document.getElementById('speed').textContent = '-- m/s';
document.getElementById('heading').textContent = '--°';
document.getElementById('distance').textContent = '-- m';
document.getElementById('voltage').textContent = '-- V';
document.getElementById('current').textContent = '-- A';
document.getElementById('battery-remaining').textContent = '--%';
document.getElementById('flight-time').textContent = '--:--';
}
function updateTelemetry(data) {
// Update drone position
if (data.latitude && data.longitude) {
const dronePos = new google.maps.LatLng(data.latitude, data.longitude);
droneMarker.setPosition(dronePos);
// Center map on drone if it's moving
if (data.ground_speed > 1) {
map.panTo(dronePos);
}
}
// Update telemetry values
if (data.latitude && data.longitude) {
document.getElementById('gps-coords').textContent = `${data.latitude.toFixed(6)}, ${data.longitude.toFixed(6)}`;
}
if (data.altitude) {
document.getElementById('altitude').textContent = `${data.altitude.toFixed(1)} m`;
}
if (data.battery_remaining !== undefined) {
const batteryPercent = Math.round(data.battery_remaining * 100);
document.getElementById('battery-level').textContent = `${batteryPercent}%`;
document.getElementById('battery-remaining').textContent = `${batteryPercent}%`;
}
if (data.satellites_visible) {
document.getElementById('satellites').textContent = data.satellites_visible;
}
if (data.custom_mode) {
document.getElementById('flight-mode').textContent = getFlightModeName(data.custom_mode);
}
if (data.ground_speed) {
document.getElementById('speed').textContent = `${data.ground_speed.toFixed(1)} m/s`;
}
if (data.heading) {
document.getElementById('heading').textContent = `${Math.round(data.heading)}°`;
}
if (data.voltage_battery) {
document.getElementById('voltage').textContent = `${data.voltage_battery.toFixed(1)} V`;
}
if (data.current_battery) {
document.getElementById('current').textContent = `${data.current_battery.toFixed(1)} A`;
}
if (data.flight_time) {
const minutes = Math.floor(data.flight_time / 60);
const seconds = data.flight_time % 60;
document.getElementById('flight-time').textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
}
}
function getFlightModeName(modeNumber) {
// Map MAVLink flight modes to human-readable names
const flightModes = {
0: 'Stabilize',
1: 'Acro',
2: 'Alt Hold',
3: 'Auto',
4: 'Guided',
5: 'Loiter',
6: 'RTL',
7: 'Circle',
9: 'Land',
11: 'Drift',
13: 'Sport',
14: 'Flip',
15: 'Auto Tune',
16: 'Pos Hold',
17: 'Brake',
18: 'Throw',
19: 'Avoid ADSB',
20: 'Guided NoGPS',
21: 'Smart RTL'
};
return flightModes[modeNumber] || 'Unknown';
}
function showDefectDetails(location, temperature, defectType) {
document.getElementById('modal-image').src = ''; // Will be set by actual image data
document.getElementById('module-temp').textContent = `${temperature}°C`;
document.getElementById('defect-type').textContent = defectType;
document.getElementById('defect-confidence').textContent = '--%';
document.getElementById('module-id').textContent = '--';
document.getElementById('module-location').textContent = '--';
document.getElementById('image-modal').classList.remove('hidden');
}
// DOM Event Listeners
document.addEventListener('DOMContentLoaded', () => {
// Initialize map when window loads
window.initMap = initMap;
// Toggle sidebar
document.getElementById('toggle-sidebar').addEventListener('click', () => {
document.getElementById('sidebar').classList.toggle('collapsed');
const icon = document.querySelector('#toggle-sidebar i');
if (document.getElementById('sidebar').classList.contains('collapsed')) {
icon.classList.remove('fa-chevron-left');
icon.classList.add('fa-chevron-right');
} else {
icon.classList.remove('fa-chevron-right');
icon.classList.add('fa-chevron-left');
}
});
// Mobile menu toggle
document.getElementById('mobile-menu-btn').addEventListener('click', () => {
document.getElementById('sidebar').classList.toggle('hidden');
});
// Connect button
document.getElementById('connect-btn').addEventListener('click', () => {
const comPort = document.getElementById('com-port').value;
const baudRate = document.getElementById('baud-rate').value;
if (!comPort) {
alert('Please select a COM port');
return;
}
// Connect to telemetry
connectToTelemetry();
document.getElementById('connect-btn').classList.add('hidden');
document.getElementById('disconnect-btn').classList.remove('hidden');
});
// Disconnect button
document.getElementById('disconnect-btn').addEventListener('click', disconnectFromTelemetry);
// Add waypoint button
document.getElementById('add-waypoint-btn').addEventListener('click', function() {
this.classList.toggle('active');
if (this.classList.contains('active')) {
this.innerHTML = '<i class="fas fa-times mr-1"></i> Cancel';
document.getElementById('map').style.cursor = 'crosshair';
} else {
this.innerHTML = '<i class="fas fa-plus mr-1"></i> Add Waypoint';
document.getElementById('map').style.cursor = '';
}
});
// Clear waypoints button
document.getElementById('clear-waypoints-btn').addEventListener('click', clearWaypoints);
// Start mission button
document.getElementById('start-mission-btn').addEventListener('click', () => {
if (waypoints.length === 0) {
alert('Please add waypoints first');
return;
}
document.getElementById('start-mission-btn').classList.add('hidden');
document.getElementById('pause-mission-btn').classList.remove('hidden');
// Send mission to drone via WebSocket
if (telemetrySocket && telemetrySocket.readyState === WebSocket.OPEN) {
const mission = {
type: 'mission',
waypoints: waypoints.map(wp => ({
lat: wp.getPosition().lat(),
lng: wp.getPosition().lng(),
alt: parseFloat(wp.altitude),
speed: parseFloat(wp.speed)
}))
};
telemetrySocket.send(JSON.stringify(mission));
}
});
// Pause mission button
document.getElementById('pause-mission-btn').addEventListener('click', () => {
document.getElementById('start-mission-btn').classList.remove('hidden');
document.getElementById('pause-mission-btn').classList.add('hidden');
// Send pause command to drone
if (telemetrySocket && telemetrySocket.readyState === WebSocket.OPEN) {
telemetrySocket.send(JSON.stringify({ type: 'pause' }));
}
});
// Upload mission button
document.getElementById('upload-mission-btn').addEventListener('click', () => {
if (waypoints.length === 0) {
alert('Please add waypoints first');
return;
}
// Send mission to drone
if (telemetrySocket && telemetrySocket.readyState === WebSocket.OPEN) {
const mission = {
type: 'mission',
waypoints: waypoints.map(wp => ({
lat: wp.getPosition().lat(),
lng: wp.getPosition().lng(),
alt: parseFloat(wp.altitude),
speed: parseFloat(wp.speed)
}))
};
telemetrySocket.send(JSON.stringify(mission));
alert(`Mission with ${waypoints.length} waypoints uploaded to drone`);
} else {
alert('Not connected to drone');
}
});
// Analyze images button
document.getElementById('analyze-btn').addEventListener('click', () => {
const threshold = document.getElementById('thermal-threshold').value;
const useYolo = document.getElementById('enable-yolo').checked;
// Send analysis request to server
if (telemetrySocket && telemetrySocket.readyState === WebSocket.OPEN) {
telemetrySocket.send(JSON.stringify({
type: 'analyze',
threshold: parseFloat(threshold),
useYolo: useYolo
}));
} else {
alert('Not connected to drone');
}
});
// Map type buttons
document.getElementById('satellite-btn').addEventListener('click', () => {
map.setMapTypeId('satellite');
});
document.getElementById('terrain-btn').addEventListener('click', () => {
map.setMapTypeId('terrain');
});
document.getElementById('thermal-overlay-btn').addEventListener('click', () => {
// This would toggle a thermal overlay if implemented
alert('Thermal overlay would be displayed here if implemented');
});
// Quick action buttons
document.getElementById('rtl-btn').addEventListener('click', () => {
if (telemetrySocket && telemetrySocket.readyState === WebSocket.OPEN) {
telemetrySocket.send(JSON.stringify({ type: 'command', command: 'RTL' }));
} else {
alert('Not connected to drone');
}
});
document.getElementById('loiter-btn').addEventListener('click', () => {
if (telemetrySocket && telemetrySocket.readyState === WebSocket.OPEN) {
telemetrySocket.send(JSON.stringify({ type: 'command', command: 'LOITER' }));
} else {
alert('Not connected to drone');
}
});
document.getElementById('land-btn').addEventListener('click', () => {
if (telemetrySocket && telemetrySocket.readyState === WebSocket.OPEN) {
telemetrySocket.send(JSON.stringify({ type: 'command', command: 'LAND' }));
} else {
alert('Not connected to drone');
}
});
document.getElementById('takeoff-btn').addEventListener
('click', () => {
if (telemetrySocket && telemetrySocket.readyState === WebSocket.OPEN) {
telemetrySocket.send(JSON.stringify({ type: 'command', command: 'TAKEOFF' }));
} else {
alert('Not connected to drone');
}
});