krushimitravit's picture
Upload 11 files
63a00a1 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pump Analysis Results - Smart Irrigation</title>
<!-- Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<!-- Bootstrap Icons -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<!-- Plotly for Charts -->
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<style>
/* ===== CSS Variables (Color System) ===== */
:root {
--deep-green: #1a5d3a;
--accent-green: #198754;
--darker-accent: #143d2e;
--bg-light: #f8f9fa;
--surface-white: #ffffff;
--text-dark: #212529;
--text-muted: #6c757d;
--border-color: #dee2e6;
}
/* ===== Global Styles ===== */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Outfit', sans-serif;
background-color: var(--bg-light);
color: var(--text-dark);
line-height: 1.6;
overflow-x: hidden;
}
/* ===== Split-Screen Layout ===== */
.dashboard-container {
display: grid;
grid-template-columns: 350px 1fr;
min-height: 100vh;
gap: 0;
}
/* ===== Left Sidebar (Sticky) ===== */
.sidebar {
background: var(--surface-white);
border-right: 5px solid #1a5d3a;
padding: 2rem 1.5rem;
position: sticky;
top: 0;
height: 100vh;
overflow-y: auto;
}
.sidebar-header {
margin-bottom: 2rem;
}
.sidebar-header h1 {
font-size: 1.5rem;
font-weight: 700;
color: var(--deep-green);
margin-bottom: 0.5rem;
}
.sidebar-header p {
font-size: 0.9rem;
color: var(--text-muted);
font-weight: 300;
}
/* ===== Status Badge ===== */
.status-badge {
padding: 1.25rem;
border-radius: 12px;
text-align: center;
margin-bottom: 1.5rem;
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
border: 2px solid #1a5d3a;
}
.status-badge.on {
background: var(--accent-green);
color: white;
}
.status-badge.off {
background: #dc3545;
color: white;
}
.status-badge i {
font-size: 2rem;
margin-bottom: 0.5rem;
display: block;
}
.status-badge .status-label {
font-size: 0.85rem;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.5px;
opacity: 0.9;
}
.status-badge .status-value {
font-size: 1.5rem;
font-weight: 700;
margin-top: 0.25rem;
}
/* ===== Time Counter ===== */
.time-counter {
background: var(--bg-light);
padding: 1rem;
border-radius: 8px;
text-align: center;
margin-bottom: 1.5rem;
}
.time-counter i {
color: var(--accent-green);
font-size: 1.2rem;
margin-right: 0.5rem;
}
.time-counter span {
font-size: 1.1rem;
font-weight: 600;
color: var(--text-dark);
}
/* ===== Gauge Container ===== */
.gauge-card {
background: var(--surface-white);
border: 2px solid #1a5d3a;
border-radius: 12px;
padding: 1.5rem;
margin-bottom: 1.5rem;
}
.gauge-card h3 {
font-size: 1rem;
font-weight: 600;
color: var(--deep-green);
margin-bottom: 1rem;
text-align: center;
}
#gauge {
width: 100%;
height: 280px;
}
/* ===== Right Panel (Scrollable) ===== */
.content-panel {
padding: 2rem;
overflow-y: auto;
}
/* ===== Diagnostic Timeline (Exclusive to Results Page) ===== */
.diagnostic-timeline {
background: var(--surface-white);
border-radius: 20px;
box-shadow: 0 10px 40px rgba(0,0,0,0.08);
padding: 2.5rem;
margin-bottom: 2rem;
border: 5px dashed #1a5d3a;
}
.timeline-header {
text-align: center;
margin-bottom: 2.5rem;
}
.timeline-header h2 {
font-size: 1.75rem;
font-weight: 700;
color: var(--deep-green);
margin-bottom: 0.5rem;
}
.timeline-header p {
color: var(--text-muted);
font-size: 0.95rem;
}
.timeline-steps {
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
max-width: 700px;
margin: 0 auto;
}
.timeline-step {
flex: 1;
text-align: center;
position: relative;
}
.timeline-icon {
width: 80px;
height: 80px;
background: linear-gradient(135deg, var(--accent-green), var(--deep-green));
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 1rem;
box-shadow: 0 6px 20px rgba(25, 135, 84, 0.3);
font-size: 2rem;
color: white;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
.timeline-label {
font-size: 1rem;
font-weight: 600;
color: var(--text-dark);
margin-bottom: 0.25rem;
}
.timeline-desc {
font-size: 0.85rem;
color: var(--text-muted);
}
.timeline-connector {
position: absolute;
top: 40px;
left: 50%;
width: 100%;
height: 3px;
background: linear-gradient(90deg, var(--accent-green), var(--deep-green));
z-index: -1;
}
.timeline-step:last-child .timeline-connector {
display: none;
}
/* ===== Chart Cards ===== */
.chart-card {
background: var(--surface-white);
border-radius: 20px;
box-shadow: 0 10px 40px rgba(0,0,0,0.08);
padding: 2rem;
margin-bottom: 2rem;
border: 5px dashed #1a5d3a;
}
.chart-card h3 {
font-size: 1.25rem;
font-weight: 600;
color: var(--deep-green);
margin-bottom: 1.5rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.chart-card h3 i {
color: var(--accent-green);
}
.chart-container {
width: 100%;
height: 400px;
}
/* ===== Info Alert ===== */
.info-alert {
background: #e7f3ff;
border-left: 4px solid #0066cc;
padding: 1rem 1.25rem;
border-radius: 8px;
margin-bottom: 2rem;
display: flex;
align-items: start;
gap: 0.75rem;
}
.info-alert i {
color: #0066cc;
font-size: 1.25rem;
margin-top: 0.125rem;
}
.info-alert p {
margin: 0;
color: var(--text-dark);
font-size: 0.9rem;
line-height: 1.5;
}
/* ===== Responsive Design ===== */
@media (max-width: 1024px) {
.dashboard-container {
grid-template-columns: 1fr;
}
.sidebar {
position: relative;
height: auto;
border-right: none;
border-bottom: 1px solid var(--border-color);
}
.timeline-steps {
flex-direction: column;
gap: 2rem;
}
.timeline-connector {
display: none;
}
}
@media (max-width: 768px) {
.content-panel {
padding: 1rem;
}
.diagnostic-timeline {
padding: 1.5rem;
}
.chart-card {
padding: 1.5rem;
}
}
</style>
<script>
let alertSound = new Audio('{{ url_for("static", filename="alarn_tune.mp3") }}');
function playAlertSound() {
alertSound.currentTime = 0;
alertSound.play().catch(error => console.error("Error playing sound:", error));
}
function fetchPumpStatus() {
fetch('/update_pump_status')
.then(response => response.json())
.then(data => {
const statusElement = document.getElementById('pumpStatus');
const statusValue = document.getElementById('statusValue');
const statusIcon = document.getElementById('statusIcon');
statusValue.textContent = data.pump_status;
statusElement.className = data.pump_status === 'On' ? 'status-badge on' : 'status-badge off';
statusIcon.className = data.pump_status === 'On' ? 'bi bi-power' : 'bi bi-exclamation-triangle';
if (data.pump_status === 'Off') {
console.log("Pump status is Off. Playing alert sound.");
playAlertSound();
}
})
.catch(err => console.error("Error fetching pump status:", err));
}
function fetchGraphData() {
fetch('/update_graph')
.then(response => response.json())
.then(data => {
const time = data.map((_, index) => index + 1);
const soilMoisture = data.map(entry => entry[0]);
const pumpStatus = data.map(entry => entry[1]);
// Update Gauge chart with soil moisture
const currentSoilMoisture = soilMoisture[soilMoisture.length - 1];
updateGaugeChart(currentSoilMoisture);
// Pump Status vs. Time
const binaryPumpStatus = pumpStatus.map(status => status);
const trace1 = {
x: time,
y: binaryPumpStatus,
mode: 'lines+markers',
type: 'scatter',
name: 'Pump Status',
line: { color: '#198754', width: 3 },
marker: { size: 8 }
};
const layout1 = {
title: '',
xaxis: { title: 'Time (seconds)', gridcolor: '#e9ecef' },
yaxis: { title: 'Pump Status', tickvals: [-1, 1], ticktext: ['Off', 'On'], range: [-1.5, 1.5], gridcolor: '#e9ecef' },
showlegend: false,
plot_bgcolor: '#f8f9fa',
paper_bgcolor: 'white',
font: { family: 'Outfit, sans-serif' }
};
Plotly.newPlot('graph1', [trace1], layout1, {responsive: true});
// Soil Moisture vs. Time
const trace2 = {
x: time,
y: soilMoisture,
mode: 'lines+markers',
type: 'scatter',
name: 'Soil Moisture',
line: { color: '#1a5d3a', width: 3 },
marker: { size: 8 },
fill: 'tozeroy',
fillcolor: 'rgba(26, 93, 58, 0.1)'
};
const layout2 = {
title: '',
xaxis: { title: 'Time (seconds)', gridcolor: '#e9ecef' },
yaxis: { title: 'Soil Moisture (%)', gridcolor: '#e9ecef' },
showlegend: false,
plot_bgcolor: '#f8f9fa',
paper_bgcolor: 'white',
font: { family: 'Outfit, sans-serif' }
};
Plotly.newPlot('graph2', [trace2], layout2, {responsive: true});
// Combined Graph for Soil Moisture vs. Pump Status
const trace3 = {
x: soilMoisture,
y: binaryPumpStatus,
mode: 'markers+lines',
type: 'scatter',
name: 'Pump Status',
line: { color: '#198754', width: 2 },
marker: { size: 10, color: '#1a5d3a' }
};
const layout3 = {
title: '',
xaxis: { title: 'Soil Moisture (%)', gridcolor: '#e9ecef' },
yaxis: { title: 'Pump Status', tickvals: [-1, 1], ticktext: ['Off', 'On'], range: [-1.5, 1.5], gridcolor: '#e9ecef' },
showlegend: false,
plot_bgcolor: '#f8f9fa',
paper_bgcolor: 'white',
font: { family: 'Outfit, sans-serif' }
};
Plotly.newPlot('graph3', [trace3], layout3, {responsive: true});
// Histogram for Soil Moisture
const trace4 = {
x: soilMoisture,
type: 'histogram',
marker: { color: '#198754' },
nbinsx: 20
};
const layout4 = {
title: '',
xaxis: { title: 'Soil Moisture (%)', gridcolor: '#e9ecef' },
yaxis: { title: 'Frequency', gridcolor: '#e9ecef' },
plot_bgcolor: '#f8f9fa',
paper_bgcolor: 'white',
font: { family: 'Outfit, sans-serif' }
};
Plotly.newPlot('graph4', [trace4], layout4, {responsive: true});
})
.catch(err => console.error("Error fetching graph data:", err));
}
// Function to update the gauge chart
function updateGaugeChart(value) {
const data = [
{
type: "indicator",
mode: "gauge+number",
value: value,
title: { text: "", font: { size: 16, family: 'Outfit, sans-serif' } },
gauge: {
axis: { range: [0, 100], tickfont: { family: 'Outfit, sans-serif' } },
steps: [
{ range: [0, 30], color: "#ffebee" },
{ range: [30, 60], color: "#fff9e6" },
{ range: [60, 100], color: "#e8f5e9" }
],
bar: { color: "#1a5d3a" },
threshold: {
line: { color: "#198754", width: 4 },
thickness: 0.75,
value: value
}
}
}
];
const layout = {
margin: { t: 20, b: 20, l: 20, r: 20 },
paper_bgcolor: 'white',
font: { family: 'Outfit, sans-serif' }
};
Plotly.newPlot('gauge', data, layout, {responsive: true});
}
// Time counter functionality
let timeCounter = 0;
function updateTimeCounter() {
timeCounter++;
document.getElementById('time-counter').textContent = `${timeCounter}s`;
}
// Set intervals for data fetching and time counting
setInterval(fetchPumpStatus, 2000);
setInterval(fetchGraphData, 2000);
setInterval(updateTimeCounter, 1000);
document.addEventListener('click', () => {
alertSound.load();
});
</script>
</head>
<body>
<div class="dashboard-container">
<!-- Left Sidebar (Sticky) -->
<div class="sidebar">
<div class="sidebar-header">
<h1><i class="bi bi-speedometer2"></i> Live Monitor</h1>
<p>Real-time pump analysis</p>
</div>
<!-- Pump Status Badge -->
<div id="pumpStatus" class="status-badge {{ 'on' if pump_status == 'On' else 'off' }}">
<i id="statusIcon" class="bi {{ 'bi-power' if pump_status == 'On' else 'bi-exclamation-triangle' }}"></i>
<div class="status-label">Pump Status</div>
<div class="status-value" id="statusValue">{{ pump_status }}</div>
</div>
<!-- Time Counter -->
<div class="time-counter">
<i class="bi bi-clock"></i>
<span>Elapsed: <strong id="time-counter">0s</strong></span>
</div>
<!-- Soil Moisture Gauge -->
<div class="gauge-card">
<h3><i class="bi bi-moisture"></i> Soil Moisture</h3>
<div id="gauge"></div>
</div>
</div>
<!-- Right Content Panel (Scrollable) -->
<div class="content-panel">
<!-- Diagnostic Timeline (Exclusive to Results Page) -->
<div class="diagnostic-timeline">
<div class="timeline-header">
<h2>Diagnostic Timeline</h2>
<p>AI-powered analysis workflow</p>
</div>
<div class="timeline-steps">
<div class="timeline-step">
<div class="timeline-icon">
<i class="bi bi-database"></i>
</div>
<div class="timeline-label">Collect Data</div>
<div class="timeline-desc">Gathering sensor inputs</div>
<div class="timeline-connector"></div>
</div>
<div class="timeline-step">
<div class="timeline-icon">
<i class="bi bi-cpu"></i>
</div>
<div class="timeline-label">Process Analysis</div>
<div class="timeline-desc">AI model prediction</div>
<div class="timeline-connector"></div>
</div>
<div class="timeline-step">
<div class="timeline-icon">
<i class="bi bi-graph-up"></i>
</div>
<div class="timeline-label">Monitor Results</div>
<div class="timeline-desc">Live status tracking</div>
</div>
</div>
</div>
<!-- Info Alert -->
<div class="info-alert">
<i class="bi bi-info-circle-fill"></i>
<p><strong>Real-time Monitoring:</strong> Charts update every 2 seconds with new predictions. The pump status will automatically adjust based on soil moisture levels and environmental conditions.</p>
</div>
<!-- Chart: Pump Status vs Time -->
<div class="chart-card">
<h3><i class="bi bi-activity"></i> Pump Status Over Time</h3>
<div id="graph1" class="chart-container"></div>
</div>
<!-- Chart: Soil Moisture vs Time -->
<div class="chart-card">
<h3><i class="bi bi-moisture"></i> Soil Moisture Trends</h3>
<div id="graph2" class="chart-container"></div>
</div>
<!-- Chart: Pump Status vs Soil Moisture -->
<div class="chart-card">
<h3><i class="bi bi-diagram-3"></i> Correlation Analysis</h3>
<div id="graph3" class="chart-container"></div>
</div>
<!-- Chart: Soil Moisture Distribution -->
<div class="chart-card">
<h3><i class="bi bi-bar-chart"></i> Moisture Distribution</h3>
<div id="graph4" class="chart-container"></div>
</div>
</div>
</div>
</body>
</html>