-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.js
More file actions
70 lines (70 loc) · 5.22 KB
/
app.js
File metadata and controls
70 lines (70 loc) · 5.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
let circuits = [
{ name:"Lighting", phase:"L1", breaker:10, load:1200, demand:90 },
{ name:"Socket Ring", phase:"L2", breaker:32, load:3600, demand:55 },
{ name:"HVAC", phase:"L3", breaker:20, load:4200, demand:80 },
{ name:"Pump Motor", phase:"L1", breaker:16, load:2500, demand:75 },
{ name:"Workshop Outlets", phase:"L2", breaker:32, load:5200, demand:60 },
{ name:"Compressor", phase:"L3", breaker:25, load:6100, demand:85 },
];
const fields = { mainBreaker:document.getElementById("mainBreaker"), voltage:document.getElementById("voltage"), diversity:document.getElementById("diversity") };
function n(id){return Number(fields[id].value)}
function analyze(){
const voltage=n("voltage"), main=n("mainBreaker"), diversity=n("diversity")/100;
const connected=circuits.reduce((s,c)=>s+c.load,0);
const phase={L1:0,L2:0,L3:0};
const overloaded=[];
circuits.forEach(c=>{ const current=c.load/voltage; const demandA=current*c.demand/100; phase[c.phase]+=demandA; if(current>c.breaker) overloaded.push(c.name); });
Object.keys(phase).forEach(p=>phase[p]*=diversity);
const demandW=circuits.reduce((s,c)=>s+c.load*c.demand/100,0)*diversity;
const demandA=demandW/voltage;
const spare=main-demandA, panelLoad=demandA/main*100;
const avg=(phase.L1+phase.L2+phase.L3)/3;
const imbalance=Math.max(...Object.values(phase).map(v=>Math.abs(v-avg)))/avg*100;
const warnings=[];
if(panelLoad>80) warnings.push("Panel demand exceeds 80% of main breaker rating.");
if(imbalance>20) warnings.push("Phase imbalance exceeds 20%; redistribute single-phase circuits.");
if(overloaded.length) warnings.push("One or more circuits exceed breaker rating.");
if(spare<0) warnings.push("Panel demand exceeds main breaker capacity.");
if(!warnings.length) warnings.push("Panel check passes configured limits.");
return {connected,demandW,demandA,spare,panelLoad,phase,imbalance,overloaded,warnings,voltage};
}
function renderRows(){
const voltage=n("voltage");
document.getElementById("circuitRows").innerHTML = circuits.map((c,i)=>{
const amps=c.load/voltage, bad=amps>c.breaker;
return `<div class="row">
<input data-i="${i}" data-f="name" value="${c.name}">
<select data-i="${i}" data-f="phase"><option ${c.phase==="L1"?"selected":""}>L1</option><option ${c.phase==="L2"?"selected":""}>L2</option><option ${c.phase==="L3"?"selected":""}>L3</option></select>
<input type="number" data-i="${i}" data-f="breaker" value="${c.breaker}">
<input type="number" data-i="${i}" data-f="load" value="${c.load}">
<input type="number" data-i="${i}" data-f="demand" value="${c.demand}">
<span class="${bad?"bad":"ok"}">${bad?"Over":"OK"}</span>
</div>`;
}).join("");
document.querySelectorAll("#circuitRows input,#circuitRows select").forEach(el=>el.addEventListener("input",e=>{const i=Number(e.target.dataset.i), f=e.target.dataset.f; circuits[i][f]=["name","phase"].includes(f)?e.target.value:Number(e.target.value); render();}));
}
function drawBar(canvas, labels, values, colors){
const ctx=canvas.getContext("2d"), ratio=window.devicePixelRatio||1, w=canvas.clientWidth||canvas.width, h=310; canvas.width=w*ratio; canvas.height=h*ratio; ctx.scale(ratio,ratio); ctx.clearRect(0,0,w,h);
const plot={x:48,y:22,w:w-82,h:220}, max=Math.max(...values,1); ctx.strokeStyle="#d8e2ed"; ctx.beginPath(); ctx.moveTo(plot.x,plot.y); ctx.lineTo(plot.x,plot.y+plot.h); ctx.lineTo(plot.x+plot.w,plot.y+plot.h); ctx.stroke();
labels.forEach((label,i)=>{const bw=plot.w/labels.length-24,x=plot.x+12+i*(plot.w/labels.length),bh=values[i]/max*(plot.h-28),y=plot.y+plot.h-bh; ctx.fillStyle=colors[i]; ctx.fillRect(x,y,bw,bh); ctx.fillStyle="#102033"; ctx.font="12px Inter, sans-serif"; ctx.fillText(label,x,plot.y+plot.h+22); ctx.fillText(values[i].toFixed(1),x,y-8);});
}
function render(){
const r=analyze();
renderRows();
document.getElementById("connected").textContent=`${(r.connected/1000).toFixed(1)} kW`;
document.getElementById("demand").textContent=`${(r.demandW/1000).toFixed(1)} kW`;
document.getElementById("demandA").textContent=`${r.demandA.toFixed(1)} A`;
document.getElementById("spare").textContent=`${r.spare.toFixed(1)} A`;
document.getElementById("panelLoad").textContent=`${r.panelLoad.toFixed(0)}%`;
document.getElementById("imbalance").textContent=`${r.imbalance.toFixed(0)}%`;
document.getElementById("overloaded").textContent=r.overloaded.length;
document.getElementById("spareCard").textContent=`${r.spare.toFixed(1)} A`;
document.getElementById("badge").textContent=r.warnings.length && !r.warnings[0].startsWith("Panel check passes")?"Warnings":"Pass";
document.getElementById("warningText").textContent=r.warnings.join(" ");
drawBar(document.getElementById("phaseChart"), ["L1","L2","L3"], [r.phase.L1,r.phase.L2,r.phase.L3], ["#1769d2","#168a45","#c77700"]);
drawBar(document.getElementById("circuitChart"), circuits.map(c=>c.name.slice(0,10)), circuits.map(c=>c.load/1000), ["#1769d2","#168a45","#c77700","#6941c6","#0ea5a4","#d92d20"]);
}
Object.values(fields).forEach(el=>el.addEventListener("input", render));
document.getElementById("addCircuit").addEventListener("click",()=>{circuits.push({name:"New Circuit",phase:"L1",breaker:16,load:1200,demand:75});render();});
window.addEventListener("resize", render);
render();