Error: A required library is missing.
';
return;
}
// --- DATA & STATE ---
let satelliteState = {
power: { status: 'Nominal', voltage: 28.5, arrayOutput: 155.2 },
thermal: { status: 'Nominal', coreTemp: -5.5 },
comms: { status: 'Nominal', signalStrength: -85.0 },
attitude: { status: 'Nominal', drift: 0.001 }
};
let alerts = [];
let telemetryHistory = { voltage: [], temp: [] };
// --- CHART INSTANCES & UTILITIES ---
let powerChart, thermalChart;
const MAX_HISTORY = 30;
const format = (val, dec, unit) => `${val.toFixed(dec)}${unit}`;
// --- RENDER FUNCTIONS ---
const renderAll = () => {
try {
renderStatus();
renderTelemetry();
renderAlerts();
renderCharts();
} catch(error) { console.error("Dashboard rendering failed:", error); }
};
const renderStatus = () => {
const statuses = [
satelliteState.power.status, satelliteState.thermal.status,
satelliteState.comms.status, satelliteState.attitude.status
];
let overallStatus = 'Nominal';
if (statuses.includes('Critical')) overallStatus = 'Critical';
else if (statuses.includes('Warning')) overallStatus = 'Warning';
const setStatus = (elId, status) => {
const el = document.getElementById(elId);
el.textContent = status;
el.className = `shd-status-indicator status-${status.toLowerCase()}`;
};
setStatus('shd-overall-status', overallStatus);
setStatus('shd-power-status', satelliteState.power.status);
setStatus('shd-thermal-status', satelliteState.thermal.status);
setStatus('shd-comms-status', satelliteState.comms.status);
setStatus('shd-attitude-status', satelliteState.attitude.status);
};
const renderTelemetry = () => {
const container = document.getElementById('shd-telemetry-readouts');
container.innerHTML = `
Battery Voltage${format(satelliteState.power.voltage, 2, 'V')}
Solar Array Output${format(satelliteState.power.arrayOutput, 1, 'W')}
Core Temperature${format(satelliteState.thermal.coreTemp, 1, '°C')}
Signal Strength${format(satelliteState.comms.signalStrength, 1, 'dBm')}
Attitude Drift${format(satelliteState.attitude.drift, 4, '°/s')}
`;
};
const renderAlerts = () => {
const log = document.getElementById('shd-alerts-log');
log.innerHTML = alerts.map(a => `
${a.time}${a.message}`).join('');
};
const renderCharts = () => {
const commonOptions = {
chart: { height: 300, animations: { easing: 'linear', dynamicAnimation: { speed: 1000 } } },
stroke: { curve: 'smooth', width: 2 },
markers: { size: 0 },
xaxis: { type: 'datetime', range: 30 * 1000 }
};
if (!powerChart) {
const powerOptions = { ...commonOptions, series: [{ name: 'Voltage', data: telemetryHistory.voltage }], title: { text: 'Battery Voltage (V)' }, colors: ['var(--shd-accent-color)'] };
powerChart = new ApexCharts(document.querySelector("#shd-power-chart"), powerOptions);
powerChart.render();
} else {
powerChart.updateSeries([{ data: telemetryHistory.voltage }]);
}
if (!thermalChart) {
const thermalOptions = { ...commonOptions, series: [{ name: 'Temperature', data: telemetryHistory.temp }], title: { text: 'Core Temperature (°C)' }, colors: ['var(--shd-warning-color)'] };
thermalChart = new ApexCharts(document.querySelector("#shd-thermal-chart"), thermalOptions);
thermalChart.render();
} else {
thermalChart.updateSeries([{ data: telemetryHistory.temp }]);
}
};
// --- SIMULATION & EVENT HANDLING ---
const addAlert = (type, message) => {
const time = new Date().toLocaleTimeString('en-US', { hour12: false });
alerts.unshift({ time, type, message });
if (alerts.length > 20) alerts.pop();
};
const runSimulation = () => {
// Power sim
satelliteState.power.voltage += (Math.random() - 0.5) * 0.1;
if (satelliteState.power.voltage > 29) satelliteState.power.voltage = 29;
satelliteState.power.status = satelliteState.power.voltage < 26.5 ? 'Warning' : 'Nominal';
// Thermal sim
satelliteState.thermal.coreTemp += (Math.random() - 0.5) * 0.5;
if (satelliteState.thermal.coreTemp > 50 || satelliteState.thermal.coreTemp < -20) satelliteState.thermal.status = 'Critical';
else if (satelliteState.thermal.coreTemp > 40 || satelliteState.thermal.coreTemp < -15) satelliteState.thermal.status = 'Warning';
else satelliteState.thermal.status = 'Nominal';
// Comms sim
if(satelliteState.comms.status !== 'Critical') {
satelliteState.comms.signalStrength += (Math.random() - 0.45);
if(satelliteState.comms.signalStrength > -80) satelliteState.comms.signalStrength = -80;
if(satelliteState.comms.signalStrength < -110) satelliteState.comms.status = 'Critical';
}
// Update history
const now = new Date().getTime();
telemetryHistory.voltage.push([now, satelliteState.power.voltage]);
telemetryHistory.temp.push([now, satelliteState.thermal.coreTemp]);
if (telemetryHistory.voltage.length > MAX_HISTORY) {
telemetryHistory.voltage.shift();
telemetryHistory.temp.shift();
}
renderAll();
};
// Tab Navigation
const tabButtons = document.querySelectorAll('.shd-tab-button');
const tabContents = document.querySelectorAll('.shd-tab-content');
const nextBtn = document.getElementById('shd-next-btn');
const prevBtn = document.getElementById('shd-prev-btn');
const switchTab = (tabId) => {
tabContents.forEach(c => c.style.display = 'none');
tabButtons.forEach(b => b.classList.remove('active'));
const activeContent = document.getElementById(tabId);
const activeButton = document.querySelector(`.shd-tab-button[data-tab="${tabId}"]`);
if (activeContent && activeButton) {
activeContent.style.display = 'block';
activeButton.classList.add('active');
}
updateNavButtons();
};
const updateNavButtons = () => {
const i = [...tabButtons].findIndex(b => b.classList.contains('active'));
prevBtn.disabled = i === 0;
nextBtn.disabled = i === tabButtons.length - 1;
};
tabButtons.forEach(b => b.addEventListener('click', () => switchTab(b.dataset.tab)));
nextBtn.addEventListener('click', () => { const i = [...tabButtons].findIndex(b=>b.classList.contains('active')); if (i < tabButtons.length - 1) switchTab(tabButtons[i+1].dataset.tab); });
prevBtn.addEventListener('click', () => { const i = [...tabButtons].findIndex(b=>b.classList.contains('active')); if (i > 0) switchTab(tabButtons[i-1].dataset.tab); });
// Simulation controls
document.querySelector('.shd-grid').addEventListener('click', e => {
if(e.target.tagName !== 'BUTTON') return;
const event = e.target.dataset.event;
if(event === 'power-warn') {
satelliteState.power.voltage = 26.2;
addAlert('warning', 'Battery voltage dropping below threshold.');
} else if (event === 'temp-crit') {
satelliteState.thermal.coreTemp = 55.0;
addAlert('critical', 'Core temperature exceeds critical limits!');
} else if (event === 'comms-loss') {
satelliteState.comms.status = 'Critical';
satelliteState.comms.signalStrength = -120;
addAlert('critical', 'AOS - Acquisition of Signal: FAILED');
} else if (event === 'normalize') {
satelliteState = {
power: { status: 'Nominal', voltage: 28.5, arrayOutput: 155.2 },
thermal: { status: 'Nominal', coreTemp: -5.5 },
comms: { status: 'Nominal', signalStrength: -85.0 },
attitude: { status: 'Nominal', drift: 0.001 }
};
addAlert('nominal', 'All systems returned to nominal state.');
}
});
// PDF Export
document.getElementById('shd-download-pdf-btn').addEventListener('click', function() {
const btn = this; btn.textContent = 'Generating...'; btn.disabled = true;
const content = document.getElementById('shd-pdf-capture-area');
html2canvas(content, { scale: 2, backgroundColor: getComputedStyle(document.documentElement).getPropertyValue('--shd-bg-color') }).then(canvas => {
const doc = new jsPDF();
const imgData = canvas.toDataURL('image/png');
doc.setFontSize(18);
doc.text('Satellite Status Report: Pathfinder-1', 14, 22);
doc.setFontSize(11);
doc.setTextColor(100);
doc.text(`Generated on: ${new Date().toUTCString()}`, 14, 29);
const imgWidth = doc.internal.pageSize.getWidth() - 28;
const imgHeight = (canvas.height * imgWidth) / canvas.width;
doc.addImage(imgData, 'PNG', 14, 40, imgWidth, imgHeight);
doc.save('Satellite_Status_Report.pdf');
}).finally(() => {
btn.textContent = 'Download Status Report'; btn.disabled = false;
});
});
// --- INITIALIZATION ---
setInterval(runSimulation, 2000);
setInterval(() => {
document.getElementById('shd-utc-time').textContent = new Date().toUTCString().slice(17, 25) + ' UTC';
}, 1000);
renderAll();
});