Spaces:
Running
Running
Fill in Health Score, Fault Probabilities, Energy ledger, Latency and Cost and Recent Alerts with template data
Browse files- index.html +62 -26
index.html
CHANGED
|
@@ -169,38 +169,74 @@ function setTheme(darkMode){
|
|
| 169 |
setTheme(dark);
|
| 170 |
document.getElementById('themeBtn').onclick = ()=>setTheme(!body.classList.contains('theme-dark'));
|
| 171 |
/* DEMO DATA GENERATION */
|
| 172 |
-
let lastHealth=0.
|
| 173 |
const dataHealth = Array(15*60).fill().map((_,i)=>{
|
| 174 |
const x = Date.now()-i*1000;
|
| 175 |
-
// Simulate
|
| 176 |
-
const
|
| 177 |
-
const
|
| 178 |
-
const
|
| 179 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 180 |
});
|
| 181 |
|
|
|
|
| 182 |
const dataSag = [], dataSwell=[], dataOsc=[], dataLatency=[], dataCost=[], dataEnergy=[];
|
| 183 |
for(let i=0;i<60*24;i++){
|
| 184 |
const x = Date.now()-i*60000;
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
dataSwell.unshift({x, y: Math.max(0, 0.2 + Math.sin(i/180)*0.15 + Math.random()*0.08)});
|
| 188 |
-
dataOsc.unshift({x, y: Math.max(0, 0.1 + Math.sin(i/240)*0.1 + (i%400<15?0.3:0) + Math.random()*0.05)});
|
| 189 |
|
| 190 |
-
//
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 196 |
}
|
| 197 |
|
|
|
|
| 198 |
const alerts = [
|
| 199 |
-
{time:new Date(Date.now()-
|
| 200 |
-
{time:new Date(Date.now()-
|
| 201 |
-
{time:new Date(Date.now()-
|
| 202 |
-
{time:new Date(Date.now()-
|
| 203 |
-
{time:new Date(Date.now()-
|
|
|
|
|
|
|
| 204 |
];
|
| 205 |
/* CHARTS */
|
| 206 |
function lineConfig(data,label,color,fill=false){
|
|
@@ -253,9 +289,9 @@ function refresh(){
|
|
| 253 |
kpiEta.classList.toggle('blink', lastEta<60);
|
| 254 |
const hour = new Date().getHours();
|
| 255 |
const isPeak = hour > 8 && hour < 20;
|
| 256 |
-
document.getElementById('latencyVal').textContent = (isPeak ? 0.
|
| 257 |
-
document.getElementById('energyVal').textContent = (isPeak ? 0.
|
| 258 |
-
document.getElementById('co2Val').textContent = (2
|
| 259 |
// alerts
|
| 260 |
const tbody = document.querySelector('#alertsTable tbody');
|
| 261 |
tbody.innerHTML='';
|
|
@@ -269,8 +305,8 @@ function refresh(){
|
|
| 269 |
<td class="italic">${a.action}</td>`;
|
| 270 |
tbody.append(tr);
|
| 271 |
});
|
| 272 |
-
|
| 273 |
-
if(Math.random()>0.
|
| 274 |
}
|
| 275 |
const format = dateFns.format;
|
| 276 |
setInterval(refresh,1000);
|
|
|
|
| 169 |
setTheme(dark);
|
| 170 |
document.getElementById('themeBtn').onclick = ()=>setTheme(!body.classList.contains('theme-dark'));
|
| 171 |
/* DEMO DATA GENERATION */
|
| 172 |
+
let lastHealth=0.94, lastEta=180;
|
| 173 |
const dataHealth = Array(15*60).fill().map((_,i)=>{
|
| 174 |
const x = Date.now()-i*1000;
|
| 175 |
+
// Simulate realistic grid health with daily patterns
|
| 176 |
+
const hour = (i/3600) % 24;
|
| 177 |
+
const isPeak = (hour >= 8 && hour <= 10) || (hour >= 17 && hour <= 20);
|
| 178 |
+
const isNight = hour >= 22 || hour <= 5;
|
| 179 |
+
|
| 180 |
+
// Base health score with daily variation
|
| 181 |
+
let base = isPeak ? 0.88 : isNight ? 0.96 : 0.92;
|
| 182 |
+
|
| 183 |
+
// Add smaller fluctuations based on minutes
|
| 184 |
+
const min = (i/60) % 60;
|
| 185 |
+
base += Math.sin(min/15) * 0.02;
|
| 186 |
+
|
| 187 |
+
// Add random equipment failures (5% chance every 15 min)
|
| 188 |
+
const failure = i % (15*60) === 0 && Math.random() > 0.95 ? -0.08 : 0;
|
| 189 |
+
|
| 190 |
+
// Add random noise
|
| 191 |
+
const noise = Math.random() * 0.01;
|
| 192 |
+
|
| 193 |
+
return {x, y: Math.max(0.7, base + failure + noise)};
|
| 194 |
});
|
| 195 |
|
| 196 |
+
// Fault probability data (last 24 hours)
|
| 197 |
const dataSag = [], dataSwell=[], dataOsc=[], dataLatency=[], dataCost=[], dataEnergy=[];
|
| 198 |
for(let i=0;i<60*24;i++){
|
| 199 |
const x = Date.now()-i*60000;
|
| 200 |
+
const hour = (i/60) % 24;
|
| 201 |
+
const isPeak = (hour >= 6 && hour <= 9) || (hour >= 16 && hour <= 20);
|
|
|
|
|
|
|
| 202 |
|
| 203 |
+
// Voltage Sag (most common during peak hours)
|
| 204 |
+
let sagProb = isPeak ? 0.25 : 0.15;
|
| 205 |
+
sagProb += Math.sin(i/180) * 0.1; // cyclic pattern every 3 hours
|
| 206 |
+
if(i % (7*60) === 0 && Math.random() > 0.7) sagProb += 0.3; // random spikes
|
| 207 |
+
dataSag.unshift({x, y: Math.min(0.9, Math.max(0, sagProb + Math.random()*0.05))});
|
| 208 |
+
|
| 209 |
+
// Voltage Swell (less common)
|
| 210 |
+
let swellProb = isPeak ? 0.18 : 0.1;
|
| 211 |
+
swellProb += Math.sin(i/240) * 0.08;
|
| 212 |
+
if(i % (10*60) === 0 && Math.random() > 0.8) swellProb += 0.2;
|
| 213 |
+
dataSwell.unshift({x, y: Math.min(0.8, Math.max(0, swellProb + Math.random()*0.03))});
|
| 214 |
+
|
| 215 |
+
// Frequency Oscillation (rare but important)
|
| 216 |
+
let oscProb = isPeak ? 0.12 : 0.08;
|
| 217 |
+
oscProb += Math.sin(i/300) * 0.05;
|
| 218 |
+
if(i % (15*60) === 0 && Math.random() > 0.9) oscProb += 0.25;
|
| 219 |
+
dataOsc.unshift({x, y: Math.min(0.7, Math.max(0, oscProb + Math.random()*0.02))});
|
| 220 |
+
|
| 221 |
+
// Latency & Cost (better at night)
|
| 222 |
+
const latency = isPeak ? 0.18 + Math.random()*0.04 : 0.12 + Math.random()*0.03;
|
| 223 |
+
dataLatency.unshift({x, y: latency});
|
| 224 |
+
dataCost.unshift({x, y: latency / 1500}); // cost proportional to latency
|
| 225 |
+
|
| 226 |
+
// Energy consumption
|
| 227 |
+
const energy = isPeak ? 0.0095 + Math.random()*0.002 : 0.006 + Math.random()*0.0015;
|
| 228 |
+
dataEnergy.unshift({x, y: energy});
|
| 229 |
}
|
| 230 |
|
| 231 |
+
// Recent Alerts (last 24 hours)
|
| 232 |
const alerts = [
|
| 233 |
+
{time:new Date(Date.now()-45000), feeder:"N12", fault:"Voltage Sag 0.85", eta:"32 s", action:"Adjust tap changer"},
|
| 234 |
+
{time:new Date(Date.now()-120000), feeder:"S08", fault:"Current Swell 0.78", eta:"1:45", action:"Notify maintenance"},
|
| 235 |
+
{time:new Date(Date.now()-300000), feeder:"E05", fault:"Frequency Osc 0.65", eta:"2:10", action:"Monitor"},
|
| 236 |
+
{time:new Date(Date.now()-1800000), feeder:"W03", fault:"Harmonic Dist 0.72", eta:"4:30", action:"Schedule filter check"},
|
| 237 |
+
{time:new Date(Date.now()-3600000), feeder:"C01", fault:"Voltage Dip 0.88", eta:"12:30", action:"Reroute load"},
|
| 238 |
+
{time:new Date(Date.now()-5400000), feeder:"N07", fault:"Phase Unbalance 0.81", eta:"6:15", action:"Balance load"},
|
| 239 |
+
{time:new Date(Date.now()-7200000), feeder:"S02", fault:"Capacitor Bank Fail", eta:"8:20", action:"Dispatch crew"}
|
| 240 |
];
|
| 241 |
/* CHARTS */
|
| 242 |
function lineConfig(data,label,color,fill=false){
|
|
|
|
| 289 |
kpiEta.classList.toggle('blink', lastEta<60);
|
| 290 |
const hour = new Date().getHours();
|
| 291 |
const isPeak = hour > 8 && hour < 20;
|
| 292 |
+
document.getElementById('latencyVal').textContent = (isPeak ? 0.17 + Math.random()*0.03 : 0.12 + Math.random()*0.02).toFixed(2)+' ms';
|
| 293 |
+
document.getElementById('energyVal').textContent = (isPeak ? 0.009 + Math.random()*0.0015 : 0.006 + Math.random()*0.0012).toFixed(3)+' Wh';
|
| 294 |
+
document.getElementById('co2Val').textContent = (3.2 + Math.sin(Date.now()/86400000)*0.4).toFixed(2)+' kg';
|
| 295 |
// alerts
|
| 296 |
const tbody = document.querySelector('#alertsTable tbody');
|
| 297 |
tbody.innerHTML='';
|
|
|
|
| 305 |
<td class="italic">${a.action}</td>`;
|
| 306 |
tbody.append(tr);
|
| 307 |
});
|
| 308 |
+
if(lastEta < 30) showToast(`Critical fault predicted in ${lastEta}s`, {type:'danger'});
|
| 309 |
+
else if(Math.random()>0.997) showToast('Load imbalance detected in Sector N', {type:'warning'});
|
| 310 |
}
|
| 311 |
const format = dateFns.format;
|
| 312 |
setInterval(refresh,1000);
|