34987-vm/federated.php
2025-10-15 16:26:32 +00:00

175 lines
6.1 KiB
PHP

<?php
$page_title = "Federated Learning";
include 'includes/header.php';
?>
<style>
.federated-container {
position: relative;
width: 100%;
height: 400px;
display: flex;
justify-content: center;
align-items: center;
}
.fl-node {
position: absolute;
width: 120px;
height: 120px;
border-radius: 50%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
padding: 10px;
font-size: 0.9rem;
box-shadow: var(--shadow-sm);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.fl-node.central {
width: 160px;
height: 160px;
background-color: var(--primary);
color: white;
z-index: 10;
font-size: 1.1rem;
}
.fl-node.local {
background-color: var(--card-bg);
color: var(--text-primary);
}
.fl-line {
position: absolute;
background-color: #ccc;
z-index: 1;
transform-origin: 0 0;
transition: background-color 0.5s ease;
}
.fl-line.animated {
background-color: var(--accent);
}
</style>
<div class="container mt-5">
<h1 class="page-title">Federated Learning Simulation</h1>
<p class="lead mb-5">Visualizing how distributed learning improves the national model without sharing sensitive data.</p>
<div class="row g-4">
<div class="col-lg-8">
<div class="card h-100">
<div class="card-body">
<div id="federated-visualization" class="federated-container"></div>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card h-100">
<div class="card-header">
<h5 class="mb-0">Simulation Log</h5>
</div>
<div id="log-container" class="card-body" style="max-height: 350px; overflow-y: auto; font-family: 'Roboto Mono', monospace; font-size: 0.85rem;">
<p class="text-muted">Click the button to start the simulation...</p>
</div>
<div class="card-footer text-center">
<button id="trigger-round" class="btn btn-primary">Trigger New Aggregation Round</button>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const container = document.getElementById('federated-visualization');
const logContainer = document.getElementById('log-container');
const triggerButton = document.getElementById('trigger-round');
let round = 0;
const nodes = [
{ id: 'central', name: 'National Model', isCentral: true },
{ id: 'node1', name: 'Mumbai Substation', angle: 0 },
{ id: 'node2', name: 'Delhi Substation', angle: 72 },
{ id: 'node3', name: 'Chennai Substation', angle: 144 },
{ id: 'node4', name: 'Kolkata Substation', angle: 216 },
{ id: 'node5', name: 'Bangalore Substation', angle: 288 },
];
const radius = 150;
const containerWidth = container.offsetWidth;
const containerHeight = container.offsetHeight;
const centerX = containerWidth / 2;
const centerY = containerHeight / 2;
// Create nodes and lines
nodes.forEach(node => {
const nodeEl = document.createElement('div');
nodeEl.id = node.id;
nodeEl.className = `fl-node ${node.isCentral ? 'central' : 'local'}`;
nodeEl.innerHTML = `<strong>${node.name}</strong>`;
if (node.isCentral) {
nodeEl.style.left = `${centerX - 80}px`;
nodeEl.style.top = `${centerY - 80}px`;
} else {
const x = centerX + radius * Math.cos(node.angle * Math.PI / 180) - 60;
const y = centerY + radius * Math.sin(node.angle * Math.PI / 180) - 60;
nodeEl.style.left = `${x}px`;
nodeEl.style.top = `${y}px`;
// Draw line from local to central
const line = document.createElement('div');
line.id = `line-${node.id}`;
line.className = 'fl-line';
const angle = Math.atan2(y - centerY, x - centerX) * 180 / Math.PI;
const length = Math.sqrt(Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2));
line.style.width = `${length}px`;
line.style.height = '2px';
line.style.left = `${centerX}px`;
line.style.top = `${centerY}px`;
line.style.transform = `rotate(${angle}deg)`;
container.appendChild(line);
}
container.appendChild(nodeEl);
});
function log(message, type = 'info') {
const colors = { info: 'text-secondary', success: 'text-success', emphasis: 'text-primary' };
logContainer.innerHTML += `<p class="mb-1 ${colors[type]}">[${new Date().toLocaleTimeString()}] ${message}</p>`;
logContainer.scrollTop = logContainer.scrollHeight;
}
triggerButton.addEventListener('click', function() {
if (round === 0) {
logContainer.innerHTML = '';
}
round++;
log(`Starting Aggregation Round ${round}...`, 'emphasis');
this.disabled = true;
let delay = 500;
nodes.filter(n => !n.isCentral).forEach(node => {
setTimeout(() => {
log(`Node ${node.name} sending model updates...`);
document.getElementById(`line-${node.id}`).classList.add('animated');
}, delay);
delay += 300;
});
setTimeout(() => {
log('All updates received. Aggregating national model...');
const centralNode = document.getElementById('central');
centralNode.style.transform = 'scale(1.1)';
}, delay + 500);
setTimeout(() => {
log(`Round ${round} complete. National model updated.`, 'success');
document.getElementById('central').style.transform = 'scale(1)';
nodes.filter(n => !n.isCentral).forEach(node => {
document.getElementById(`line-${node.id}`).classList.remove('animated');
});
this.disabled = false;
}, delay + 1500);
});
});
</script>
<?php include 'includes/footer.php'; ?>