Compare commits
7
Commits
be44d008c7
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c297d84ec4 | ||
|
|
4eba40832b | ||
|
|
708b9a87cf | ||
|
|
5f8ac4ffc4 | ||
|
|
ae603d440c | ||
|
|
cad4ae65f7 | ||
|
|
88b5ff98cd |
No files matched your search
@@ -24,5 +24,3 @@ go.work.sum
|
||||
|
||||
# env file
|
||||
.env
|
||||
|
||||
!lhm/*
|
||||
@@ -5,8 +5,16 @@ import (
|
||||
"rpi5-health/internal/http_internal"
|
||||
)
|
||||
|
||||
/* TODO:
|
||||
* 1. Storage & Disk I/O: Disk capacity | Disk I/O rates | IOWAIT metrics
|
||||
* 2. Network traffic: Bandwidth usage | Total network usage | Active connections
|
||||
* 3. Process monitoring: Top consumers | Zombie processes */
|
||||
|
||||
func main() {
|
||||
srv := http_internal.NewAppServer()
|
||||
|
||||
srv.RegisterRoute("/api/events", http_internal.SSEHandler)
|
||||
|
||||
go srv.Start()
|
||||
|
||||
cli.Listen()
|
||||
|
||||
@@ -1,12 +1,28 @@
|
||||
package http_internal
|
||||
|
||||
import "net/http"
|
||||
import (
|
||||
"net/http"
|
||||
loadaverage "rpi5-health/internal/load_average"
|
||||
"rpi5-health/internal/memory"
|
||||
"rpi5-health/internal/processor"
|
||||
"rpi5-health/internal/temperature"
|
||||
"rpi5-health/internal/uptime"
|
||||
)
|
||||
|
||||
type AppServer struct {
|
||||
srv *http.Server
|
||||
mux *http.ServeMux
|
||||
}
|
||||
|
||||
// To be sent to the client
|
||||
type ServerStatistics struct {
|
||||
LoadAvgInfo loadaverage.LoadAverageInformation `json:"load_average"`
|
||||
MemoryInfo memory.MemoryInformation `json:"memory_info"`
|
||||
ProcessorInfo processor.ProcessorInformation `json:"processor_info"`
|
||||
TemperatureInfo temperature.TemperatureInformation `json:"temperature_info"`
|
||||
UptimeInfo uptime.UptimeInformation `json:"uptime_info"`
|
||||
}
|
||||
|
||||
func NewAppServer() *AppServer {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
@@ -28,3 +44,7 @@ func NewAppServer() *AppServer {
|
||||
func (srv *AppServer) Start() {
|
||||
srv.srv.ListenAndServe()
|
||||
}
|
||||
|
||||
func (srv *AppServer) RegisterRoute(endpoint string, handler http.HandlerFunc) {
|
||||
srv.mux.HandleFunc(endpoint, handler)
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package http_internal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
loadaverage "rpi5-health/internal/load_average"
|
||||
"rpi5-health/internal/memory"
|
||||
"rpi5-health/internal/processor"
|
||||
"rpi5-health/internal/temperature"
|
||||
"rpi5-health/internal/uptime"
|
||||
"time"
|
||||
)
|
||||
|
||||
func SSEHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
w.Header().Set("Connection", "keep-alive")
|
||||
|
||||
// For CORS requests
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
|
||||
clientGone := r.Context().Done() // Channel to signal client disconnection
|
||||
|
||||
rc := http.NewResponseController(w)
|
||||
t := time.NewTicker(time.Millisecond * 1000)
|
||||
defer t.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-clientGone:
|
||||
log.Println("Client disconnected")
|
||||
return
|
||||
case <-t.C:
|
||||
resp := buildDashboardResponse()
|
||||
_, err := fmt.Fprintf(w, "data: %s\n\n", resp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = rc.Flush()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildDashboardResponse() []byte {
|
||||
// TODO: proper error handling
|
||||
loadavg, _ := loadaverage.GetLoadAverage()
|
||||
memoryInfo, _ := memory.GetRamInformation()
|
||||
processorInfo := processor.GetCPUInformation()
|
||||
temperatureInfo, _ := temperature.GetTemperatureInformation()
|
||||
uptimeInfo, _ := uptime.GetUptimeInformation()
|
||||
|
||||
stats := ServerStatistics{
|
||||
LoadAvgInfo: loadavg,
|
||||
MemoryInfo: memoryInfo,
|
||||
ProcessorInfo: processorInfo,
|
||||
TemperatureInfo: temperatureInfo,
|
||||
UptimeInfo: uptimeInfo,
|
||||
}
|
||||
|
||||
payload, _ := json.Marshal(stats)
|
||||
|
||||
return payload
|
||||
}
|
||||
@@ -37,7 +37,7 @@ func getLinuxUptime() (UptimeInformation, error) {
|
||||
busyTime, _ = strconv.ParseFloat(fields[0], 64)
|
||||
|
||||
return UptimeInformation{
|
||||
uptime: busyTime,
|
||||
Uptime: busyTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -6,15 +6,11 @@ import (
|
||||
)
|
||||
|
||||
type UptimeInformation struct {
|
||||
uptime float64
|
||||
}
|
||||
|
||||
func (uptimeInfo UptimeInformation) GetUptime() (seconds float64) {
|
||||
return uptimeInfo.uptime
|
||||
Uptime float64
|
||||
}
|
||||
|
||||
func (uptimeInfo UptimeInformation) FormatSeconds() string {
|
||||
total := int64(math.Round(uptimeInfo.uptime))
|
||||
total := int64(math.Round(uptimeInfo.Uptime))
|
||||
|
||||
if total < 60 {
|
||||
return fmt.Sprintf("%d", total)
|
||||
|
||||
@@ -15,7 +15,7 @@ func getWinUptime(uptime *windows.LazyProc) (UptimeInformation, error) {
|
||||
|
||||
uptimeSeconds := float64(uptimeMetric) / 1000.0
|
||||
|
||||
return UptimeInformation{uptime: uptimeSeconds}, nil
|
||||
return UptimeInformation{Uptime: uptimeSeconds}, nil
|
||||
}
|
||||
|
||||
func GetUptimeInformation() (UptimeInformation, error) {
|
||||
|
||||
@@ -4,8 +4,14 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
<script src="js/script.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello world!</h1>
|
||||
<p id="cpu-load">Waiting for load data...</p>
|
||||
<p id="cpu-usage">Waiting for usage data...</p>
|
||||
<p id="memory">Waiting for memory data...</p>
|
||||
<p id="temperature">Waiting for temperature data...</p>
|
||||
<p id="uptime">Waiting for uptime data...</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,74 @@
|
||||
const eventSource = new EventSource("/api/events")
|
||||
|
||||
let prevCPUIdle = 0
|
||||
let prevCPUKernel = 0
|
||||
let prevCPUUser = 0
|
||||
|
||||
eventSource.onmessage = function (event) {
|
||||
const data = JSON.parse(event.data)
|
||||
|
||||
const GB = 1024 * 1024 * 1024
|
||||
|
||||
let currentCPUIdle = data.processor_info.IdleTimeSinceBoot
|
||||
let currentCPUKernel = data.processor_info.KernelTimeSinceBoot
|
||||
let currentCPUUser = data.processor_info.UserTimeSinceBoot
|
||||
|
||||
document.getElementById("cpu-load").innerText = "Load:" + data.load_average.Load1.toFixed(2) + " " + data.load_average.Load5.toFixed(2) + " " + data.load_average.Load15.toFixed(2)
|
||||
document.getElementById("cpu-usage").innerText = "CPU Usage:" + calculateCPUUsage(currentCPUIdle, currentCPUKernel, currentCPUUser).toFixed(2) + "%"
|
||||
document.getElementById("memory").innerText = "Memory:" + ((data.memory_info.SystemMemory - data.memory_info.AvailableMemory) / GB).toFixed(2) + "GB/" + (data.memory_info.SystemMemory / GB).toFixed(2)+"GB"
|
||||
document.getElementById("temperature").innerText ="CPU Temperature:" + checkTemperatureAvailability(data.temperature_info)
|
||||
document.getElementById("uptime").innerText ="Uptime:" + formatUptime(data.uptime_info)
|
||||
}
|
||||
|
||||
function calculateCPUUsage(currentIdle, currentKernel, currentUser) {
|
||||
if (prevCPUIdle === 0 || prevCPUKernel === 0 || prevCPUUser === 0) {
|
||||
prevCPUIdle = currentIdle
|
||||
prevCPUKernel = currentKernel
|
||||
prevCPUUser = currentUser
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
deltaIdle = currentIdle - prevCPUIdle
|
||||
deltaKernel = currentKernel - prevCPUKernel
|
||||
deltaUser = currentUser - prevCPUUser
|
||||
|
||||
deltaTotal = deltaIdle + deltaKernel + deltaUser
|
||||
|
||||
totalCPUUsage = ((deltaKernel + deltaUser) / deltaTotal) * 100
|
||||
|
||||
prevCPUIdle = currentIdle
|
||||
prevCPUKernel = currentKernel
|
||||
prevCPUUser = currentUser
|
||||
|
||||
return totalCPUUsage
|
||||
}
|
||||
|
||||
function checkTemperatureAvailability(temperatureStruct) {
|
||||
if (temperatureStruct.Supported) {
|
||||
return temperatureStruct.Celsius + "C°"
|
||||
} else {
|
||||
return "Unsupported"
|
||||
}
|
||||
}
|
||||
|
||||
function formatUptime(uptimeStruct) {
|
||||
total = Math.round(uptimeStruct.Uptime)
|
||||
if (total < 60) {
|
||||
return Math.floor(total)
|
||||
}
|
||||
s = total % 60
|
||||
m = (total / 60) % 60
|
||||
|
||||
if (total < 3000) {
|
||||
return Math.floor(m)+":"+String(Math.floor(s)).padStart(2, '0')
|
||||
}
|
||||
|
||||
h = (total / 3600) % 24
|
||||
if (total < 86400) {
|
||||
return Math.floor(h)+":"+String(Math.floor(m)).padStart(2, '0')+":"+String(Math.floor(s)).padStart(2, '0')
|
||||
}
|
||||
|
||||
d = total / 86400
|
||||
return Math.floor(d)+":"+String(Math.floor(h)).padStart(2, '0')+":"+String(Math.floor(m)).padStart(2, '0')+":"+String(Math.floor(s)).padStart(2, '0')
|
||||
}
|
||||
Reference in new issue
Block a user