package tui import ( "fmt" "get-container/cmd/hytop/backend" "get-container/cmd/hytop/tchart" "get-container/utils" "math" "strings" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" linkedlist "github.com/emirpasic/gods/v2/lists/doublylinkedlist" ) const ( MPDHeader = " DCU PID USER DCU-MEM %CPU %MEM TIME Docker Command" MPDProcess = "This process has finished running." ) type ModelProcessDetail struct { CPU, Mem, DCU, DCUMem *MyTimeChart Pid int32 dcu int Header, Cmd, docker string width, height int // 界面的尺寸 modelMsg *ModelMsg // maxVal [4]float64 // 分别代表CPU、Mem、DCU、DCUMem的最大值 maxValThreshold [4]float64 cpuPoints *linkedlist.List[tchart.TimePoint] cpuPointsNum int // axeYStr [4]string // 纵坐标字符串 h, w [2]int // h[0]代表上边两个表的高度、h[1]代表下边两个表的高度,w[0]表示左边两个表的宽度,w[1]表示右边两个表的宽度 lines [5]string // 5条横线 DCUMemTotal, MemTotal utils.MemorySize // 内存总量 MemMax utils.MemorySize // 内存使用最大值 DCUMemUsed utils.MemorySize CPUPercent, MemPercent float64 DCUPercent, DCUMemPercent float64 t string color1, color2 []lipgloss.Color // 分别给上方和下方两个图表的渐变颜色 } func NewModelProcessDetail(width, height int, pid int32) *ModelProcessDetail { result := ModelProcessDetail{} if (width-3)%2 == 0 { result.w[0] = (width - 3) / 2 result.w[1] = result.w[0] } else { result.w[0] = (width - 3) / 2 result.w[1] = result.w[0] + 1 } if (height-8)%2 == 0 { result.h[0] = (height - 8) / 2 result.h[1] = result.h[0] } else { result.h[0] = (height - 8) / 2 result.h[1] = result.h[0] + 1 } var lines [5]string lines[0] = myBorder.TopLeft + strings.Repeat(myBorder.Top, width-2) + myBorder.TopRight lines[1] = "╞" + strings.Repeat("═", width-2) + "╡" lines[2] = "╞" + strings.Repeat("═", result.w[0]) + "╤" + strings.Repeat("═", result.w[1]) + "╡" lines[3] = myBorder.MiddleLeft + genXAxis(result.w[0]) + myBorder.Middle + genXAxis(result.w[1]) + myBorder.MiddleRight lines[4] = myBorder.BottomLeft + strings.Repeat("═", result.w[0]) + "╧" + strings.Repeat("═", result.w[1]) + myBorder.BottomRight result.lines = lines result.Pid = pid result.cpuPoints = linkedlist.New[tchart.TimePoint]() result.height = height result.width = width style := lipgloss.NewStyle() uah := fmt.Sprintf("%s@%s ", style.Foreground(lipgloss.Color("#edff2cff")).Render(backend.User), style.Foreground(lipgloss.Color("#a3ff2bff")).Render(backend.HostName)) sb := strings.Builder{} sb.WriteString(result.lines[0]) sb.WriteByte('\n') sb.WriteString(myBorder.Left) sb.WriteString(" Process:") sb.WriteString(strings.Repeat(" ", width-2-lipgloss.Width(uah)-lipgloss.Width(" Process:"))) sb.WriteString(uah) sb.WriteString(myBorder.Left) sb.WriteByte('\n') sb.WriteString(myBorder.Left) sb.WriteString(MPDHeader) sb.WriteString(strings.Repeat(" ", width-2-lipgloss.Width(MPDHeader))) sb.WriteString(myBorder.Left) sb.WriteByte('\n') sb.WriteString(result.lines[1]) sb.WriteByte('\n') result.Header = sb.String() result.color1 = GenGradientColor(lipgloss.Color("#ff0000"), lipgloss.Color("#00ff00ff"), result.h[0]) result.color2 = GenGradientColor(lipgloss.Color("#ff0000"), lipgloss.Color("#00ff00ff"), result.h[1]) result.CPU = NewTimeChart(result.w[0], result.h[0], 0, 100, []lipgloss.Color{lipgloss.Color("#00ff00ff")}) result.Mem = NewTimeChart(result.w[0], result.h[1], 0, 100, result.color2) result.DCU = NewTimeChart(result.w[1], result.h[1], 0, 100, result.color2) result.DCUMem = NewTimeChart(result.w[1], result.h[0], 0, 100, result.color1) result.cpuPointsNum = result.w[0]*2 + 1 for k := range result.maxVal { result.maxVal[k] = 0 } for k := range result.maxValThreshold { result.maxValThreshold[k] = 100 } return &result } func (m *ModelProcessDetail) Init() tea.Cmd { return nil } func (m *ModelProcessDetail) Update(imsg tea.Msg) (tea.Model, tea.Cmd) { switch msg := imsg.(type) { case *ModelMsg: m.modelMsg = msg m.handleModelMsg(msg) } return m, nil } func (m *ModelProcessDetail) handleModelMsg(msg *ModelMsg) { find := false var process *backend.DCUProcessInfo for dcu, pids := range msg.DCUPidInfo { for _, v := range pids { if v.Info.Pid == m.Pid { find = true process = &v if m.Cmd == "" { m.Cmd = v.Info.Cmd m.dcu = dcu } if v.Info.ContInfo != nil { m.docker = v.Info.ContInfo.Name } break } } if find { break } } var cpu, mem, dcu, dcumem float64 if process == nil { m.Cmd = MPDProcess m.CPUPercent = 0 m.MemPercent = 0 cpu = 0 mem = 0 } else { cpu = process.Info.CPU mem = float64(process.Info.Mem) m.CPUPercent = cpu m.MemPercent = mem m.t = process.Info.Time } qinfo, lock := msg.DCUInfo.GetQuitInfo() dcuInfo, have := qinfo[m.dcu] if !have { dcu = 0 dcumem = 0 m.DCUPercent = 0 m.DCUMemPercent = 0 } else { dcumem = float64(dcuInfo.MemUsedPerent) dcu = float64(dcuInfo.DCUUTil) m.DCUPercent = dcu m.DCUMemPercent = dcumem } m.DCUMemTotal = utils.MemorySize{Unit: utils.Byte, Num: dcuInfo.MemTotal} m.DCUMemUsed = utils.MemorySize{Unit: utils.Byte, Num: dcuInfo.MemUsed} lock.Unlock() m.MemTotal = utils.MemorySize{Unit: utils.Byte, Num: msg.systemInfo.MemTotal} m.maxVal[0] = max(m.maxVal[0], cpu) m.maxVal[1] = max(m.maxVal[1], mem) m.maxVal[2] = max(m.maxVal[2], dcu) m.maxVal[3] = max(m.maxVal[3], dcumem) m.DCU.Update(MyTimeChartMsg{Reset: false, Points: []tchart.TimePoint{{Time: msg.t, Value: dcu}}}) m.DCUMem.Update(MyTimeChartMsg{Reset: false, Points: []tchart.TimePoint{{Time: msg.t, Value: dcumem}}}) m.Mem.Update(MyTimeChartMsg{Reset: false, Points: []tchart.TimePoint{{Time: msg.t, Value: mem}}}) m.cpuPoints.Add(tchart.TimePoint{Time: msg.t, Value: cpu}) if m.cpuPoints.Size() > m.cpuPointsNum { m.cpuPoints.Remove(0) } if m.CPU.max < cpu { // 需要更新 if int(math.Ceil(cpu))%50 == 0 { m.maxValThreshold[0] = math.Ceil(cpu) } else { m.maxValThreshold[0] = float64(int(math.Ceil(cpu))/50)*50 + 50 } m.CPU = NewTimeChart(m.w[0], m.h[0], 0, m.maxValThreshold[0], []lipgloss.Color{lipgloss.Color("#00ff00ff")}) m.CPU.Update(MyTimeChartMsg{Reset: false, Points: m.cpuPoints.Values()}) } else { m.CPU.Update(MyTimeChartMsg{Reset: false, Points: []tchart.TimePoint{{Time: msg.t, Value: cpu}}}) } } func (m *ModelProcessDetail) View() string { sb := strings.Builder{} sb.WriteString(m.Header) sb.WriteString(myBorder.Left) sb.WriteString(FormatStr(fmt.Sprintf("%d", m.dcu), 4, lipgloss.Right)) sb.WriteByte(' ') sb.WriteString(FormatStr(fmt.Sprintf("%d", m.Pid), 7, lipgloss.Left)) sb.WriteByte(' ') sb.WriteString(FormatStr(backend.User, 8, lipgloss.Left)) sb.WriteByte(' ') sb.WriteString(FormatStr(m.DCUMemUsed.HumanReadStr(1), 9, lipgloss.Left)) sb.WriteByte(' ') sb.WriteString(FormatStr(fmt.Sprintf("%.1f", m.CPUPercent), 5, lipgloss.Left)) sb.WriteByte(' ') sb.WriteString(FormatStr(fmt.Sprintf("%.1f", m.MemPercent), 5, lipgloss.Left)) sb.WriteByte(' ') sb.WriteString(FormatStr(m.t, 8, lipgloss.Left)) sb.WriteByte(' ') if m.docker == "" { sb.WriteString(FormatStr(" - ", 19, lipgloss.Left)) } else { sb.WriteString(FormatStr(m.docker, 19, lipgloss.Left)) } sb.WriteByte(' ') sb.WriteString(FormatStr(m.Cmd, m.width-3-lipgloss.Width(MPDHeader)+lipgloss.Width("Command"), lipgloss.Left)) sb.WriteByte(' ') sb.WriteString(myBorder.Left) sb.WriteByte('\n') sb.WriteString(m.lines[2]) sb.WriteByte('\n') style := lipgloss.NewStyle() cpuTitle := fmt.Sprintf(" Max CPU: %.1f%%\n CPU: %.1f%%", m.maxVal[0], m.CPUPercent) memUsed := utils.MemorySize{Unit: utils.Byte, Num: (uint64(m.MemPercent*10000) * m.MemTotal.Num) / 1000000} memMax := utils.MemorySize{Unit: utils.Byte, Num: (uint64(m.maxVal[1]*10000) * m.MemTotal.Num) / 1000000} memTitle := fmt.Sprintf(" Host-Mem: %s (%.1f%%)\n Max Host-Mem: %s (%.1f%%) / %s", memUsed.HumanReadStr(1), m.MemPercent, memMax.HumanReadStr(1), m.maxVal[1], m.MemTotal.HumanReadStr(1)) dcuTitle := fmt.Sprintf(" DCU: %.1f%%\n Max DCU: %.1f%%", m.DCUPercent, m.maxVal[2]) dcuMemMax := utils.MemorySize{Unit: utils.Byte, Num: (uint64(m.maxVal[3]*100) * m.DCUMemTotal.Num / 10000)} dcuMemTitle := fmt.Sprintf(" Max DCU-Mem: %s (%.1f%%) / %s\n DCU-Mem: %s (%.1f%%)", dcuMemMax.HumanReadStr(1), m.maxVal[3], m.DCUMemTotal.HumanReadStr(1), m.DCUMemUsed.HumanReadStr(1), m.DCUMemPercent) CPU := style.Border(lipgloss.NormalBorder(), false, true, false).Render(StackPosition(cpuTitle, m.CPU.View(), lipgloss.Top, lipgloss.Left)) CPU = StackPosition(fmt.Sprintf("├%s", LowLeightStyle.Render(fmt.Sprintf("%d%%", int(m.maxValThreshold[0]/2)))), CPU, lipgloss.Center, lipgloss.Left) DCUM := style.Border(lipgloss.NormalBorder(), false, true, false, false).Render(StackPosition(dcuMemTitle, m.DCUMem.View(), lipgloss.Top, lipgloss.Left)) Mem := style.Border(lipgloss.NormalBorder(), false, true, false).Render(StackPosition(memTitle, m.Mem.View(), lipgloss.Bottom, lipgloss.Left)) DCU := style.Border(lipgloss.NormalBorder(), false, true, false, false).Render(StackPosition(dcuTitle, m.DCU.View(), lipgloss.Bottom, lipgloss.Left)) sb.WriteString(lipgloss.JoinVertical(lipgloss.Left, lipgloss.JoinHorizontal(lipgloss.Top, CPU, DCUM), m.lines[3], lipgloss.JoinHorizontal(lipgloss.Top, Mem, DCU), m.lines[4])) return sb.String() }