undefined - Initial Deployment
Browse files- README.md +7 -5
- index.html +894 -19
README.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
| 1 |
---
|
| 2 |
+
title: drone1
|
| 3 |
+
emoji: 🐳
|
| 4 |
+
colorFrom: red
|
| 5 |
+
colorTo: blue
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
| 8 |
+
tags:
|
| 9 |
+
- deepsite
|
| 10 |
---
|
| 11 |
|
| 12 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
index.html
CHANGED
|
@@ -1,19 +1,894 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>ThermoDrone - Photovoltaic Plant Monitoring</title>
|
| 7 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 9 |
+
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCGNXE_QanVIz3XUFL0EDgYn2ZQ1QQhG1U&libraries=visualization,geometry&callback=initMap" async defer></script>
|
| 10 |
+
<style>
|
| 11 |
+
#map {
|
| 12 |
+
height: 100vh;
|
| 13 |
+
width: 100%;
|
| 14 |
+
}
|
| 15 |
+
.thermal-gradient {
|
| 16 |
+
background: linear-gradient(to right, #000000, #4000ff, #00ffff, #00ff00, #ffff00, #ff0000);
|
| 17 |
+
height: 20px;
|
| 18 |
+
border-radius: 4px;
|
| 19 |
+
}
|
| 20 |
+
.telemetry-value {
|
| 21 |
+
font-family: 'Courier New', monospace;
|
| 22 |
+
font-weight: bold;
|
| 23 |
+
}
|
| 24 |
+
.sidebar {
|
| 25 |
+
transition: all 0.3s ease;
|
| 26 |
+
}
|
| 27 |
+
.sidebar.collapsed {
|
| 28 |
+
width: 60px;
|
| 29 |
+
}
|
| 30 |
+
.sidebar.collapsed .sidebar-content {
|
| 31 |
+
opacity: 0;
|
| 32 |
+
pointer-events: none;
|
| 33 |
+
}
|
| 34 |
+
.defect-marker {
|
| 35 |
+
animation: pulse 2s infinite;
|
| 36 |
+
}
|
| 37 |
+
@keyframes pulse {
|
| 38 |
+
0% { transform: scale(1); opacity: 0.7; }
|
| 39 |
+
50% { transform: scale(1.2); opacity: 1; }
|
| 40 |
+
100% { transform: scale(1); opacity: 0.7; }
|
| 41 |
+
}
|
| 42 |
+
#image-modal {
|
| 43 |
+
transition: opacity 0.3s ease;
|
| 44 |
+
}
|
| 45 |
+
.modal-content {
|
| 46 |
+
max-height: 90vh;
|
| 47 |
+
}
|
| 48 |
+
.active {
|
| 49 |
+
background-color: #3b82f6;
|
| 50 |
+
color: white;
|
| 51 |
+
}
|
| 52 |
+
</style>
|
| 53 |
+
</head>
|
| 54 |
+
<body class="bg-gray-100 h-screen flex overflow-hidden">
|
| 55 |
+
<!-- Sidebar -->
|
| 56 |
+
<div id="sidebar" class="sidebar bg-gray-800 text-white w-64 flex flex-col">
|
| 57 |
+
<div class="p-4 flex items-center justify-between border-b border-gray-700">
|
| 58 |
+
<div class="flex items-center space-x-2">
|
| 59 |
+
<i class="fas fa-drone-alt text-blue-400 text-2xl"></i>
|
| 60 |
+
<h1 class="text-xl font-bold">ThermoDrone</h1>
|
| 61 |
+
</div>
|
| 62 |
+
<button id="toggle-sidebar" class="text-gray-400 hover:text-white">
|
| 63 |
+
<i class="fas fa-chevron-left"></i>
|
| 64 |
+
</button>
|
| 65 |
+
</div>
|
| 66 |
+
|
| 67 |
+
<div class="sidebar-content flex-1 overflow-y-auto p-4 space-y-6">
|
| 68 |
+
<!-- Connection Panel -->
|
| 69 |
+
<div class="bg-gray-700 rounded-lg p-4">
|
| 70 |
+
<h2 class="text-lg font-semibold mb-3 flex items-center">
|
| 71 |
+
<i class="fas fa-plug mr-2 text-blue-400"></i>
|
| 72 |
+
Connection
|
| 73 |
+
</h2>
|
| 74 |
+
<div class="space-y-3">
|
| 75 |
+
<div>
|
| 76 |
+
<label class="block text-sm font-medium mb-1">Serial Port</label>
|
| 77 |
+
<select id="com-port" class="w-full bg-gray-600 border border-gray-500 rounded px-3 py-2 text-sm">
|
| 78 |
+
<option value="">Select COM Port</option>
|
| 79 |
+
<option value="COM3">COM3 (USB Serial Device)</option>
|
| 80 |
+
<option value="COM8">COM8 (FTDI Serial Port)</option>
|
| 81 |
+
</select>
|
| 82 |
+
</div>
|
| 83 |
+
<div>
|
| 84 |
+
<label class="block text-sm font-medium mb-1">Baud Rate</label>
|
| 85 |
+
<select id="baud-rate" class="w-full bg-gray-600 border border-gray-500 rounded px-3 py-2 text-sm">
|
| 86 |
+
<option value="57600">57600</option>
|
| 87 |
+
<option value="115200" selected>115200</option>
|
| 88 |
+
<option value="921600">921600</option>
|
| 89 |
+
</select>
|
| 90 |
+
</div>
|
| 91 |
+
<button id="connect-btn" class="w-full bg-blue-600 hover:bg-blue-700 text-white py-2 rounded flex items-center justify-center">
|
| 92 |
+
<i class="fas fa-link mr-2"></i> Connect
|
| 93 |
+
</button>
|
| 94 |
+
<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">
|
| 95 |
+
<i class="fas fa-unlink mr-2"></i> Disconnect
|
| 96 |
+
</button>
|
| 97 |
+
</div>
|
| 98 |
+
</div>
|
| 99 |
+
|
| 100 |
+
<!-- Mission Planning -->
|
| 101 |
+
<div class="bg-gray-700 rounded-lg p-4">
|
| 102 |
+
<h2 class="text-lg font-semibold mb-3 flex items-center">
|
| 103 |
+
<i class="fas fa-map-marked-alt mr-2 text-green-400"></i>
|
| 104 |
+
Mission Planning
|
| 105 |
+
</h2>
|
| 106 |
+
<div class="space-y-3">
|
| 107 |
+
<div class="flex space-x-2">
|
| 108 |
+
<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">
|
| 109 |
+
<i class="fas fa-plus mr-1"></i> Add Waypoint
|
| 110 |
+
</button>
|
| 111 |
+
<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">
|
| 112 |
+
<i class="fas fa-trash mr-1"></i> Clear All
|
| 113 |
+
</button>
|
| 114 |
+
</div>
|
| 115 |
+
<div>
|
| 116 |
+
<label class="block text-sm font-medium mb-1">Altitude (m)</label>
|
| 117 |
+
<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">
|
| 118 |
+
</div>
|
| 119 |
+
<div>
|
| 120 |
+
<label class="block text-sm font-medium mb-1">Speed (m/s)</label>
|
| 121 |
+
<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">
|
| 122 |
+
</div>
|
| 123 |
+
<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">
|
| 124 |
+
<i class="fas fa-upload mr-2"></i> Upload Mission
|
| 125 |
+
</button>
|
| 126 |
+
<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">
|
| 127 |
+
<i class="fas fa-play mr-2"></i> Start Mission
|
| 128 |
+
</button>
|
| 129 |
+
<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">
|
| 130 |
+
<i class="fas fa-pause mr-2"></i> Pause Mission
|
| 131 |
+
</button>
|
| 132 |
+
</div>
|
| 133 |
+
</div>
|
| 134 |
+
|
| 135 |
+
<!-- Thermal Analysis -->
|
| 136 |
+
<div class="bg-gray-700 rounded-lg p-4">
|
| 137 |
+
<h2 class="text-lg font-semibold mb-3 flex items-center">
|
| 138 |
+
<i class="fas fa-thermometer-half mr-2 text-red-400"></i>
|
| 139 |
+
Thermal Analysis
|
| 140 |
+
</h2>
|
| 141 |
+
<div class="space-y-3">
|
| 142 |
+
<div class="thermal-gradient rounded-full mb-2"></div>
|
| 143 |
+
<div class="flex justify-between text-xs">
|
| 144 |
+
<span>20°C</span>
|
| 145 |
+
<span>40°C</span>
|
| 146 |
+
<span>60°C</span>
|
| 147 |
+
<span>80°C</span>
|
| 148 |
+
<span>100°C</span>
|
| 149 |
+
</div>
|
| 150 |
+
<div>
|
| 151 |
+
<label class="block text-sm font-medium mb-1">Threshold (°C)</label>
|
| 152 |
+
<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">
|
| 153 |
+
</div>
|
| 154 |
+
<div class="flex items-center">
|
| 155 |
+
<input type="checkbox" id="enable-yolo" class="mr-2" checked>
|
| 156 |
+
<label for="enable-yolo" class="text-sm">Enable Defect Detection</label>
|
| 157 |
+
</div>
|
| 158 |
+
<button id="analyze-btn" class="w-full bg-purple-600 hover:bg-purple-700 text-white py-2 rounded flex items-center justify-center">
|
| 159 |
+
<i class="fas fa-search mr-2"></i> Analyze Images
|
| 160 |
+
</button>
|
| 161 |
+
</div>
|
| 162 |
+
</div>
|
| 163 |
+
</div>
|
| 164 |
+
|
| 165 |
+
<div class="p-4 border-t border-gray-700">
|
| 166 |
+
<div class="text-xs text-gray-400">
|
| 167 |
+
<p>MAVLink Connected: <span id="mavlink-status" class="text-red-500">No</span></p>
|
| 168 |
+
<p>Drone Status: <span id="drone-status" class="text-gray-300">Disconnected</span></p>
|
| 169 |
+
</div>
|
| 170 |
+
</div>
|
| 171 |
+
</div>
|
| 172 |
+
|
| 173 |
+
<!-- Main Content -->
|
| 174 |
+
<div class="flex-1 flex flex-col overflow-hidden">
|
| 175 |
+
<!-- Top Bar -->
|
| 176 |
+
<div class="bg-gray-800 text-white p-3 flex items-center justify-between">
|
| 177 |
+
<div class="flex items-center space-x-4">
|
| 178 |
+
<button id="mobile-menu-btn" class="md:hidden text-gray-300 hover:text-white">
|
| 179 |
+
<i class="fas fa-bars"></i>
|
| 180 |
+
</button>
|
| 181 |
+
<h2 class="text-lg font-semibold">Photovoltaic Plant Monitoring</h2>
|
| 182 |
+
</div>
|
| 183 |
+
<div class="flex items-center space-x-4">
|
| 184 |
+
<div class="flex items-center space-x-2">
|
| 185 |
+
<i class="fas fa-battery-three-quarters text-green-400"></i>
|
| 186 |
+
<span id="battery-level" class="text-sm">--%</span>
|
| 187 |
+
</div>
|
| 188 |
+
<div class="flex items-center space-x-2">
|
| 189 |
+
<i class="fas fa-satellite-dish text-blue-400"></i>
|
| 190 |
+
<span id="satellites" class="text-sm">--</span>
|
| 191 |
+
</div>
|
| 192 |
+
<div class="flex items-center space-x-2">
|
| 193 |
+
<i class="fas fa-map-marker-alt text-yellow-400"></i>
|
| 194 |
+
<span id="gps-coords" class="text-sm">--, --</span>
|
| 195 |
+
</div>
|
| 196 |
+
</div>
|
| 197 |
+
</div>
|
| 198 |
+
|
| 199 |
+
<!-- Map and Telemetry Area -->
|
| 200 |
+
<div class="flex-1 flex flex-col md:flex-row overflow-hidden">
|
| 201 |
+
<!-- Map Container -->
|
| 202 |
+
<div class="flex-1 relative">
|
| 203 |
+
<div id="map" class="absolute inset-0"></div>
|
| 204 |
+
|
| 205 |
+
<!-- Map Controls -->
|
| 206 |
+
<div class="absolute top-4 right-4 space-y-2 z-10">
|
| 207 |
+
<button id="satellite-btn" class="bg-white p-2 rounded-full shadow hover:bg-gray-100" title="Satellite View">
|
| 208 |
+
<i class="fas fa-satellite text-gray-800"></i>
|
| 209 |
+
</button>
|
| 210 |
+
<button id="terrain-btn" class="bg-white p-2 rounded-full shadow hover:bg-gray-100" title="Terrain View">
|
| 211 |
+
<i class="fas fa-mountain text-gray-800"></i>
|
| 212 |
+
</button>
|
| 213 |
+
<button id="thermal-overlay-btn" class="bg-white p-2 rounded-full shadow hover:bg-gray-100" title="Thermal Overlay">
|
| 214 |
+
<i class="fas fa-fire text-gray-800"></i>
|
| 215 |
+
</button>
|
| 216 |
+
</div>
|
| 217 |
+
|
| 218 |
+
<!-- Mission Progress -->
|
| 219 |
+
<div class="absolute bottom-4 left-4 bg-white bg-opacity-90 p-3 rounded shadow z-10">
|
| 220 |
+
<h3 class="font-semibold text-sm mb-1">Mission Progress</h3>
|
| 221 |
+
<div class="w-full bg-gray-200 rounded-full h-2.5">
|
| 222 |
+
<div id="mission-progress" class="bg-blue-600 h-2.5 rounded-full" style="width: 0%"></div>
|
| 223 |
+
</div>
|
| 224 |
+
<div class="flex justify-between text-xs mt-1">
|
| 225 |
+
<span>0%</span>
|
| 226 |
+
<span id="mission-percentage">0%</span>
|
| 227 |
+
</div>
|
| 228 |
+
</div>
|
| 229 |
+
</div>
|
| 230 |
+
|
| 231 |
+
<!-- Telemetry Panel -->
|
| 232 |
+
<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">
|
| 233 |
+
<div class="p-4 space-y-6">
|
| 234 |
+
<div>
|
| 235 |
+
<h3 class="font-semibold mb-2 flex items-center">
|
| 236 |
+
<i class="fas fa-heartbeat mr-2 text-red-400"></i>
|
| 237 |
+
Drone Telemetry
|
| 238 |
+
</h3>
|
| 239 |
+
<div class="space-y-2">
|
| 240 |
+
<div class="flex justify-between">
|
| 241 |
+
<span class="text-sm text-gray-300">Mode:</span>
|
| 242 |
+
<span id="flight-mode" class="telemetry-value">--</span>
|
| 243 |
+
</div>
|
| 244 |
+
<div class="flex justify-between">
|
| 245 |
+
<span class="text-sm text-gray-300">Altitude:</span>
|
| 246 |
+
<span id="altitude" class="telemetry-value">-- m</span>
|
| 247 |
+
</div>
|
| 248 |
+
<div class="flex justify-between">
|
| 249 |
+
<span class="text-sm text-gray-300">Speed:</span>
|
| 250 |
+
<span id="speed" class="telemetry-value">-- m/s</span>
|
| 251 |
+
</div>
|
| 252 |
+
<div class="flex justify-between">
|
| 253 |
+
<span class="text-sm text-gray-300">Heading:</span>
|
| 254 |
+
<span id="heading" class="telemetry-value">--°</span>
|
| 255 |
+
</div>
|
| 256 |
+
<div class="flex justify-between">
|
| 257 |
+
<span class="text-sm text-gray-300">Distance:</span>
|
| 258 |
+
<span id="distance" class="telemetry-value">-- m</span>
|
| 259 |
+
</div>
|
| 260 |
+
</div>
|
| 261 |
+
</div>
|
| 262 |
+
|
| 263 |
+
<div>
|
| 264 |
+
<h3 class="font-semibold mb-2 flex items-center">
|
| 265 |
+
<i class="fas fa-bolt mr-2 text-yellow-400"></i>
|
| 266 |
+
Power System
|
| 267 |
+
</h3>
|
| 268 |
+
<div class="space-y-2">
|
| 269 |
+
<div class="flex justify-between">
|
| 270 |
+
<span class="text-sm text-gray-300">Voltage:</span>
|
| 271 |
+
<span id="voltage" class="telemetry-value">-- V</span>
|
| 272 |
+
</div>
|
| 273 |
+
<div class="flex justify-between">
|
| 274 |
+
<span class="text-sm text-gray-300">Current:</span>
|
| 275 |
+
<span id="current" class="telemetry-value">-- A</span>
|
| 276 |
+
</div>
|
| 277 |
+
<div class="flex justify-between">
|
| 278 |
+
<span class="text-sm text-gray-300">Remaining:</span>
|
| 279 |
+
<span id="battery-remaining" class="telemetry-value">--%</span>
|
| 280 |
+
</div>
|
| 281 |
+
<div class="flex justify-between">
|
| 282 |
+
<span class="text-sm text-gray-300">Flight Time:</span>
|
| 283 |
+
<span id="flight-time" class="telemetry-value">--:--</span>
|
| 284 |
+
</div>
|
| 285 |
+
</div>
|
| 286 |
+
</div>
|
| 287 |
+
|
| 288 |
+
<div>
|
| 289 |
+
<h3 class="font-semibold mb-2 flex items-center">
|
| 290 |
+
<i class="fas fa-search mr-2 text-purple-400"></i>
|
| 291 |
+
Thermal Defects
|
| 292 |
+
</h3>
|
| 293 |
+
<div class="space-y-2">
|
| 294 |
+
<div class="flex justify-between">
|
| 295 |
+
<span class="text-sm text-gray-300">Detected:</span>
|
| 296 |
+
<span id="defects-count" class="telemetry-value">0</span>
|
| 297 |
+
</div>
|
| 298 |
+
<div class="flex justify-between">
|
| 299 |
+
<span class="text-sm text-gray-300">Max Temp:</span>
|
| 300 |
+
<span id="max-temp" class="telemetry-value">--°C</span>
|
| 301 |
+
</div>
|
| 302 |
+
<div class="flex justify-between">
|
| 303 |
+
<span class="text-sm text-gray-300">Avg Temp:</span>
|
| 304 |
+
<span id="avg-temp" class="telemetry-value">--°C</span>
|
| 305 |
+
</div>
|
| 306 |
+
</div>
|
| 307 |
+
</div>
|
| 308 |
+
|
| 309 |
+
<div class="bg-gray-600 rounded p-3">
|
| 310 |
+
<h3 class="font-semibold mb-2 text-sm">Quick Actions</h3>
|
| 311 |
+
<div class="grid grid-cols-2 gap-2">
|
| 312 |
+
<button id="rtl-btn" class="bg-yellow-600 hover:bg-yellow-700 text-white py-1 px-2 rounded text-xs">
|
| 313 |
+
<i class="fas fa-home mr-1"></i> RTL
|
| 314 |
+
</button>
|
| 315 |
+
<button id="loiter-btn" class="bg-blue-600 hover:bg-blue-700 text-white py-1 px-2 rounded text-xs">
|
| 316 |
+
<i class="fas fa-circle-notch mr-1"></i> Loiter
|
| 317 |
+
</button>
|
| 318 |
+
<button id="land-btn" class="bg-red-600 hover:bg-red-700 text-white py-1 px-2 rounded text-xs">
|
| 319 |
+
<i class="fas fa-arrow-down mr-1"></i> Land
|
| 320 |
+
</button>
|
| 321 |
+
<button id="takeoff-btn" class="bg-green-600 hover:bg-green-700 text-white py-1 px-2 rounded text-xs">
|
| 322 |
+
<i class="fas fa-arrow-up mr-1"></i> Takeoff
|
| 323 |
+
</button>
|
| 324 |
+
</div>
|
| 325 |
+
</div>
|
| 326 |
+
</div>
|
| 327 |
+
</div>
|
| 328 |
+
</div>
|
| 329 |
+
</div>
|
| 330 |
+
|
| 331 |
+
<!-- Image Preview Modal -->
|
| 332 |
+
<div id="image-modal" class="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50 hidden">
|
| 333 |
+
<div class="bg-gray-800 rounded-lg max-w-4xl w-full modal-content overflow-hidden">
|
| 334 |
+
<div class="flex justify-between items-center p-4 border-b border-gray-700">
|
| 335 |
+
<h3 class="text-lg font-semibold">Thermal Image Analysis</h3>
|
| 336 |
+
<button id="close-modal" class="text-gray-400 hover:text-white">
|
| 337 |
+
<i class="fas fa-times"></i>
|
| 338 |
+
</button>
|
| 339 |
+
</div>
|
| 340 |
+
<div class="p-4 flex flex-col md:flex-row overflow-auto">
|
| 341 |
+
<div class="w-full md:w-2/3 mb-4 md:mb-0 md:pr-4">
|
| 342 |
+
<img id="modal-image" src="" alt="Thermal Image" class="w-full h-auto rounded max-h-[60vh] object-contain">
|
| 343 |
+
</div>
|
| 344 |
+
<div class="w-full md:w-1/3">
|
| 345 |
+
<h4 class="font-semibold mb-2">Defect Details</h4>
|
| 346 |
+
<div class="space-y-3">
|
| 347 |
+
<div>
|
| 348 |
+
<label class="block text-sm text-gray-300">Module ID:</label>
|
| 349 |
+
<p id="module-id" class="font-mono">PV-12-045</p>
|
| 350 |
+
</div>
|
| 351 |
+
<div>
|
| 352 |
+
<label class="block text-sm text-gray-300">Temperature:</label>
|
| 353 |
+
<p id="module-temp" class="font-mono">78.4°C</p>
|
| 354 |
+
</div>
|
| 355 |
+
<div>
|
| 356 |
+
<label class="block text-sm text-gray-300">Location:</label>
|
| 357 |
+
<p id="module-location" class="font-mono">Row 12, Position 45</p>
|
| 358 |
+
</div>
|
| 359 |
+
<div>
|
| 360 |
+
<label class="block text-sm text-gray-300">Defect Type:</label>
|
| 361 |
+
<p id="defect-type" class="font-mono">Hot Spot</p>
|
| 362 |
+
</div>
|
| 363 |
+
<div>
|
| 364 |
+
<label class="block text-sm text-gray-300">Confidence:</label>
|
| 365 |
+
<p id="defect-confidence" class="font-mono">92%</p>
|
| 366 |
+
</div>
|
| 367 |
+
</div>
|
| 368 |
+
<div class="mt-4">
|
| 369 |
+
<button class="w-full bg-blue-600 hover:bg-blue-700 text-white py-2 rounded">
|
| 370 |
+
<i class="fas fa-file-export mr-2"></i> Export Report
|
| 371 |
+
</button>
|
| 372 |
+
</div>
|
| 373 |
+
</div>
|
| 374 |
+
</div>
|
| 375 |
+
</div>
|
| 376 |
+
</div>
|
| 377 |
+
|
| 378 |
+
<script>
|
| 379 |
+
// Initialize Google Maps
|
| 380 |
+
let map;
|
| 381 |
+
let droneMarker;
|
| 382 |
+
let waypoints = [];
|
| 383 |
+
let defectMarkers = [];
|
| 384 |
+
let isConnected = false;
|
| 385 |
+
let missionWaypoints = [];
|
| 386 |
+
let currentWaypointIndex = 0;
|
| 387 |
+
let missionInterval;
|
| 388 |
+
let isMissionPaused = false;
|
| 389 |
+
let flightPath = null;
|
| 390 |
+
|
| 391 |
+
function initMap() {
|
| 392 |
+
try {
|
| 393 |
+
// Default to a solar farm location (example: Topaz Solar Farm, California)
|
| 394 |
+
const solarFarm = { lat: 36.6625, lng: 2.71986 };
|
| 395 |
+
|
| 396 |
+
map = new google.maps.Map(document.getElementById('map'), {
|
| 397 |
+
center: solarFarm,
|
| 398 |
+
zoom: 17,
|
| 399 |
+
mapTypeId: 'satellite',
|
| 400 |
+
disableDefaultUI: true,
|
| 401 |
+
mapTypeControlOptions: {
|
| 402 |
+
mapTypeIds: ['roadmap', 'satellite', 'hybrid', 'terrain']
|
| 403 |
+
}
|
| 404 |
+
});
|
| 405 |
+
|
| 406 |
+
// Add drone marker
|
| 407 |
+
droneMarker = new google.maps.Marker({
|
| 408 |
+
position: solarFarm,
|
| 409 |
+
map: map,
|
| 410 |
+
icon: {
|
| 411 |
+
path: google.maps.SymbolPath.CIRCLE,
|
| 412 |
+
fillColor: '#ef4444',
|
| 413 |
+
fillOpacity: 1,
|
| 414 |
+
strokeColor: 'white',
|
| 415 |
+
strokeWeight: 2,
|
| 416 |
+
scale: 10
|
| 417 |
+
},
|
| 418 |
+
title: 'Drone',
|
| 419 |
+
zIndex: 999
|
| 420 |
+
});
|
| 421 |
+
|
| 422 |
+
// Add click listener for waypoints
|
| 423 |
+
map.addListener('click', (event) => {
|
| 424 |
+
const addWaypointBtn = document.getElementById('add-waypoint-btn');
|
| 425 |
+
if (addWaypointBtn.classList.contains('active')) {
|
| 426 |
+
addWaypoint(event.latLng);
|
| 427 |
+
}
|
| 428 |
+
});
|
| 429 |
+
|
| 430 |
+
// Load saved waypoints from localStorage
|
| 431 |
+
loadWaypoints();
|
| 432 |
+
|
| 433 |
+
console.log("Map initialized successfully");
|
| 434 |
+
|
| 435 |
+
} catch (error) {
|
| 436 |
+
console.error("Error loading map:", error);
|
| 437 |
+
alert("Unable to load map. Please check your internet connection and API key.");
|
| 438 |
+
}
|
| 439 |
+
}
|
| 440 |
+
|
| 441 |
+
function addWaypoint(location) {
|
| 442 |
+
const waypointNumber = waypoints.length + 1;
|
| 443 |
+
const altitude = document.getElementById('waypoint-altitude').value;
|
| 444 |
+
|
| 445 |
+
const marker = new google.maps.Marker({
|
| 446 |
+
position: location,
|
| 447 |
+
map: map,
|
| 448 |
+
icon: {
|
| 449 |
+
path: google.maps.SymbolPath.CIRCLE,
|
| 450 |
+
fillColor: '#3b82f6',
|
| 451 |
+
fillOpacity: 1,
|
| 452 |
+
strokeColor: 'white',
|
| 453 |
+
strokeWeight: 2,
|
| 454 |
+
scale: 8
|
| 455 |
+
},
|
| 456 |
+
label: {
|
| 457 |
+
text: waypointNumber.toString(),
|
| 458 |
+
color: 'white',
|
| 459 |
+
fontSize: '10px'
|
| 460 |
+
},
|
| 461 |
+
title: `Waypoint ${waypointNumber} (${altitude}m)`
|
| 462 |
+
});
|
| 463 |
+
|
| 464 |
+
// Store additional waypoint data
|
| 465 |
+
marker.altitude = altitude;
|
| 466 |
+
marker.speed = document.getElementById('waypoint-speed').value;
|
| 467 |
+
|
| 468 |
+
waypoints.push(marker);
|
| 469 |
+
missionWaypoints.push(location);
|
| 470 |
+
|
| 471 |
+
// Draw flight path
|
| 472 |
+
drawFlightPath();
|
| 473 |
+
|
| 474 |
+
// Save waypoints to localStorage
|
| 475 |
+
saveWaypoints();
|
| 476 |
+
}
|
| 477 |
+
|
| 478 |
+
function drawFlightPath() {
|
| 479 |
+
// Clear previous path if exists
|
| 480 |
+
if (flightPath) {
|
| 481 |
+
flightPath.setMap(null);
|
| 482 |
+
}
|
| 483 |
+
|
| 484 |
+
if (waypoints.length > 1) {
|
| 485 |
+
flightPath = new google.maps.Polyline({
|
| 486 |
+
path: waypoints.map(wp => wp.getPosition()),
|
| 487 |
+
geodesic: true,
|
| 488 |
+
strokeColor: '#3b82f6',
|
| 489 |
+
strokeOpacity: 1.0,
|
| 490 |
+
strokeWeight: 3,
|
| 491 |
+
zIndex: 1
|
| 492 |
+
});
|
| 493 |
+
|
| 494 |
+
flightPath.setMap(map);
|
| 495 |
+
}
|
| 496 |
+
}
|
| 497 |
+
|
| 498 |
+
function clearWaypoints() {
|
| 499 |
+
waypoints.forEach(marker => marker.setMap(null));
|
| 500 |
+
waypoints = [];
|
| 501 |
+
missionWaypoints = [];
|
| 502 |
+
|
| 503 |
+
if (flightPath) {
|
| 504 |
+
flightPath.setMap(null);
|
| 505 |
+
flightPath = null;
|
| 506 |
+
}
|
| 507 |
+
|
| 508 |
+
document.getElementById('mission-percentage').textContent = '0%';
|
| 509 |
+
document.getElementById('mission-progress').style.width = '0%';
|
| 510 |
+
currentWaypointIndex = 0;
|
| 511 |
+
|
| 512 |
+
// Clear saved waypoints
|
| 513 |
+
localStorage.removeItem('savedWaypoints');
|
| 514 |
+
}
|
| 515 |
+
|
| 516 |
+
function saveWaypoints() {
|
| 517 |
+
const waypointsData = waypoints.map(marker => ({
|
| 518 |
+
lat: marker.getPosition().lat(),
|
| 519 |
+
lng: marker.getPosition().lng(),
|
| 520 |
+
altitude: marker.altitude,
|
| 521 |
+
speed: marker.speed
|
| 522 |
+
}));
|
| 523 |
+
localStorage.setItem('savedWaypoints', JSON.stringify(waypointsData));
|
| 524 |
+
}
|
| 525 |
+
|
| 526 |
+
function loadWaypoints() {
|
| 527 |
+
const savedWaypoints = localStorage.getItem('savedWaypoints');
|
| 528 |
+
if (savedWaypoints) {
|
| 529 |
+
const waypointsData = JSON.parse(savedWaypoints);
|
| 530 |
+
waypointsData.forEach(wpData => {
|
| 531 |
+
const location = new google.maps.LatLng(wpData.lat, wpData.lng);
|
| 532 |
+
document.getElementById('waypoint-altitude').value = wpData.altitude;
|
| 533 |
+
document.getElementById('waypoint-speed').value = wpData.speed;
|
| 534 |
+
addWaypoint(location);
|
| 535 |
+
});
|
| 536 |
+
}
|
| 537 |
+
}
|
| 538 |
+
|
| 539 |
+
function startMission() {
|
| 540 |
+
if (isMissionPaused) {
|
| 541 |
+
isMissionPaused = false;
|
| 542 |
+
simulateDroneMovement();
|
| 543 |
+
return;
|
| 544 |
+
}
|
| 545 |
+
|
| 546 |
+
if (missionWaypoints.length === 0) {
|
| 547 |
+
alert('Please add waypoints first');
|
| 548 |
+
return;
|
| 549 |
+
}
|
| 550 |
+
|
| 551 |
+
// Reset mission progress
|
| 552 |
+
currentWaypointIndex = 0;
|
| 553 |
+
document.getElementById('mission-percentage').textContent = '0%';
|
| 554 |
+
document.getElementById('mission-progress').style.width = '0%';
|
| 555 |
+
|
| 556 |
+
// Clear existing defects
|
| 557 |
+
defectMarkers.forEach(marker => marker.setMap(null));
|
| 558 |
+
defectMarkers = [];
|
| 559 |
+
document.getElementById('defects-count').textContent = '0';
|
| 560 |
+
document.getElementById('max-temp').textContent = '--°C';
|
| 561 |
+
document.getElementById('avg-temp').textContent = '--°C';
|
| 562 |
+
|
| 563 |
+
// Start simulation
|
| 564 |
+
simulateDroneMovement();
|
| 565 |
+
}
|
| 566 |
+
|
| 567 |
+
function pauseMission() {
|
| 568 |
+
isMissionPaused = true;
|
| 569 |
+
if (missionInterval) {
|
| 570 |
+
clearTimeout(missionInterval);
|
| 571 |
+
}
|
| 572 |
+
}
|
| 573 |
+
|
| 574 |
+
function simulateDroneMovement() {
|
| 575 |
+
if (isMissionPaused || missionWaypoints.length === 0) return;
|
| 576 |
+
|
| 577 |
+
const nextWaypoint = missionWaypoints[currentWaypointIndex];
|
| 578 |
+
const currentPosition = droneMarker.getPosition();
|
| 579 |
+
|
| 580 |
+
// Calculate new position 1% closer to the waypoint
|
| 581 |
+
const latDiff = nextWaypoint.lat() - currentPosition.lat();
|
| 582 |
+
const lngDiff = nextWaypoint.lng() - currentPosition.lng();
|
| 583 |
+
|
| 584 |
+
const newLat = currentPosition.lat() + latDiff * 0.01;
|
| 585 |
+
const newLng = currentPosition.lng() + lngDiff * 0.01;
|
| 586 |
+
|
| 587 |
+
const newPosition = new google.maps.LatLng(newLat, newLng);
|
| 588 |
+
|
| 589 |
+
// Calculate distance to waypoint
|
| 590 |
+
const distance = google.maps.geometry.spherical.computeDistanceBetween(
|
| 591 |
+
newPosition, nextWaypoint);
|
| 592 |
+
|
| 593 |
+
// Update drone position and heading
|
| 594 |
+
droneMarker.setPosition(newPosition);
|
| 595 |
+
|
| 596 |
+
// Calculate heading (bearing)
|
| 597 |
+
const heading = google.maps.geometry.spherical.computeHeading(
|
| 598 |
+
currentPosition, newPosition);
|
| 599 |
+
droneMarker.setIcon({
|
| 600 |
+
path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW,
|
| 601 |
+
fillColor: '#ef4444',
|
| 602 |
+
fillOpacity: 1,
|
| 603 |
+
strokeColor: 'white',
|
| 604 |
+
strokeWeight: 1,
|
| 605 |
+
scale: 5,
|
| 606 |
+
rotation: heading
|
| 607 |
+
});
|
| 608 |
+
|
| 609 |
+
// Update telemetry
|
| 610 |
+
updateTelemetry(newPosition, heading, distance);
|
| 611 |
+
|
| 612 |
+
// Check if reached waypoint
|
| 613 |
+
if (distance < 5) { // 5 meters threshold
|
| 614 |
+
currentWaypointIndex = (currentWaypointIndex + 1) % missionWaypoints.length;
|
| 615 |
+
|
| 616 |
+
// Update mission progress
|
| 617 |
+
const progress = Math.floor((currentWaypointIndex / missionWaypoints.length) * 100);
|
| 618 |
+
document.getElementById('mission-percentage').textContent = `${progress}%`;
|
| 619 |
+
document.getElementById('mission-progress').style.width = `${progress}%`;
|
| 620 |
+
|
| 621 |
+
// If completed full loop, pause mission
|
| 622 |
+
if (currentWaypointIndex === 0) {
|
| 623 |
+
pauseMission();
|
| 624 |
+
document.getElementById('start-mission-btn').classList.remove('hidden');
|
| 625 |
+
document.getElementById('pause-mission-btn').classList.add('hidden');
|
| 626 |
+
return;
|
| 627 |
+
}
|
| 628 |
+
}
|
| 629 |
+
|
| 630 |
+
// Randomly detect thermal defects during flight
|
| 631 |
+
if (Math.random() < 0.02 && document.getElementById('enable-yolo').checked) {
|
| 632 |
+
detectThermalDefect(newPosition);
|
| 633 |
+
}
|
| 634 |
+
|
| 635 |
+
// Continue simulation
|
| 636 |
+
missionInterval = setTimeout(simulateDroneMovement, 100);
|
| 637 |
+
}
|
| 638 |
+
|
| 639 |
+
function updateTelemetry(position, heading, distance) {
|
| 640 |
+
document.getElementById('gps-coords').textContent =
|
| 641 |
+
`${position.lat().toFixed(6)}, ${position.lng().toFixed(6)}`;
|
| 642 |
+
document.getElementById('altitude').textContent = `${waypoints[currentWaypointIndex]?.altitude || '50'} m`;
|
| 643 |
+
document.getElementById('speed').textContent = `${waypoints[currentWaypointIndex]?.speed || '5'} m/s`;
|
| 644 |
+
document.getElementById('heading').textContent = `${Math.round(heading)}°`;
|
| 645 |
+
document.getElementById('distance').textContent = `${Math.round(distance)} m`;
|
| 646 |
+
|
| 647 |
+
const batteryLevel = Math.floor(80 - (currentWaypointIndex / missionWaypoints.length) * 30);
|
| 648 |
+
document.getElementById('battery-level').textContent = `${batteryLevel}%`;
|
| 649 |
+
document.getElementById('battery-remaining').textContent = `${batteryLevel}%`;
|
| 650 |
+
|
| 651 |
+
document.getElementById('satellites').textContent = '12';
|
| 652 |
+
document.getElementById('flight-mode').textContent = 'AUTO';
|
| 653 |
+
document.getElementById('voltage').textContent = '15.7 V';
|
| 654 |
+
document.getElementById('current').textContent = '12.3 A';
|
| 655 |
+
|
| 656 |
+
// Update flight time (simulated)
|
| 657 |
+
const minutes = Math.floor(currentWaypointIndex / 10);
|
| 658 |
+
const seconds = currentWaypointIndex % 10 * 6;
|
| 659 |
+
document.getElementById('flight-time').textContent =
|
| 660 |
+
`${minutes}:${seconds < 10 ? '0' + seconds : seconds}`;
|
| 661 |
+
}
|
| 662 |
+
|
| 663 |
+
function detectThermalDefect(position) {
|
| 664 |
+
// Random offset to place defect marker near drone
|
| 665 |
+
const latOffset = (Math.random() - 0.5) * 0.0005;
|
| 666 |
+
const lngOffset = (Math.random() - 0.5) * 0.0005;
|
| 667 |
+
|
| 668 |
+
const defectLocation = new google.maps.LatLng(
|
| 669 |
+
position.lat() + latOffset,
|
| 670 |
+
position.lng() + lngOffset
|
| 671 |
+
);
|
| 672 |
+
|
| 673 |
+
const temp = Math.round(70 + Math.random() * 30);
|
| 674 |
+
const defectTypes = ['Hot Spot', 'Broken Cell', 'Bypass Diode Failure', 'Soiling'];
|
| 675 |
+
const defectType = defectTypes[Math.floor(Math.random() * defectTypes.length)];
|
| 676 |
+
|
| 677 |
+
const marker = new google.maps.Marker({
|
| 678 |
+
position: defectLocation,
|
| 679 |
+
map: map,
|
| 680 |
+
icon: {
|
| 681 |
+
path: google.maps.SymbolPath.CIRCLE,
|
| 682 |
+
fillColor: 'rgba(239, 68, 68, 0.7)',
|
| 683 |
+
fillOpacity: 1,
|
| 684 |
+
strokeColor: 'white',
|
| 685 |
+
strokeWeight: 1,
|
| 686 |
+
scale: 8
|
| 687 |
+
},
|
| 688 |
+
title: `Thermal Defect: ${temp}°C (${defectType})`,
|
| 689 |
+
zIndex: 10
|
| 690 |
+
});
|
| 691 |
+
|
| 692 |
+
// Add animation class
|
| 693 |
+
marker.addListener('domready', () => {
|
| 694 |
+
const markerElement = document.querySelector(`[title="Thermal Defect: ${temp}°C (${defectType})"]`);
|
| 695 |
+
if (markerElement) {
|
| 696 |
+
markerElement.classList.add('defect-marker');
|
| 697 |
+
}
|
| 698 |
+
});
|
| 699 |
+
|
| 700 |
+
defectMarkers.push(marker);
|
| 701 |
+
|
| 702 |
+
// Add click listener to show defect details
|
| 703 |
+
marker.addListener('click', () => {
|
| 704 |
+
showDefectDetails(defectLocation, temp, defectType);
|
| 705 |
+
});
|
| 706 |
+
|
| 707 |
+
// Update defects count
|
| 708 |
+
document.getElementById('defects-count').textContent = defectMarkers.length;
|
| 709 |
+
|
| 710 |
+
// Update max and avg temp
|
| 711 |
+
const temps = defectMarkers.map(m => parseInt(m.getTitle().match(/(\d+)°C/)[1]));
|
| 712 |
+
const maxTemp = Math.max(...temps);
|
| 713 |
+
const avgTemp = Math.round(temps.reduce((a, b) => a + b, 0) / temps.length);
|
| 714 |
+
|
| 715 |
+
document.getElementById('max-temp').textContent = `${maxTemp}°C`;
|
| 716 |
+
document.getElementById('avg-temp').textContent = `${avgTemp}°C`;
|
| 717 |
+
}
|
| 718 |
+
|
| 719 |
+
function showDefectDetails(location, temperature, defectType) {
|
| 720 |
+
document.getElementById('modal-image').src = 'https://via.placeholder.com/800x600/333/fff?text=Thermal+Image+Analysis';
|
| 721 |
+
document.getElementById('module-temp').textContent = `${temperature}°C`;
|
| 722 |
+
document.getElementById('defect-type').textContent = defectType;
|
| 723 |
+
document.getElementById('defect-confidence').textContent = `${Math.round(85 + Math.random() * 15)}%`;
|
| 724 |
+
|
| 725 |
+
// Generate random module ID
|
| 726 |
+
const row = Math.floor(1 + Math.random() * 20);
|
| 727 |
+
const pos = Math.floor(1 + Math.random() * 100);
|
| 728 |
+
document.getElementById('module-id').textContent = `PV-${row}-${pos}`;
|
| 729 |
+
document.getElementById('module-location').textContent = `Row ${row}, Position ${pos}`;
|
| 730 |
+
|
| 731 |
+
// Show modal
|
| 732 |
+
document.getElementById('image-modal').classList.remove('hidden');
|
| 733 |
+
}
|
| 734 |
+
|
| 735 |
+
// DOM Event Listeners
|
| 736 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 737 |
+
// Initialize map when window loads
|
| 738 |
+
window.initMap = initMap;
|
| 739 |
+
|
| 740 |
+
// Toggle sidebar
|
| 741 |
+
document.getElementById('toggle-sidebar').addEventListener('click', () => {
|
| 742 |
+
document.getElementById('sidebar').classList.toggle('collapsed');
|
| 743 |
+
const icon = document.querySelector('#toggle-sidebar i');
|
| 744 |
+
if (document.getElementById('sidebar').classList.contains('collapsed')) {
|
| 745 |
+
icon.classList.remove('fa-chevron-left');
|
| 746 |
+
icon.classList.add('fa-chevron-right');
|
| 747 |
+
} else {
|
| 748 |
+
icon.classList.remove('fa-chevron-right');
|
| 749 |
+
icon.classList.add('fa-chevron-left');
|
| 750 |
+
}
|
| 751 |
+
});
|
| 752 |
+
|
| 753 |
+
// Mobile menu toggle
|
| 754 |
+
document.getElementById('mobile-menu-btn').addEventListener('click', () => {
|
| 755 |
+
document.getElementById('sidebar').classList.toggle('hidden');
|
| 756 |
+
});
|
| 757 |
+
|
| 758 |
+
// Connect button
|
| 759 |
+
document.getElementById('connect-btn').addEventListener('click', () => {
|
| 760 |
+
const comPort = document.getElementById('com-port').value;
|
| 761 |
+
const baudRate = document.getElementById('baud-rate').value;
|
| 762 |
+
|
| 763 |
+
if (!comPort) {
|
| 764 |
+
alert('Please select a COM port');
|
| 765 |
+
return;
|
| 766 |
+
}
|
| 767 |
+
|
| 768 |
+
// Simulate connection
|
| 769 |
+
isConnected = true;
|
| 770 |
+
document.getElementById('connect-btn').classList.add('hidden');
|
| 771 |
+
document.getElementById('disconnect-btn').classList.remove('hidden');
|
| 772 |
+
document.getElementById('mavlink-status').textContent = 'Yes';
|
| 773 |
+
document.getElementById('mavlink-status').classList.remove('text-red-500');
|
| 774 |
+
document.getElementById('mavlink-status').classList.add('text-green-500');
|
| 775 |
+
document.getElementById('drone-status').textContent = 'Connected';
|
| 776 |
+
document.getElementById('drone-status').classList.remove('text-gray-300');
|
| 777 |
+
document.getElementById('drone-status').classList.add('text-green-400');
|
| 778 |
+
document.getElementById('start-mission-btn').classList.remove('hidden');
|
| 779 |
+
|
| 780 |
+
// Simulate COM port connection
|
| 781 |
+
console.log(`Connected to ${comPort} at ${baudRate} baud`);
|
| 782 |
+
});
|
| 783 |
+
|
| 784 |
+
// Disconnect button
|
| 785 |
+
document.getElementById('disconnect-btn').addEventListener('click', () => {
|
| 786 |
+
isConnected = false;
|
| 787 |
+
document.getElementById('connect-btn').classList.remove('hidden');
|
| 788 |
+
document.getElementById('disconnect-btn').classList.add('hidden');
|
| 789 |
+
document.getElementById('mavlink-status').textContent = 'No';
|
| 790 |
+
document.getElementById('mavlink-status').classList.remove('text-green-500');
|
| 791 |
+
document.getElementById('mavlink-status').classList.add('text-red-500');
|
| 792 |
+
document.getElementById('drone-status').textContent = 'Disconnected';
|
| 793 |
+
document.getElementById('drone-status').classList.remove('text-green-400');
|
| 794 |
+
document.getElementById('drone-status').classList.add('text-gray-300');
|
| 795 |
+
document.getElementById('start-mission-btn').classList.add('hidden');
|
| 796 |
+
document.getElementById('pause-mission-btn').classList.add('hidden');
|
| 797 |
+
|
| 798 |
+
// Stop any ongoing mission
|
| 799 |
+
pauseMission();
|
| 800 |
+
});
|
| 801 |
+
|
| 802 |
+
// Add waypoint button
|
| 803 |
+
document.getElementById('add-waypoint-btn').addEventListener('click', function() {
|
| 804 |
+
this.classList.toggle('active');
|
| 805 |
+
if (this.classList.contains('active')) {
|
| 806 |
+
this.innerHTML = '<i class="fas fa-times mr-1"></i> Cancel';
|
| 807 |
+
document.getElementById('map').style.cursor = 'crosshair';
|
| 808 |
+
} else {
|
| 809 |
+
this.innerHTML = '<i class="fas fa-plus mr-1"></i> Add Waypoint';
|
| 810 |
+
document.getElementById('map').style.cursor = '';
|
| 811 |
+
}
|
| 812 |
+
});
|
| 813 |
+
|
| 814 |
+
// Clear waypoints button
|
| 815 |
+
document.getElementById('clear-waypoints-btn').addEventListener('click', clearWaypoints);
|
| 816 |
+
|
| 817 |
+
// Start mission button
|
| 818 |
+
document.getElementById('start-mission-btn').addEventListener('click', () => {
|
| 819 |
+
document.getElementById('start-mission-btn').classList.add('hidden');
|
| 820 |
+
document.getElementById('pause-mission-btn').classList.remove('hidden');
|
| 821 |
+
startMission();
|
| 822 |
+
});
|
| 823 |
+
|
| 824 |
+
// Pause mission button
|
| 825 |
+
document.getElementById('pause-mission-btn').addEventListener('click', () => {
|
| 826 |
+
document.getElementById('start-mission-btn').classList.remove('hidden');
|
| 827 |
+
document.getElementById('pause-mission-btn').classList.add('hidden');
|
| 828 |
+
pauseMission();
|
| 829 |
+
});
|
| 830 |
+
|
| 831 |
+
// Upload mission button
|
| 832 |
+
document.getElementById('upload-mission-btn').addEventListener('click', () => {
|
| 833 |
+
if (waypoints.length === 0) {
|
| 834 |
+
alert('Please add waypoints first');
|
| 835 |
+
return;
|
| 836 |
+
}
|
| 837 |
+
|
| 838 |
+
alert(`Mission with ${waypoints.length} waypoints uploaded to drone`);
|
| 839 |
+
});
|
| 840 |
+
|
| 841 |
+
// Analyze images button
|
| 842 |
+
document.getElementById('analyze-btn').addEventListener('click', () => {
|
| 843 |
+
const threshold = document.getElementById('thermal-threshold').value;
|
| 844 |
+
alert(`Analyzing thermal images with threshold ${threshold}°C`);
|
| 845 |
+
});
|
| 846 |
+
|
| 847 |
+
// Map type buttons
|
| 848 |
+
document.getElementById('satellite-btn').addEventListener('click', () => {
|
| 849 |
+
map.setMapTypeId('satellite');
|
| 850 |
+
});
|
| 851 |
+
|
| 852 |
+
document.getElementById('terrain-btn').addEventListener('click', () => {
|
| 853 |
+
map.setMapTypeId('terrain');
|
| 854 |
+
});
|
| 855 |
+
|
| 856 |
+
document.getElementById('thermal-overlay-btn').addEventListener('click', () => {
|
| 857 |
+
alert('Thermal overlay would be displayed here');
|
| 858 |
+
});
|
| 859 |
+
|
| 860 |
+
// Quick action buttons
|
| 861 |
+
document.getElementById('rtl-btn').addEventListener('click', () => {
|
| 862 |
+
pauseMission();
|
| 863 |
+
alert('Return to Launch command sent');
|
| 864 |
+
});
|
| 865 |
+
|
| 866 |
+
document.getElementById('loiter-btn').addEventListener('click', () => {
|
| 867 |
+
pauseMission();
|
| 868 |
+
alert('Loiter command sent');
|
| 869 |
+
});
|
| 870 |
+
|
| 871 |
+
document.getElementById('land-btn').addEventListener('click', () => {
|
| 872 |
+
pauseMission();
|
| 873 |
+
alert('Land command sent');
|
| 874 |
+
});
|
| 875 |
+
|
| 876 |
+
document.getElementById('takeoff-btn').addEventListener('click', () => {
|
| 877 |
+
alert('Takeoff command sent');
|
| 878 |
+
});
|
| 879 |
+
|
| 880 |
+
// Close modal button
|
| 881 |
+
document.getElementById('close-modal').addEventListener('click', () => {
|
| 882 |
+
document.getElementById('image-modal').classList.add('hidden');
|
| 883 |
+
});
|
| 884 |
+
|
| 885 |
+
// Handle clicks outside modal to close it
|
| 886 |
+
document.getElementById('image-modal').addEventListener('click', (e) => {
|
| 887 |
+
if (e.target === document.getElementById('image-modal')) {
|
| 888 |
+
document.getElementById('image-modal').classList.add('hidden');
|
| 889 |
+
}
|
| 890 |
+
});
|
| 891 |
+
});
|
| 892 |
+
</script>
|
| 893 |
+
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=nkellil/drone1" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
|
| 894 |
+
</html>
|