init
This commit is contained in:
@@ -0,0 +1,269 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
tea "charm.land/bubbletea/v2"
|
||||
)
|
||||
|
||||
type SysValue[T int | float64] struct {
|
||||
Val T
|
||||
Fmt string
|
||||
Inf bool
|
||||
}
|
||||
|
||||
type Detail struct {
|
||||
Name string
|
||||
Desc string
|
||||
Memory SysValue[float64]
|
||||
MemoryMax SysValue[float64]
|
||||
Tasks SysValue[int]
|
||||
TasksMax SysValue[int]
|
||||
Cpu float64
|
||||
Active string
|
||||
Load string
|
||||
Sub string
|
||||
}
|
||||
|
||||
type DetailLoadedMsg struct {
|
||||
Detail Detail
|
||||
Err error
|
||||
}
|
||||
|
||||
func (m *model) LoadDetail() tea.Msg {
|
||||
d, err := m.loadDetail()
|
||||
|
||||
if err != nil {
|
||||
return DetailLoadedMsg{Err: err}
|
||||
}
|
||||
|
||||
if d == nil {
|
||||
return DetailLoadedMsg{}
|
||||
}
|
||||
|
||||
cpu, _ := strconv.ParseFloat(d["CPUUsageNSec"], 64)
|
||||
|
||||
return DetailLoadedMsg{Detail: Detail{
|
||||
Name: m.filtered[m.cursor].Name,
|
||||
Desc: m.filtered[m.cursor].Desc,
|
||||
Memory: take[float64](d, "MemoryCurrent", "", "B", 2, 1000),
|
||||
MemoryMax: take[float64](d, "MemoryMax", "", "B", 2, 1000),
|
||||
Tasks: take[int](d, "TasksCurrent", "", "", 1, 1000),
|
||||
TasksMax: take[int](d, "TasksMax", "", "", 1, 1000),
|
||||
Cpu: cpu / 1000000000,
|
||||
Active: d["ActiveState"],
|
||||
Load: d["LoadState"],
|
||||
Sub: d["SubState"],
|
||||
}}
|
||||
}
|
||||
|
||||
func take[T int | float64](d map[string]string, key string, start string, unit string, decimals int, step int) SysValue[T] {
|
||||
val, _ := takeVal[T](d, key, start)
|
||||
inf := normUnit(d, key, start) == ""
|
||||
fmt := formatUnit(d, key, start, unit, decimals, step)
|
||||
return SysValue[T]{Val: val, Inf: inf, Fmt: fmt}
|
||||
}
|
||||
|
||||
func (v SysValue[T]) Alt(alt SysValue[T]) SysValue[T] {
|
||||
if v.Inf {
|
||||
return alt
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (v SysValue[T]) Div(y SysValue[T]) float64 {
|
||||
return float64(v.Val) / float64(y.Val)
|
||||
}
|
||||
|
||||
func takeVal[T int | float64](d map[string]string, key string, start string) (T, error) {
|
||||
x := normUnit(d, key, start)
|
||||
var zero T
|
||||
switch any(zero).(type) {
|
||||
case int:
|
||||
v, err := strconv.Atoi(x)
|
||||
return any(v).(T), err
|
||||
case float64:
|
||||
v, err := strconv.ParseFloat(x, 64)
|
||||
return any(v).(T), err
|
||||
default:
|
||||
return zero, fmt.Errorf("unsupported type")
|
||||
}
|
||||
}
|
||||
|
||||
func (m *model) loadDetail() (map[string]string, error) {
|
||||
if len(m.filtered) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
detail := make(map[string]string)
|
||||
|
||||
name := m.filtered[m.cursor].Name
|
||||
|
||||
cmd := exec.Command(
|
||||
"systemctl", "show", name,
|
||||
"-p", "CPUUsageNSec",
|
||||
"-p", "CPUQuotaPerSecUSec",
|
||||
"-p", "MemoryCurrent",
|
||||
"-p", "MemoryMax",
|
||||
"-p", "TasksCurrent",
|
||||
"-p", "TasksMax",
|
||||
"-p", "ActiveState",
|
||||
"-p", "LoadState",
|
||||
"-p", "SubState",
|
||||
)
|
||||
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lines := strings.SplitSeq(string(out), "\n")
|
||||
|
||||
for line := range lines {
|
||||
if parts := strings.SplitN(line, "=", 2); len(parts) == 2 {
|
||||
detail[parts[0]] = parts[1]
|
||||
}
|
||||
}
|
||||
|
||||
return detail, nil
|
||||
}
|
||||
|
||||
type System struct {
|
||||
Memory SysValue[float64]
|
||||
Tasks SysValue[int]
|
||||
}
|
||||
|
||||
type SystemLoadedMsg struct {
|
||||
System System
|
||||
Err error
|
||||
}
|
||||
|
||||
func LoadSystem() tea.Msg {
|
||||
d, err := loadMemSystem()
|
||||
|
||||
if err != nil {
|
||||
return SystemLoadedMsg{Err: err}
|
||||
}
|
||||
|
||||
if d == nil {
|
||||
return SystemLoadedMsg{}
|
||||
}
|
||||
|
||||
return SystemLoadedMsg{System: System{
|
||||
Memory: take[float64](d, "MemTotal", "K", "B", 2, 1000),
|
||||
Tasks: take[int](d, "TasksMax", "", "", 1, 1000),
|
||||
}}
|
||||
}
|
||||
|
||||
func loadMemSystem() (map[string]string, error) {
|
||||
f, err := os.Open("/proc/meminfo")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
l := make(map[string]string)
|
||||
|
||||
scanner := bufio.NewScanner(f)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := strings.TrimSuffix(parts[0], ":")
|
||||
val, err := strconv.ParseUint(parts[1], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
l[key] = strconv.FormatUint(val, 10)
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func normUnit(dic map[string]string, key string, start string) string {
|
||||
start = strings.ToUpper(start)
|
||||
|
||||
st := dic[key]
|
||||
|
||||
if st == "infinity" || st == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
sizes := []string{"", "K", "M", "G", "T", "P", "E", "Z", "Y"}
|
||||
|
||||
n := 0
|
||||
for i, s := range sizes {
|
||||
if s == start {
|
||||
n = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s%s", dic[key], strings.Repeat("0", n*3))
|
||||
}
|
||||
|
||||
func formatUnit(dic map[string]string, key string, start string, unit string, decimals int, step int) string {
|
||||
start = strings.ToUpper(start)
|
||||
fstep := float64(step)
|
||||
|
||||
st := dic[key]
|
||||
|
||||
if st == "infinity" || st == "" {
|
||||
return fmt.Sprintf("∞ %s", unit)
|
||||
}
|
||||
|
||||
value, _ := strconv.ParseFloat(st, 64)
|
||||
|
||||
if value == 0 {
|
||||
return fmt.Sprintf("0 %s%s", start, unit)
|
||||
}
|
||||
|
||||
sizes := []string{"", "K", "M", "G", "T", "P", "E", "Z", "Y"}
|
||||
|
||||
n := 0
|
||||
for i, s := range sizes {
|
||||
if s == start {
|
||||
n = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
i := n + int(math.Floor(math.Log(value)/math.Log(fstep)))
|
||||
if i < 0 {
|
||||
i = 0
|
||||
}
|
||||
if i >= len(sizes) {
|
||||
i = len(sizes) - 1
|
||||
}
|
||||
|
||||
value = value / math.Pow(fstep, float64(i-n))
|
||||
|
||||
if math.Abs(value-math.Round(value)) < 0.05 {
|
||||
value = math.Round(value)
|
||||
}
|
||||
|
||||
var formatted string
|
||||
if math.Mod(value, 1) == 0 {
|
||||
formatted = fmt.Sprintf("%.0f", value)
|
||||
} else {
|
||||
format := fmt.Sprintf("%%.%df", decimals)
|
||||
formatted = fmt.Sprintf(format, value)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s %s%s", formatted, sizes[i], unit)
|
||||
}
|
||||
Reference in New Issue
Block a user