package tui import ( "fmt" "get-container/cmd/dcutop/backend" "get-container/cmd/dcutop/tchart" "get-container/utils" "maps" "sync" "time" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/emirpasic/gods/v2/maps/linkedhashmap" "github.com/emirpasic/gods/v2/trees/binaryheap" ) const ( SysLoadHeight = 5 // 固定图表高度 SysLoadWidth = 77 // 固定图表宽度,不包含左右的边框 SysLoadCap = 500 // 记录 ) // ModelSysLoad 系统负载组件 type ModelSysLoad struct { SysMem *MyTimeChart SysCPU *MyTimeChart DCU *MyTimeChart DCUMem *MyTimeChart Cache map[int]backend.DCUInfo SysInfo *linkedhashmap.Map[time.Time, SysLoadInfo] Keys *binaryheap.Heap[time.Time] current *SysLoadInfo line string style lipgloss.Style } type SysLoadInfo struct { T time.Time DCUUsage map[int]float32 DCUMemUsage map[int]float32 Load1, Load5, Load15 float64 MemTotal, SwapTotal uint64 MemUsed, SwapUsed uint64 MemUsedPercent, SwapUsedPercent float64 CPUPercent float64 DCUUsageAvg float32 DCUMemUsageAvg float32 } func NewModelSysLoad(width int) *ModelSysLoad { result := ModelSysLoad{} result.Cache = make(map[int]backend.DCUInfo) result.SysInfo = linkedhashmap.New[time.Time, SysLoadInfo]() result.Keys = binaryheap.NewWith(func(a, b time.Time) int { if a.After(b) { return 1 } if a.Before(b) { return -1 } return 0 }) result.SysMem = New(SysLoadWidth, SysLoadHeight, 0, 100, map[string]lipgloss.Color{"default": lipgloss.Color("#0400ffff")}) result.SysCPU = New(SysLoadWidth, SysLoadHeight, 0, 100, map[string]lipgloss.Color{"default": lipgloss.Color("#8400ffff")}) result.DCU = New(width, SysLoadHeight, 0, 100, map[string]lipgloss.Color{"default": lipgloss.Color("#00ff00ff")}) result.DCUMem = New(width, SysLoadHeight, 0, 100, map[string]lipgloss.Color{"default": lipgloss.Color("#eeff00ff")}) result.line = genXAxis(SysLoadWidth) + myBorder.Middle + genXAxis(width) result.style = lipgloss.NewStyle() return &result } func (m *ModelSysLoad) Init() tea.Cmd { result := make([]tea.Cmd, 0) if c := m.DCU.Init(); c != nil { result = append(result, c) } if c := m.DCUMem.Init(); c != nil { result = append(result, c) } if c := m.SysMem.Init(); c != nil { result = append(result, c) } if c := m.SysCPU.Init(); c != nil { result = append(result, c) } if len(result) > 0 { return tea.Batch(result...) } sysInfo, _ := utils.GetSysInfo() s := SysLoadInfo{} s.Load1 = sysInfo.LoadAverage1 s.Load5 = sysInfo.LoadAverage5 s.Load15 = sysInfo.LoadAverage15 s.MemTotal = sysInfo.MemTotal s.MemUsed = sysInfo.MemUsage s.SwapTotal = sysInfo.SwapTotal s.SwapUsed = sysInfo.SwapUsage s.MemUsedPercent = sysInfo.MemUsagePercent s.SwapUsedPercent = sysInfo.SwapUsagePercent s.T = time.Now() s.CPUPercent = sysInfo.CPUPercent s.DCUUsageAvg = 0 s.DCUMemUsageAvg = 0 m.current = &s return nil } func (m *ModelSysLoad) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) { switch msg := inputMsg.(type) { case *ModelMsg: clear(m.Cache) maps.Copy(m.Cache, msg.DCUInfo) m.updateInfo(msg.t) return m, nil } return m, nil } func (m *ModelSysLoad) View() string { load := fmt.Sprintf("Load Average: %.2f %.2f %.2f", m.current.Load1, m.current.Load5, m.current.Load15) mem := fmt.Sprintf("MEM: %s (%.1f%%)", "todo", 12.3) dcuMem := fmt.Sprintf("AVG DCU MEM: %.1f%%", m.current.DCUMemUsageAvg) dcu := fmt.Sprintf("AVG DCU UTL: %.1f%%", m.current.DCUUsageAvg) load = StackPosition(load, m.SysCPU.View(), lipgloss.Top, lipgloss.Left) mem = StackPosition(mem, m.SysMem.View(), lipgloss.Bottom, lipgloss.Left) dcuMem = StackPosition(dcuMem, m.DCUMem.View(), lipgloss.Top, lipgloss.Left) dcu = StackPosition(dcu, m.DCU.View(), lipgloss.Bottom, lipgloss.Left) load = m.style.Border(myBorder, false, true, false).Render(load) mem = m.style.Render(mem) dcuMem = m.style.Border(myBorder, false, true, false, false).UnsetBorderRight().Render(dcuMem) dcu = m.style.Render(dcu) up := lipgloss.JoinHorizontal(lipgloss.Top, load, dcuMem) down := lipgloss.JoinHorizontal(lipgloss.Top, mem, dcu) return lipgloss.JoinVertical(lipgloss.Left, up, m.line, down) } // updateInfo func (m *ModelSysLoad) updateInfo(t time.Time) { sysInfo, _ := utils.GetSysInfo() s := SysLoadInfo{} s.Load1 = sysInfo.LoadAverage1 s.Load5 = sysInfo.LoadAverage5 s.Load15 = sysInfo.LoadAverage15 s.MemTotal = sysInfo.MemTotal s.MemUsed = sysInfo.MemUsage s.SwapTotal = sysInfo.SwapTotal s.SwapUsed = sysInfo.SwapUsage s.MemUsedPercent = sysInfo.MemUsagePercent s.SwapUsedPercent = sysInfo.SwapUsagePercent s.T = t s.CPUPercent = sysInfo.CPUPercent s.DCUUsage = make(map[int]float32) s.DCUMemUsage = make(map[int]float32) s.DCUMemUsageAvg, s.DCUUsageAvg = 0, 0 for k, v := range m.Cache { s.DCUMemUsageAvg += v.MemUsedPerent s.DCUMemUsage[k] = v.MemUsedPerent s.DCUMemUsageAvg += v.DCUUTil s.DCUUsage[k] = v.DCUUTil } l := len(m.Cache) s.DCUMemUsageAvg /= float32(l) s.DCUUsageAvg /= float32(l) m.current = &s wg := sync.WaitGroup{} wg.Add(4) go func() { defer wg.Done() m1, _ := m.SysMem.Update(MyTimeChartMsg{Points: map[string][]tchart.TimePoint{"default": {{Time: t, Value: s.MemUsedPercent}}}}) m.SysMem = m1.(*MyTimeChart) }() go func() { defer wg.Done() m2, _ := m.SysCPU.Update(MyTimeChartMsg{Points: map[string][]tchart.TimePoint{"default": {{Time: t, Value: s.CPUPercent}}}}) m.SysCPU = m2.(*MyTimeChart) }() go func() { defer wg.Done() m3, _ := m.DCU.Update(MyTimeChartMsg{Points: map[string][]tchart.TimePoint{"default": {{Time: t, Value: float64(s.DCUUsageAvg)}}}}) m.DCU = m3.(*MyTimeChart) }() go func() { defer wg.Done() m4, _ := m.DCUMem.Update(MyTimeChartMsg{Points: map[string][]tchart.TimePoint{"default": {{Time: t, Value: float64(s.DCUMemUsageAvg)}}}}) m.DCUMem = m4.(*MyTimeChart) }() // todo wg.Wait() }