diff --git a/posimai-dev/station.html b/posimai-dev/station.html index 88107181..5f752e38 100644 --- a/posimai-dev/station.html +++ b/posimai-dev/station.html @@ -83,11 +83,11 @@ .metric-header-row { display:flex;justify-content:space-between;align-items:baseline; } .metric-label { font-size:12px;color:var(--text2); } .metric-val { font-family:'JetBrains Mono',monospace;font-size:12px;font-weight:500; } - .metric-bin { font-family:'JetBrains Mono',monospace;font-size:9px;letter-spacing:0.18em;color:rgba(34,211,238,0.35);margin-top:2px;transition:color 0.4s; } - .bar-track { height:4px;border-radius:2px;background:rgba(255,255,255,0.05);overflow:hidden; } - .bar-fill { height:100%;border-radius:2px;background:var(--accent);transition:width 0.8s cubic-bezier(0.4,0,0.2,1),background 0.4s; } - .bar-fill.warn { background:var(--warn); } - .bar-fill.crit { background:var(--crit); } + .bin-bar { font-family:'JetBrains Mono',monospace;font-size:11px;letter-spacing:0.04em;display:flex;gap:0px;margin-top:2px; } + .bin-bar .b1 { color:var(--accent);transition:color 0.5s; } + .bin-bar .b0 { color:rgba(255,255,255,0.1); } + .bin-bar.warn .b1 { color:var(--warn); } + .bin-bar.crit .b1 { color:var(--crit); } .load-row { display:flex;gap:6px;flex-shrink:0; } .load-chip { flex:1;background:var(--surface2);border-radius:7px;padding:7px;text-align:center; } @@ -144,8 +144,8 @@ .service-uptime.full { color:var(--ok); } .service-uptime.partial { color:#FB923C; } .service-uptime.down { color:var(--crit); } - .latency-bar-wrap { height:3px;border-radius:2px;background:rgba(255,255,255,0.07);overflow:hidden;margin-top:4px; } - .latency-bar { height:100%;border-radius:2px;transition:width 0.6s ease; } + .svc-spark-wrap { height:28px;margin:2px 0; } + .svc-spark { width:100%;height:100%;overflow:visible; } /* stream */ #stream-feed { flex:1;overflow:hidden;display:flex;flex-direction:column;gap:0; } @@ -162,7 +162,7 @@ .s-bar-fill.warn { background:var(--warn); } .s-bar-fill.crit { background:var(--crit); } #stream-ticker { font-family:'JetBrains Mono',monospace;font-size:9px;color:var(--text3);padding-top:7px;overflow:hidden;white-space:nowrap;border-top:1px solid var(--border2);flex-shrink:0; } - #stream-ticker-inner { display:inline-block;animation:ticker 30s linear infinite; } + #stream-ticker-inner { display:inline-block;animation:ticker 60s linear infinite; } @keyframes ticker { from{transform:translateX(0)} to{transform:translateX(-50%)} } #bottom { display:flex;align-items:center;justify-content:space-between;padding-top:12px;border-top:1px solid var(--border); } @@ -200,18 +200,15 @@
Ubuntu PC
CPU
-
-
--------
+
Memory
-
-
--------
+
Disk (/)
-
-
--------
+
Load Avg
@@ -328,7 +325,8 @@ const SERVICES = [ ]; const hist = {cpu:[], load:[]}; const svcHist = {}; -SERVICES.forEach(s => svcHist[s.id] = []); +const svcLatHist = {}; +SERVICES.forEach(s => { svcHist[s.id] = []; svcLatHist[s.id] = []; }); let streamData = null; function p(n){ return String(n).padStart(2,'0'); } @@ -396,15 +394,12 @@ async function fetchHealth(){ window._cpuCount=cpuCount; document.getElementById('cpu-val').textContent=`${cpuPct}%`; - const cb=document.getElementById('cpu-bar');cb.style.width=`${cpuPct}%`;cb.className='bar-fill'+(cpuPct>80?' crit':cpuPct>60?' warn':''); - document.getElementById('cpu-bin').textContent=cpuPct.toString(2).padStart(8,'0'); + renderBinBar('cpu-bar',cpuPct,60,80); document.getElementById('mem-val').textContent=`${data.mem_used_mb}/${data.mem_total_mb}MB (${memPct}%)`; - const mb=document.getElementById('mem-bar');mb.style.width=`${memPct}%`;mb.className='bar-fill'+(memPct>85?' crit':memPct>65?' warn':''); - document.getElementById('mem-bin').textContent=memPct.toString(2).padStart(8,'0'); + renderBinBar('mem-bar',memPct,65,85); if(data.disk){ document.getElementById('disk-val').textContent=`${data.disk.used_gb}/${data.disk.total_gb}GB (${diskPct}%)`; - const db=document.getElementById('disk-bar');db.style.width=`${diskPct}%`;db.className='bar-fill'+(diskPct>90?' crit':diskPct>75?' warn':''); - document.getElementById('disk-bin').textContent=diskPct.toString(2).padStart(8,'0'); + renderBinBar('disk-bar',diskPct,75,90); } document.getElementById('cpu-count-label').textContent=`(core:${cpuCount})`; ['load-1','load-5','load-15'].forEach((id,i)=>{ @@ -453,17 +448,50 @@ function buildServiceCards(){ SERVICES.forEach(svc=>{ const card=document.createElement('div'); card.className='service-card'; card.id=`svc-${svc.id}`; const dots=Array(5).fill(0).map((_,i)=>`
`).join(''); - card.innerHTML=`
${svc.name}...
${svc.desc}
`; + card.innerHTML=`
${svc.name}...
${svc.desc}
`; grid.appendChild(card); }); } -function updateLatencyBar(id,ms){ - const bar=document.getElementById(`lbar-${id}`); if(!bar)return; - if(ms===null){bar.style.width='100%';bar.style.background='var(--crit)';return;} - const w=ms<=50?100:ms<=200?80:ms<=500?55:35; - const color=ms<=200?'var(--ok)':ms<=500?'#FB923C':'var(--crit)'; - bar.style.width=w+'%'; bar.style.background=color; +function renderBinBar(id,pct,warnTh,critTh){ + const el=document.getElementById(id); if(!el)return; + const cells=20,filled=Math.round(pct/100*cells); + const cls=pct>critTh?'crit':pct>warnTh?'warn':''; + el.className='bin-bar'+(cls?' '+cls:''); + el.innerHTML=Array.from({length:cells},(_,i)=> + `${i` + ).join(''); +} + +function updateSparkline(id, ms, ok){ + const h = svcLatHist[id]; + h.push(ok ? (ms||0) : null); + if(h.length > 12) h.shift(); + const svg = document.getElementById(`spark-${id}`); + if(!svg || h.length < 2) return; + const valid = h.filter(v => v !== null); + if(valid.length < 2){ svg.innerHTML=''; return; } + const maxV = Math.max(...valid, 1); + const W=100, H=24, pad=2; + const pts = h.map((v,i) => { + const x = (i/(h.length-1))*W; + const y = v===null ? H-pad : H-pad - ((v/maxV)*(H-pad*2)); + return `${x.toFixed(1)},${y.toFixed(1)}`; + }).join(' '); + const color = !ok ? 'rgba(248,113,113,0.7)' : maxV > 500 ? 'rgba(251,146,60,0.7)' : 'rgba(34,211,238,0.6)'; + const area = h.map((v,i) => { + const x=(i/(h.length-1))*W; + const y=v===null?H-pad:H-pad-((v/maxV)*(H-pad*2)); + return `${x.toFixed(1)},${y.toFixed(1)}`; + }); + area.push(`${W},${H}`, `0,${H}`); + svg.innerHTML = ` + + + + + + `; } function pushSvcHistory(id,ok){ @@ -498,7 +526,7 @@ async function checkService(svc){ badge.className='service-badge '+(ok?'ok':'crit'); badge.textContent=ok?'OK':'DOWN'; latEl.textContent=ms?`${ms}ms`:''; - updateLatencyBar(svc.id,ok?ms:null); + updateSparkline(svc.id,ms,!!ok); pushSvcHistory(svc.id,!!ok); }else{ await fetch(svc.url,{method:'HEAD',mode:'no-cors',signal:ctrl.signal}); @@ -506,7 +534,7 @@ async function checkService(svc){ const ms=Date.now()-t0; badge.className='service-badge ok'; badge.textContent='OK'; latEl.textContent=`${ms}ms`; - updateLatencyBar(svc.id,ms); + updateSparkline(svc.id,ms,true); pushSvcHistory(svc.id,true); } }catch(e){ @@ -555,10 +583,21 @@ function updateStream(data){ const r=rows[Math.floor(Date.now()/1000)%rows.length]; pushStreamRow(r.label,r.value,r.bin,r.pct,r.level); const ip='100.77.11.43'; - const ipBin=ip.split('.').map(o=>toBin(parseInt(o),8)).join(' '); - const base=`${ipBin} // ${data.hostname||'ubuntu-pc'} // ${data.node_version||''} // `; + const b8 = n => n.toString(2).padStart(8,'0'); + const segments = [ + `CPU:${b8(cpuPct??0)}`, + `MEM:${b8(memPct??0)}`, + `DISK:${b8(diskPct??0)}`, + `LOAD:${b8(Math.min(255,Math.round((loadAvg[0]||0)*64)))}`, + `UP:${(data.uptime_s||0).toString(2)}`, + `SESSION:${b8(data.active_sessions||0)}`, + `TIME:${(Math.floor(Date.now()/1000)).toString(2).slice(-20)}`, + `IP:${ip.split('.').map(o=>b8(parseInt(o))).join('.')}`, + `HOST:${(data.hostname||'ubuntu-pc').split('').map(c=>b8(c.charCodeAt(0))).join(' ')}`, + ]; + const tape = segments.join(' // ') + ' // '; const el=document.getElementById('stream-ticker-inner'); - if(el)el.textContent=base+base; + if(el){ el.textContent=tape+tape; el.style.animation='none'; void el.offsetWidth; el.style.animation=''; } } setInterval(()=>{if(streamData)updateStream(streamData);},4000);