pv-uav / index.html
nkellil's picture
Add 2 files
757b6d0 verified
<!DOCTYPE html>
<html lang="fr">
<head>
<script src="https://unpkg.com/mavlink-mappings@2.0.0/mavlink_ardupilotmega_v2.0.js"></script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PV Drone Control - Surveillance des centrales photovoltaïques</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
#map { height: 100%; width: 100%; }
.mission-waypoint {
background-color: #3b82f6;
color: white;
border-radius: 50%;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
cursor: move;
}
.mission-path {
stroke: #3b82f6;
stroke-width: 3;
stroke-dasharray: 10, 5;
fill: none;
}
.sidebar {
transition: all 0.3s ease;
}
.sidebar.collapsed {
width: 60px !important;
}
.sidebar.collapsed .sidebar-content {
display: none;
}
.drone-status-indicator {
width: 12px;
height: 12px;
border-radius: 50%;
display: inline-block;
margin-right: 8px;
}
.status-connected {
background-color: #10B981;
box-shadow: 0 0 10px #10B981;
}
.status-disconnected {
background-color: #EF4444;
}
.status-warning {
background-color: #F59E0B;
box-shadow: 0 0 10px #F59E0B;
}
.gauge {
position: relative;
width: 100%;
height: 8px;
background-color: #e5e7eb;
border-radius: 4px;
overflow: hidden;
}
.gauge-fill {
height: 100%;
border-radius: 4px;
transition: width 0.3s ease;
}
.battery-high {
background-color: #10B981;
}
.battery-medium {
background-color: #F59E0B;
}
.battery-low {
background-color: #EF4444;
}
.custom-scrollbar::-webkit-scrollbar {
width: 6px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 10px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 10px;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: #94a3b8;
}
</style>
</head>
<body class="bg-gray-100 h-screen flex overflow-hidden">
<!-- Sidebar -->
<div class="sidebar bg-white w-64 h-full flex flex-col border-r border-gray-200 shadow-sm relative">
<div class="p-4 border-b border-gray-200 flex items-center justify-between">
<div class="flex items-center">
<i class="fas fa-solar-panel text-blue-500 text-xl mr-2"></i>
<h1 class="text-lg font-semibold text-gray-800">PV Drone Control</h1>
</div>
<button id="toggle-sidebar" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-chevron-left"></i>
</button>
</div>
<div class="sidebar-content flex-1 flex flex-col overflow-hidden">
<!-- Drone Status -->
<div class="p-4 border-b border-gray-200">
<h2 class="text-sm font-semibold text-gray-500 uppercase tracking-wider mb-2">Statut du drone</h2>
<div class="flex items-center mb-2">
<span class="drone-status-indicator status-connected"></span>
<span class="text-sm font-medium">DJI Mavic 3 Enterprise</span>
</div>
<div class="grid grid-cols-2 gap-2 text-xs">
<div>
<div class="text-gray-500">Batterie</div>
<div class="flex items-center">
<div class="gauge w-full mr-2">
<div class="gauge-fill battery-high" style="width: 78%"></div>
</div>
<span>78%</span>
</div>
</div>
<div>
<div class="text-gray-500">Altitude</div>
<div>120 m</div>
</div>
<div>
<div class="text-gray-500">Distance</div>
<div>450 m</div>
</div>
<div>
<div class="text-gray-500">Vitesse</div>
<div>8 m/s</div>
</div>
</div>
</div>
<!-- Missions -->
<div class="flex-1 overflow-y-auto custom-scrollbar">
<div class="p-4 border-b border-gray-200">
<div class="flex items-center justify-between">
<h2 class="text-sm font-semibold text-gray-500 uppercase tracking-wider">Missions</h2>
<button class="text-blue-500 hover:text-blue-700 text-sm">
<i class="fas fa-plus mr-1"></i>
<span>Nouvelle</span>
</button>
</div>
<div class="mt-3 space-y-2">
<div class="p-2 bg-blue-50 rounded-md border border-blue-100 cursor-pointer">
<div class="flex justify-between items-center">
<span class="text-sm font-medium">Centrale PV Nord</span>
<span class="text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded-full">En cours</span>
</div>
<div class="text-xs text-gray-500 mt-1">12/06/2023 - Inspection thermique</div>
</div>
<div class="p-2 hover:bg-gray-50 rounded-md cursor-pointer">
<div class="flex justify-between items-center">
<span class="text-sm font-medium">Centrale PV Sud</span>
<span class="text-xs bg-gray-100 text-gray-800 px-2 py-1 rounded-full">Planifiée</span>
</div>
<div class="text-xs text-gray-500 mt-1">15/06/2023 - Contrôle visuel</div>
</div>
<div class="p-2 hover:bg-gray-50 rounded-md cursor-pointer">
<div class="text-sm font-medium">Centrale PV Est</div>
<div class="text-xs text-gray-500 mt-1">10/06/2023 - Inspection complète</div>
</div>
</div>
</div>
<!-- PV Plants -->
<div class="p-4">
<h2 class="text-sm font-semibold text-gray-500 uppercase tracking-wider mb-2">Centrales PV</h2>
<div class="space-y-2">
<div class="flex items-center p-2 hover:bg-gray-50 rounded-md cursor-pointer">
<div class="w-3 h-3 bg-green-500 rounded-full mr-2"></div>
<span class="text-sm">Centrale Nord (12 MW)</span>
</div>
<div class="flex items-center p-2 hover:bg-gray-50 rounded-md cursor-pointer">
<div class="w-3 h-3 bg-yellow-500 rounded-full mr-2"></div>
<span class="text-sm">Centrale Sud (8 MW)</span>
</div>
<div class="flex items-center p-2 hover:bg-gray-50 rounded-md cursor-pointer">
<div class="w-3 h-3 bg-red-500 rounded-full mr-2"></div>
<span class="text-sm">Centrale Est (5 MW)</span>
</div>
</div>
</div>
</div>
<!-- User Profile -->
<div class="p-4 border-t border-gray-200">
<div class="flex items-center">
<div class="w-8 h-8 rounded-full bg-blue-500 flex items-center justify-center text-white font-semibold mr-2">JD</div>
<div class="text-sm">
<div class="font-medium">John Doe</div>
<div class="text-gray-500 text-xs">Technicien PV</div>
</div>
</div>
</div>
</div>
</div>
<!-- Main Content -->
<div class="flex-1 flex flex-col overflow-hidden">
<!-- Top Bar -->
<div class="bg-white border-b border-gray-200 p-4 flex items-center justify-between">
<div class="flex items-center space-x-4">
<h2 class="text-lg font-semibold text-gray-800">Mission: Centrale PV Nord</h2>
<div class="flex items-center text-sm">
<span class="text-gray-500 mr-2">Progression:</span>
<div class="gauge w-32">
<div class="gauge-fill battery-medium" style="width: 45%"></div>
</div>
<span class="ml-2">45%</span>
</div>
</div>
<div class="flex items-center space-x-3">
<button class="px-3 py-1 bg-blue-50 text-blue-600 rounded-md text-sm hover:bg-blue-100 flex items-center">
<i class="fas fa-play mr-1"></i>
<span>Démarrer</span>
</button>
<button class="px-3 py-1 bg-gray-50 text-gray-600 rounded-md text-sm hover:bg-gray-100 flex items-center">
<i class="fas fa-pause mr-1"></i>
<span>Pause</span>
</button>
<button class="px-3 py-1 bg-red-50 text-red-600 rounded-md text-sm hover:bg-red-100 flex items-center">
<i class="fas fa-stop mr-1"></i>
<span>Arrêter</span>
</button>
<button class="px-3 py-1 bg-green-50 text-green-600 rounded-md text-sm hover:bg-green-100 flex items-center">
<i class="fas fa-upload mr-1"></i>
<span>Exporter</span>
</button>
</div>
</div>
<!-- Map and Controls -->
<div class="flex-1 flex overflow-hidden">
<!-- Map Container -->
<div id="map" class="flex-1"></div>
<!-- Right Panel -->
<div class="w-80 bg-white border-l border-gray-200 flex flex-col">
<div class="p-4 border-b border-gray-200">
<h3 class="font-medium text-gray-800 mb-2">Paramètres de mission</h3>
<div class="space-y-4">
<div>
<label class="block text-sm text-gray-500 mb-1">Nom de la mission</label>
<input type="text" class="w-full px-3 py-2 border border-gray-300 rounded-md text-sm" value="Centrale PV Nord">
</div>
<div>
<label class="block text-sm text-gray-500 mb-1">Type d'inspection</label>
<select class="w-full px-3 py-2 border border-gray-300 rounded-md text-sm">
<option>Thermique</option>
<option>Visuelle</option>
<option>Complète</option>
</select>
</div>
<div class="grid grid-cols-2 gap-3">
<div>
<label class="block text-sm text-gray-500 mb-1">Altitude</label>
<div class="flex">
<input type="number" class="w-full px-3 py-2 border border-gray-300 rounded-l-md text-sm" value="120">
<span class="bg-gray-100 px-3 py-2 border-t border-b border-r border-gray-300 rounded-r-md text-sm flex items-center">m</span>
</div>
</div>
<div>
<label class="block text-sm text-gray-500 mb-1">Vitesse</label>
<div class="flex">
<input type="number" class="w-full px-3 py-2 border border-gray-300 rounded-l-md text-sm" value="8">
<span class="bg-gray-100 px-3 py-2 border-t border-b border-r border-gray-300 rounded-r-md text-sm flex items-center">m/s</span>
</div>
</div>
</div>
<div>
<label class="block text-sm text-gray-500 mb-1">Chevauchement</label>
<div class="flex items-center">
<input type="range" class="w-full" min="0" max="100" value="70">
<span class="ml-2 text-sm w-8">70%</span>
</div>
</div>
</div>
</div>
<div class="p-4 border-b border-gray-200 flex-1 overflow-y-auto custom-scrollbar">
<h3 class="font-medium text-gray-800 mb-2">Points de passage</h3>
<div class="space-y-2" id="waypoints-list">
<div class="flex items-center p-2 bg-blue-50 rounded-md">
<div class="w-6 h-6 bg-blue-500 text-white rounded-full flex items-center justify-center mr-2 text-xs">1</div>
<div class="text-sm flex-1">
<div>45.1234, 5.6789</div>
<div class="text-xs text-gray-500">Alt: 120m</div>
</div>
<button class="text-gray-400 hover:text-red-500">
<i class="fas fa-times"></i>
</button>
</div>
<div class="flex items-center p-2 hover:bg-gray-50 rounded-md">
<div class="w-6 h-6 bg-blue-500 text-white rounded-full flex items-center justify-center mr-2 text-xs">2</div>
<div class="text-sm flex-1">
<div>45.1245, 5.6801</div>
<div class="text-xs text-gray-500">Alt: 120m</div>
</div>
<button class="text-gray-400 hover:text-red-500">
<i class="fas fa-times"></i>
</button>
</div>
<div class="flex items-center p-2 hover:bg-gray-50 rounded-md">
<div class="w-6 h-6 bg-blue-500 text-white rounded-full flex items-center justify-center mr-2 text-xs">3</div>
<div class="text-sm flex-1">
<div>45.1256, 5.6812</div>
<div class="text-xs text-gray-500">Alt: 120m</div>
</div>
<button class="text-gray-400 hover:text-red-500">
<i class="fas fa-times"></i>
</button>
</div>
</div>
<div class="mt-4">
<button class="w-full py-2 bg-blue-50 text-blue-600 rounded-md text-sm hover:bg-blue-100 flex items-center justify-center">
<i class="fas fa-plus mr-1"></i>
<span>Ajouter un point</span>
</button>
</div>
</div>
<div class="p-4">
<h3 class="font-medium text-gray-800 mb-2">Statistiques</h3>
<div class="grid grid-cols-2 gap-3 text-sm">
<div class="bg-gray-50 p-2 rounded-md">
<div class="text-gray-500">Distance</div>
<div class="font-medium">2.4 km</div>
</div>
<div class="bg-gray-50 p-2 rounded-md">
<div class="text-gray-500">Durée estimée</div>
<div class="font-medium">18 min</div>
</div>
<div class="bg-gray-50 p-2 rounded-md">
<div class="text-gray-500">Points</div>
<div class="font-medium">24</div>
</div>
<div class="bg-gray-50 p-2 rounded-md">
<div class="text-gray-500">Couverture</div>
<div class="font-medium">5.2 ha</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// Initialize the map
const map = L.map('map').setView([45.1234, 5.6789], 17);
// Add Google Satellite layer
L.tileLayer('https://{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}', {
maxZoom: 20,
subdomains: ['mt0', 'mt1', 'mt2', 'mt3'],
attribution: '&copy; Google'
}).addTo(map);
// Add PV plant area
const pvPlant = L.polygon([
[45.1235, 5.6785],
[45.1245, 5.6795],
[45.1255, 5.6785],
[45.1245, 5.6775]
], {
color: '#f59e0b',
fillColor: '#fef3c7',
fillOpacity: 0.5,
weight: 2
}).addTo(map);
// Add mission waypoints
const waypoints = [
{ lat: 45.1234, lng: 5.6789, alt: 120 },
{ lat: 45.1245, lng: 5.6801, alt: 120 },
{ lat: 45.1256, lng: 5.6812, alt: 120 }
];
const waypointMarkers = [];
const waypointCoordinates = [];
waypoints.forEach((wp, index) => {
const marker = L.marker([wp.lat, wp.lng], {
icon: L.divIcon({
html: `<div class="mission-waypoint">${index + 1}</div>`,
className: '',
iconSize: [24, 24]
}),
draggable: true
}).addTo(map);
marker.on('drag', function(e) {
const newLatLng = e.target.getLatLng();
waypointCoordinates[index] = [newLatLng.lat, newLatLng.lng];
updatePath();
});
waypointMarkers.push(marker);
waypointCoordinates.push([wp.lat, wp.lng]);
});
// Add mission path
let missionPath = L.polyline(waypointCoordinates, {
color: '#3b82f6',
weight: 3,
dashArray: '10, 5',
className: 'mission-path'
}).addTo(map);
function updatePath() {
missionPath.setLatLngs(waypointCoordinates);
}
// Add drone position
const droneMarker = L.marker([45.1238, 5.6795], {
icon: L.divIcon({
html: '<i class="fas fa-drone-alt text-blue-600 text-2xl"></i>',
className: '',
iconSize: [32, 32]
})
}).addTo(map);
// Simulate drone movement
let currentWpIndex = 0;
const droneInterval = setInterval(() => {
if (currentWpIndex >= waypoints.length - 1) {
currentWpIndex = 0;
} else {
currentWpIndex++;
}
const nextWp = waypoints[currentWpIndex];
droneMarker.setLatLng([nextWp.lat, nextWp.lng]);
}, 3000);
// Toggle sidebar
document.getElementById('toggle-sidebar').addEventListener('click', function() {
document.querySelector('.sidebar').classList.toggle('collapsed');
const icon = this.querySelector('i');
if (icon.classList.contains('fa-chevron-left')) {
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');
}
});
// Add click event to create new waypoints
map.on('click', function(e) {
const newWaypoint = {
lat: e.latlng.lat,
lng: e.latlng.lng,
alt: 120
};
const index = waypoints.length;
const marker = L.marker([newWaypoint.lat, newWaypoint.lng], {
icon: L.divIcon({
html: `<div class="mission-waypoint">${index + 1}</div>`,
className: '',
iconSize: [24, 24]
}),
draggable: true
}).addTo(map);
marker.on('drag', function(e) {
const newLatLng = e.target.getLatLng();
waypointCoordinates[index] = [newLatLng.lat, newLatLng.lng];
updatePath();
});
waypoints.push(newWaypoint);
waypointMarkers.push(marker);
waypointCoordinates.push([newWaypoint.lat, newWaypoint.lng]);
updatePath();
// Add to waypoints list
const waypointItem = document.createElement('div');
waypointItem.className = 'flex items-center p-2 hover:bg-gray-50 rounded-md';
waypointItem.innerHTML = `
<div class="w-6 h-6 bg-blue-500 text-white rounded-full flex items-center justify-center mr-2 text-xs">${index + 1}</div>
<div class="text-sm flex-1">
<div>${newWaypoint.lat.toFixed(4)}, ${newWaypoint.lng.toFixed(4)}</div>
<div class="text-xs text-gray-500">Alt: ${newWaypoint.alt}m</div>
</div>
<button class="text-gray-400 hover:text-red-500">
<i class="fas fa-times"></i>
</button>
`;
document.getElementById('waypoints-list').appendChild(waypointItem);
});
</script>
<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/pv-uav" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>