anujjj321's picture
deployed the files
9875454 verified
Raw
History Blame Contribute Delete
40.2 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AERO|PULSE Dashboard</title>
<!-- Use 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=Inter:wght@300;400;600;800&family=Orbitron:wght@500;700&display=swap" rel="stylesheet">
<!-- Chart.js for graphs -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
:root {
--bg-color: #0b1120;
--panel-bg: rgba(20, 30, 50, 0.6);
--border-color: rgba(60, 90, 130, 0.4);
--text-main: #e2e8f0;
--text-muted: #94a3b8;
--accent-blue: #38bdf8;
--accent-green: #34d399;
--accent-warning: #fbbf24;
--accent-danger: #f87171;
--glow: 0 0 15px rgba(56, 189, 248, 0.4);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Inter', sans-serif;
background-color: var(--bg-color);
color: var(--text-main);
display: flex;
height: 100vh;
overflow: hidden;
position: relative;
}
.engine-bg {
position: fixed;
top: 0;
left: 250px; /* offset by sidebar */
width: calc(100vw - 250px);
height: 100vh;
background-image: url('engine_bg.png');
background-size: cover;
background-position: top left;
z-index: -1;
opacity: 0.4;
mask-image: radial-gradient(circle at top left, rgba(0,0,0,1) 0%, rgba(0,0,0,0) 65%);
-webkit-mask-image: radial-gradient(circle at top left, rgba(0,0,0,1) 0%, rgba(0,0,0,0) 65%);
pointer-events: none;
}
/* Sidebar */
.sidebar {
width: 240px;
background: rgba(10, 15, 30, 0.8);
border-right: 1px solid var(--border-color);
padding: 2rem 1rem;
display: flex;
flex-direction: column;
gap: 2rem;
backdrop-filter: blur(10px);
}
.logo {
font-family: 'Orbitron', sans-serif;
font-size: 1.5rem;
font-weight: 700;
color: var(--text-main);
letter-spacing: 2px;
display: flex;
align-items: center;
gap: 8px;
}
.logo span { color: var(--accent-blue); text-shadow: var(--glow); }
.nav-item {
padding: 0.8rem 1rem;
border-radius: 8px;
cursor: pointer;
color: var(--text-muted);
transition: all 0.3s ease;
font-weight: 600;
}
.nav-item:hover, .nav-item.active {
background: rgba(56, 189, 248, 0.1);
color: var(--accent-blue);
}
/* Main Content */
.main-content {
flex: 1;
padding: 2rem;
display: flex;
flex-direction: column;
gap: 1.5rem;
overflow-y: auto;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 1rem;
border-bottom: 1px solid var(--border-color);
}
.status-badge {
display: inline-flex;
align-items: center;
gap: 8px;
background: rgba(52, 211, 153, 0.1);
color: var(--accent-green);
padding: 0.5rem 1rem;
border-radius: 20px;
font-size: 0.875rem;
font-weight: 600;
border: 1px solid rgba(52, 211, 153, 0.3);
}
.status-badge .dot {
width: 8px; height: 8px;
background: var(--accent-green);
border-radius: 50%;
box-shadow: 0 0 10px var(--accent-green);
}
/* Grid Layout */
.dashboard-grid {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: auto auto;
gap: 1.5rem;
height: 100%;
}
.panel {
background: var(--panel-bg);
border: 1px solid var(--border-color);
border-radius: 16px;
padding: 1.5rem;
backdrop-filter: blur(12px);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
display: flex;
flex-direction: column;
}
.panel-title {
font-size: 0.875rem;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 1rem;
font-weight: 600;
}
/* RUL Gauge Area */
.rul-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex: 1;
}
.gauge {
position: relative;
width: 250px;
height: 250px;
border-radius: 50%;
background: conic-gradient(var(--accent-warning) 0% 78%, rgba(255,255,255,0.05) 78% 100%);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 30px rgba(251, 191, 36, 0.2);
margin-bottom: 1rem;
}
.gauge::before {
content: '';
position: absolute;
width: 210px; height: 210px;
background: var(--bg-color);
border-radius: 50%;
box-shadow: inset 0 0 20px rgba(0,0,0,0.8);
}
.gauge-content {
position: relative;
z-index: 1;
text-align: center;
}
.gauge-value {
font-size: 3.5rem;
font-family: 'Orbitron', sans-serif;
font-weight: 700;
background: linear-gradient(180deg, #fff, #fbbf24);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
line-height: 1.1;
}
.gauge-label { color: var(--text-muted); font-size: 0.875rem; }
.engine-selector {
background: rgba(10, 15, 30, 0.6);
color: #fff;
border: 1px solid var(--border-color);
padding: 0.85rem 1.25rem;
border-radius: 8px;
font-size: 1rem;
font-weight: 500;
margin-bottom: 0.75rem;
width: 100%;
cursor: pointer;
outline: none;
transition: all 0.3s ease;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
background-image: url("data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%2338bdf8%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E");
background-repeat: no-repeat;
background-position: right 1rem top 50%;
background-size: 0.8rem auto;
backdrop-filter: blur(10px);
box-shadow: inset 0 1px 3px rgba(0,0,0,0.3), 0 2px 4px rgba(0,0,0,0.1);
}
.engine-selector:hover {
border-color: var(--accent-blue);
background-color: rgba(15, 25, 45, 0.8);
box-shadow: 0 0 0 2px rgba(56, 189, 248, 0.2);
transform: translateY(-1px);
}
.engine-selector:focus {
border-color: var(--accent-blue);
}
.engine-selector option {
background-color: #0f172a;
color: #fff;
}
.engine-stats {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
width: 100%;
gap: 1rem;
margin-top: 2rem;
border-top: 1px solid var(--border-color);
padding-top: 1rem;
text-align: center;
}
.stat-val { font-size: 1.25rem; font-weight: 600; color: #fff; }
.stat-title { font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; }
/* Charts */
.chart-container {
position: relative;
flex: 1;
width: 100%;
min-height: 200px;
}
.alerts-panel {
grid-row: span 2;
}
.alert-item {
background: rgba(248, 113, 113, 0.1);
border-left: 4px solid var(--accent-danger);
padding: 1rem;
border-radius: 4px;
margin-bottom: 1rem;
}
.alert-title { color: var(--accent-danger); font-weight: 600; font-size: 0.875rem; }
.alert-desc { color: var(--text-muted); font-size: 0.75rem; margin-top: 4px; }
/* Views */
.view { display: none; height: 100%; }
.view.active { display: block; }
/* Fleet Table */
.fleet-table-container {
background: var(--panel-bg);
border: 1px solid var(--border-color);
border-radius: 16px;
padding: 1.5rem;
margin-top: 1.5rem;
overflow-y: auto;
max-height: calc(100vh - 120px);
}
.fleet-table {
width: 100%;
border-collapse: collapse;
text-align: left;
}
.fleet-table th {
color: var(--text-muted);
font-size: 0.75rem;
text-transform: uppercase;
padding: 1rem;
border-bottom: 1px solid var(--border-color);
position: sticky;
top: 0;
background: rgba(10, 15, 30, 0.95);
z-index: 2;
}
.fleet-table td {
padding: 1rem;
border-bottom: 1px solid rgba(60, 90, 130, 0.2);
font-size: 0.875rem;
}
.fleet-table tr:hover {
background: rgba(56, 189, 248, 0.05);
}
.status-pill {
padding: 4px 12px;
border-radius: 20px;
font-size: 0.75rem;
font-weight: 600;
display: inline-block;
}
</style>
</head>
<body>
<div class="engine-bg"></div>
<div class="sidebar">
<div class="logo">AERO<span>|</span>PULSE</div>
<div class="nav-item active" onclick="switchView('dashboard', this)">Dashboard</div>
<div class="nav-item" onclick="switchView('analysis', this)">Analysis</div>
<div class="nav-item" onclick="switchView('engines', this)">Engines</div>
<div class="nav-item" onclick="switchView('maintenance', this)">Maintenance</div>
<div class="nav-item" onclick="switchView('alerts', this)">Alerts</div>
</div>
<div class="main-content">
<div class="header">
<div class="status-badge"><div class="dot"></div> System Status: Healthy</div>
<div style="color: var(--text-muted); font-size: 0.875rem;">LSTM Predictive Maintenance API</div>
</div>
<div id="view-dashboard" class="view active">
<div class="dashboard-grid">
<!-- RUL Panel -->
<div class="panel">
<div class="panel-title">Remaining Useful Life (RUL)</div>
<div class="rul-container">
<div class="gauge" id="rulGauge">
<div class="gauge-content">
<div class="gauge-value" id="rulValue">--</div>
<div class="gauge-label">Cycles Remaining</div>
</div>
</div>
<div style="text-align: center; margin-bottom: 2rem; width: 90%; margin-left: auto; margin-right: auto;">
<select id="engineSelect" class="engine-selector" onchange="changeEngine()">
<!-- Options injected by JS -->
</select>
<p style="color: var(--text-muted); font-size: 0.75rem; letter-spacing: 0.5px; text-transform: uppercase;">NASA CMAPSS Simulation</p>
</div>
<div class="engine-stats">
<div>
<div class="stat-val" id="flightCycles">0</div>
<div class="stat-title">Flight Cycles</div>
</div>
<div>
<div class="stat-val" style="color: var(--accent-green);" id="engineStatus">OPERATIONAL</div>
<div class="stat-title">Status</div>
</div>
<div>
<button onclick="fetchEngineData()" style="background: var(--accent-blue); color: #fff; border: none; padding: 0.5rem 1rem; border-radius: 4px; cursor: pointer; font-weight: 600;">Refresh Data</button>
</div>
</div>
</div>
</div>
<!-- EGT Sensor Chart -->
<div class="panel">
<div class="panel-title">SENSOR TELEMETRY - Sensor 11 (EGT)</div>
<div class="chart-container">
<canvas id="egtChart"></canvas>
</div>
</div>
<!-- RPM Sensor Chart -->
<div class="panel">
<div class="panel-title">SENSOR TELEMETRY - Sensor 14 (Core Speed)</div>
<div class="chart-container">
<canvas id="rpmChart"></canvas>
</div>
</div>
<!-- Alerts -->
<div class="panel alerts-panel">
<div class="panel-title">Active Alerts</div>
<div id="alertsContainer">
<p style="color: var(--text-muted); font-size: 0.875rem;">No critical alerts currently.</p>
</div>
</div>
</div>
</div>
<div id="view-analysis" class="view">
<h2 style="margin-bottom: 0.5rem; color: #fff;">Fleet Analysis & Diagnostics</h2>
<p style="color: var(--text-muted); font-size: 0.875rem; margin-bottom: 1.5rem;">Macro-level overview of RR Trent XWB predictive health models.</p>
<div class="dashboard-grid">
<!-- Fleet Health Distribution Chart -->
<div class="panel" style="grid-column: span 2;">
<div class="panel-title">Fleet RUL Distribution</div>
<div class="chart-container" style="height: 300px;">
<canvas id="analysisChart"></canvas>
</div>
</div>
<!-- Overall Fleet Average RUL Gauge -->
<div class="panel">
<div class="panel-title">Average Fleet RUL</div>
<div class="rul-container" style="margin-top: 1rem;">
<div class="gauge" id="analysisGauge">
<div class="gauge-content">
<div class="gauge-value" id="analysisGaugeValue">--</div>
<div class="gauge-label">Avg. Cycles</div>
</div>
</div>
<div class="engine-stats" style="margin-top: 2rem;">
<div>
<div class="stat-val" id="statCritical" style="color: var(--accent-danger);">0</div>
<div class="stat-title" style="font-size: 0.7rem;">Critical</div>
</div>
<div>
<div class="stat-val" id="statMaintenance" style="color: var(--accent-warning);">0</div>
<div class="stat-title" style="font-size: 0.7rem;">Maintenance</div>
</div>
<div>
<div class="stat-val" id="statOperational" style="color: var(--accent-green);">0</div>
<div class="stat-title" style="font-size: 0.7rem;">Operational</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="view-engines" class="view">
<h2 style="margin-bottom: 0.5rem; color: #fff;">Fleet Overview</h2>
<p style="color: var(--text-muted); font-size: 0.875rem;">Real-time health status of all 100 RR Trent XWB engines</p>
<div class="fleet-table-container">
<table class="fleet-table">
<thead>
<tr>
<th>Engine ID</th>
<th>Current Cycles</th>
<th>Predicted RUL</th>
<th>Health Status</th>
<th>Action</th>
</tr>
</thead>
<tbody id="fleetTableBody">
<!-- Populated by JS -->
</tbody>
</table>
</div>
</div>
<div id="view-maintenance" class="view">
<h2 style="margin-bottom: 0.5rem; color: #fff;">Maintenance Work Orders</h2>
<p style="color: var(--text-muted); font-size: 0.875rem; margin-bottom: 1.5rem;">Manage and dispatch repair crews for degraded engines.</p>
<div class="dashboard-grid">
<div class="panel" style="grid-column: span 1;">
<div class="panel-title" style="color: var(--accent-danger);">High Priority (Ground Immediate)</div>
<div id="maintenanceCriticalList" style="margin-top: 1rem;">
<!-- JS injected -->
</div>
</div>
<div class="panel" style="grid-column: span 1;">
<div class="panel-title" style="color: var(--accent-warning);">Scheduled Maintenance (Due Soon)</div>
<div id="maintenanceWarningList" style="margin-top: 1rem;">
<!-- JS injected -->
</div>
</div>
</div>
</div>
<div id="view-alerts" class="view">
<h2 style="margin-bottom: 0.5rem; color: #fff;">System Alerts Log</h2>
<p style="color: var(--text-muted); font-size: 0.875rem; margin-bottom: 1.5rem;">Chronological history of all automated RUL warnings.</p>
<div class="fleet-table-container" style="background: transparent; border: none; padding: 0; margin-top: 0;">
<div id="alertsLogContainer" style="display: flex; flex-direction: column; gap: 1rem;">
<!-- JS Injected -->
</div>
</div>
</div>
</div>
<script>
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false }
},
scales: {
x: { display: false },
y: {
grid: { color: 'rgba(60, 90, 130, 0.05)' },
border: { display: false },
ticks: { color: '#94a3b8' }
}
},
elements: {
point: { radius: 0 }
}
};
function createChart(ctx, color, label) {
// Gradient fill
let gradient = ctx.createLinearGradient(0, 0, 0, 300);
gradient.addColorStop(0, color + '22');
gradient.addColorStop(1, color + '00');
return new Chart(ctx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: label,
borderColor: color,
backgroundColor: gradient,
borderWidth: 2,
tension: 0.4,
fill: true
}]
},
options: chartOptions
});
}
// Initialize Charts
const egtChart = createChart(document.getElementById('egtChart').getContext('2d'), '#f97316', 'Temperature');
const rpmChart = createChart(document.getElementById('rpmChart').getContext('2d'), '#38bdf8', 'RPM');
let analysisChart = new Chart(document.getElementById('analysisChart'), {
type: 'bar',
data: {
labels: ['Critical (<20)', 'Maintenance Due (20-50)', 'Operational (>50)'],
datasets: [{
label: 'Number of Engines',
data: [0, 0, 0],
backgroundColor: [
'rgba(248, 113, 113, 0.8)',
'rgba(251, 191, 36, 0.8)',
'rgba(52, 211, 153, 0.8)'
],
borderRadius: 4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { display: false } },
scales: {
y: { beginAtZero: true, grid: { color: 'rgba(60, 90, 130, 0.1)' } },
x: { grid: { display: false } }
}
}
});
let currentCycle = 0;
let currentEngineId = 1;
// Populate engine selector
const engineSelect = document.getElementById('engineSelect');
for(let i=1; i<=100; i++) {
const opt = document.createElement('option');
opt.value = i;
opt.innerText = `Engine #${i} - RR Trent XWB`;
engineSelect.appendChild(opt);
}
function changeEngine() {
currentEngineId = document.getElementById('engineSelect').value;
currentCycle = 0;
document.getElementById('flightCycles').innerText = currentCycle;
document.getElementById('rulValue').innerText = '--';
document.getElementById('engineStatus').innerText = 'OPERATIONAL';
document.getElementById('engineStatus').style.color = '#34d399';
// Reset charts visually
[egtChart, rpmChart].forEach(chart => {
chart.data.datasets[0].data = [];
chart.update();
});
fetchEngineData();
}
// Real prediction fetching from FastAPI backend
async function fetchEngineData() {
try {
const response = await fetch(`/engine/${currentEngineId}/history`);
if (response.ok) {
const data = await response.json();
document.getElementById('flightCycles').innerText = data.current_cycle;
updateDashboard(data.predicted_rul);
// Populate charts with actual NASA test data
egtChart.data.labels = data.history.cycles;
egtChart.data.datasets[0].data = data.history.sensor_11;
egtChart.update();
rpmChart.data.labels = data.history.cycles;
rpmChart.data.datasets[0].data = data.history.sensor_14;
rpmChart.update();
} else {
console.error("Failed to load engine data");
}
} catch (e) {
console.error("Backend offline", e);
}
}
function updateDashboard(rul) {
const rulValue = document.getElementById('rulValue');
const gauge = document.getElementById('rulGauge');
const status = document.getElementById('engineStatus');
const alerts = document.getElementById('alertsContainer');
rul = Math.round(rul);
rulValue.innerText = rul;
// Calculate percentage assuming max ~200
let pct = Math.min((rul / 200) * 100, 100);
// Color logic
let color = '#34d399'; // Green
if (rul < 50) color = '#fbbf24'; // Yellow
if (rul < 20) color = '#f87171'; // Red
gauge.style.background = `conic-gradient(${color} 0% ${pct}%, rgba(255,255,255,0.05) ${pct}% 100%)`;
rulValue.style.background = `linear-gradient(180deg, #fff, ${color})`;
rulValue.style.webkitBackgroundClip = 'text';
if (rul >= 50) {
status.innerText = 'OPERATIONAL';
status.style.color = '#34d399';
alerts.innerHTML = '<p style="color: var(--text-muted); font-size: 0.875rem;">No critical alerts currently.</p>';
} else if (rul >= 20) {
status.innerText = 'MAINTENANCE DUE';
status.style.color = '#fbbf24';
alerts.innerHTML = `
<div class="alert-item" style="border-left-color: #fbbf24; background: rgba(251, 191, 36, 0.1);">
<div class="alert-title" style="color: #fbbf24;">Maintenance Warning</div>
<div class="alert-desc">RUL has dropped below 50 cycles. Schedule inspection.</div>
</div>`;
} else {
status.innerText = 'CRITICAL FAILING';
status.style.color = '#f87171';
alerts.innerHTML = `
<div class="alert-item">
<div class="alert-title">CRITICAL ALERT</div>
<div class="alert-desc">Engine failure imminent. RUL < 20 cycles. Ground aircraft immediately.</div>
</div>`;
}
// Charts are now updated directly from fetchEngineData()
}
// View Switching & Fleet Generation
function switchView(viewId, element) {
document.querySelectorAll('.view').forEach(v => v.classList.remove('active'));
document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
document.getElementById('view-' + viewId).classList.add('active');
element.classList.add('active');
if (viewId === 'engines') {
generateFleetTable();
} else if (viewId === 'analysis') {
generateAnalysisView();
} else if (viewId === 'maintenance') {
generateMaintenanceView();
} else if (viewId === 'alerts') {
generateAlertsView();
}
}
async function generateAnalysisView() {
try {
const response = await fetch('/fleet');
if (response.ok) {
const engines = await response.json();
let critical = 0;
let warning = 0;
let operational = 0;
let totalRul = 0;
engines.forEach(eng => {
let rul = Math.round(eng.rul);
totalRul += rul;
if (rul < 20) critical++;
else if (rul <= 50) warning++;
else operational++;
});
// Update Stats
document.getElementById('statCritical').innerText = critical;
document.getElementById('statMaintenance').innerText = warning;
document.getElementById('statOperational').innerText = operational;
// Update Gauge
let avgRul = Math.round(totalRul / engines.length);
document.getElementById('analysisGaugeValue').innerText = avgRul;
const gauge = document.getElementById('analysisGauge');
if (avgRul > 50) {
gauge.style.background = `conic-gradient(var(--accent-green) ${(avgRul/150)*100}%, rgba(255,255,255,0.05) 0)`;
document.getElementById('analysisGaugeValue').style.color = 'var(--accent-green)';
} else if (avgRul > 20) {
gauge.style.background = `conic-gradient(var(--accent-warning) ${(avgRul/150)*100}%, rgba(255,255,255,0.05) 0)`;
document.getElementById('analysisGaugeValue').style.color = 'var(--accent-warning)';
} else {
gauge.style.background = `conic-gradient(var(--accent-danger) ${(avgRul/150)*100}%, rgba(255,255,255,0.05) 0)`;
document.getElementById('analysisGaugeValue').style.color = 'var(--accent-danger)';
}
// Update Chart
analysisChart.data.datasets[0].data = [critical, warning, operational];
analysisChart.update();
}
} catch (e) {
console.error("Failed to load analysis data", e);
}
}
async function generateFleetTable() {
const tbody = document.getElementById('fleetTableBody');
tbody.innerHTML = '<tr><td colspan="5" style="text-align: center; color: var(--text-muted); padding: 2rem;">Analyzing all 100 engines...</td></tr>';
try {
const response = await fetch('/fleet');
if (response.ok) {
const engines = await response.json();
tbody.innerHTML = '';
engines.forEach(eng => {
let statusHtml = '';
let rul = Math.round(eng.rul);
if (rul >= 50) {
statusHtml = `<span class="status-pill" style="background: rgba(52, 211, 153, 0.1); color: var(--accent-green); border: 1px solid var(--accent-green);">OPERATIONAL</span>`;
} else if (rul >= 20) {
statusHtml = `<span class="status-pill" style="background: rgba(251, 191, 36, 0.1); color: var(--accent-warning); border: 1px solid var(--accent-warning);">MAINTENANCE DUE</span>`;
} else {
statusHtml = `<span class="status-pill" style="background: rgba(248, 113, 113, 0.1); color: var(--accent-danger); border: 1px solid var(--accent-danger);">CRITICAL FAILING</span>`;
}
const tr = document.createElement('tr');
tr.innerHTML = `
<td style="font-weight: 600;">Engine #${eng.id}</td>
<td>${eng.cycles}</td>
<td style="font-family: 'Orbitron', sans-serif;">${rul} cycles</td>
<td>${statusHtml}</td>
<td>
<button onclick="inspectEngine(${eng.id})" style="background: transparent; color: var(--accent-blue); border: 1px solid var(--accent-blue); padding: 4px 8px; border-radius: 4px; cursor: pointer; font-size: 0.75rem;">Inspect</button>
</td>
`;
tbody.appendChild(tr);
});
}
} catch (e) {
console.error("Failed to load fleet data", e);
}
}
function inspectEngine(id) {
// Jump back to dashboard and select this engine
document.getElementById('engineSelect').value = id;
changeEngine();
switchView('dashboard', document.querySelector('.nav-item')); // Selects first nav item
}
async function generateMaintenanceView() {
document.getElementById('maintenanceCriticalList').innerHTML = '<p style="color: var(--text-muted);">Loading...</p>';
document.getElementById('maintenanceWarningList').innerHTML = '<p style="color: var(--text-muted);">Loading...</p>';
try {
const response = await fetch('/fleet');
if (response.ok) {
const engines = await response.json();
let critHtml = '';
let warnHtml = '';
engines.forEach(eng => {
let rul = Math.round(eng.rul);
if (rul < 20) {
critHtml += `
<div style="background: rgba(248, 113, 113, 0.05); border: 1px solid var(--accent-danger); border-radius: 8px; padding: 1rem; margin-bottom: 1rem;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem;">
<strong style="color: var(--accent-danger); font-size: 1.1rem;">Engine #${eng.id}</strong>
<span style="font-family: 'Orbitron', sans-serif; font-size: 0.875rem; color: #fff;">RUL: ${rul}</span>
</div>
<p style="font-size: 0.75rem; color: var(--text-muted); margin-bottom: 1rem;">Requires immediate HPC inspection. High risk of failure.</p>
<button style="background: var(--accent-danger); color: #fff; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 0.75rem; font-weight: 600;">Dispatch Crew</button>
</div>
`;
} else if (rul <= 50) {
warnHtml += `
<div style="background: rgba(251, 191, 36, 0.05); border: 1px solid rgba(251, 191, 36, 0.3); border-radius: 8px; padding: 1rem; margin-bottom: 1rem;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem;">
<strong style="color: var(--accent-warning); font-size: 1.1rem;">Engine #${eng.id}</strong>
<span style="font-family: 'Orbitron', sans-serif; font-size: 0.875rem; color: #fff;">RUL: ${rul}</span>
</div>
<p style="font-size: 0.75rem; color: var(--text-muted); margin-bottom: 1rem;">Schedule routine maintenance within ${rul} flights.</p>
<button style="background: transparent; color: var(--accent-warning); border: 1px solid var(--accent-warning); padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 0.75rem; font-weight: 600;">Schedule Task</button>
</div>
`;
}
});
document.getElementById('maintenanceCriticalList').innerHTML = critHtml || '<p style="color: var(--text-muted); font-size: 0.875rem;">No critical work orders.</p>';
document.getElementById('maintenanceWarningList').innerHTML = warnHtml || '<p style="color: var(--text-muted); font-size: 0.875rem;">No scheduled maintenance.</p>';
}
} catch (e) {
console.error("Failed to load maintenance data", e);
}
}
async function generateAlertsView() {
const container = document.getElementById('alertsLogContainer');
container.innerHTML = '<p style="color: var(--text-muted);">Loading system logs...</p>';
try {
const response = await fetch('/fleet');
if (response.ok) {
const engines = await response.json();
let logHtml = '';
// Sort engines by worst first to show critical alerts at top
engines.sort((a, b) => a.rul - b.rul);
engines.forEach(eng => {
let rul = Math.round(eng.rul);
let timeOffset = Math.floor(Math.random() * 60); // Mock timestamp offset
if (rul < 20) {
logHtml += `
<div style="background: var(--panel-bg); border-left: 4px solid var(--accent-danger); padding: 1.25rem; border-radius: 4px; display: flex; justify-content: space-between; align-items: center; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
<div>
<div style="color: var(--accent-danger); font-weight: 600; margin-bottom: 4px;">CRITICAL: Engine #${eng.id} Remaining Useful Life breached 20 cycles</div>
<div style="color: var(--text-muted); font-size: 0.875rem;">Automated grounding protocol recommended. Estimated RUL: <span style="color:#fff; font-weight:600">${rul}</span> cycles.</div>
</div>
<div style="color: var(--text-muted); font-size: 0.75rem; min-width: 80px; text-align: right;">${timeOffset} mins ago</div>
</div>
`;
} else if (rul <= 50) {
logHtml += `
<div style="background: var(--panel-bg); border-left: 4px solid var(--accent-warning); padding: 1.25rem; border-radius: 4px; display: flex; justify-content: space-between; align-items: center; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
<div>
<div style="color: var(--accent-warning); font-weight: 600; margin-bottom: 4px;">WARNING: Engine #${eng.id} degraded below operational threshold</div>
<div style="color: var(--text-muted); font-size: 0.875rem;">Maintenance scheduling required. Estimated RUL: <span style="color:#fff; font-weight:600">${rul}</span> cycles.</div>
</div>
<div style="color: var(--text-muted); font-size: 0.75rem; min-width: 80px; text-align: right;">${timeOffset + 60} mins ago</div>
</div>
`;
}
});
container.innerHTML = logHtml || '<p style="color: var(--text-muted);">No system alerts. Fleet is healthy.</p>';
}
} catch (e) {
console.error("Failed to load alerts data", e);
}
}
// Auto-load Engine 1 on startup
window.onload = () => {
fetchEngineData();
};
</script>
</body>
</html>