Commit 9608d37d authored by liming6's avatar liming6
Browse files

fix 修复折线图的bug

parent acd70ae1
......@@ -26,9 +26,8 @@ const (
BottomLine = `╞═══════════════════════════════╧══════════════════════╧══════════════════════╪`
OtherWidth = 8
StaticWidth = 87 // 固定表格的宽度
ProgressMaxWidth = 105 // 进度条最大宽度
ProgressMinWidth = 15 // 进度条最小宽度
StaticWidth = 79 // 固定表格的宽度
ProgressMinWidth = 15 // 进度条最小宽度
)
func (m *ModelDCUInfo) Init() tea.Cmd {
......@@ -36,10 +35,8 @@ func (m *ModelDCUInfo) Init() tea.Cmd {
return tea.Quit
}
m.proWidth = ProgressMinWidth
if m.width > StaticWidth+ProgressMaxWidth {
m.proWidth = ProgressMaxWidth
} else {
m.proWidth = m.width - StaticWidth
if m.width > StaticWidth+ProgressMinWidth+OtherWidth {
m.proWidth = m.width - OtherWidth - StaticWidth
}
m.pro = progress.New(progress.WithDefaultGradient(), progress.WithWidth(m.proWidth))
return nil
......@@ -127,7 +124,7 @@ func (m *ModelDCUInfo) View() string {
if len(infos) > 0 {
lineWidth = lipgloss.Width(infos[0])
}
subLen := lineWidth - StaticWidth + OtherWidth - 1
subLen := lineWidth - StaticWidth - 1
if subLen <= 0 {
subLen = 0
}
......
......@@ -44,7 +44,7 @@ func NewModelMain(width, height int) ModelMain {
result.height = height
result.Header = &ModelHeader{}
result.DCUInfo = &ModelDCUInfo{width: width, height: height, info: make(map[int]backend.DCUInfo)}
result.SysLoad = NewModelSysLoad(100)
result.SysLoad = NewModelSysLoad(width)
return result
}
......@@ -95,13 +95,13 @@ func (m *ModelMain) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
case TickMsg: // 定时事件
m.index++
updateModelInfo(m.modelMsg, m.index, time.Time(msg))
cmds := make([]tea.Cmd, 0)
header, cmdHeader := m.Header.Update(m.modelMsg)
dcuInfo, dcuHeader := m.DCUInfo.Update(m.modelMsg)
header, _ := m.Header.Update(m.modelMsg)
dcuInfo, _ := m.DCUInfo.Update(m.modelMsg)
sysLoad, _ := m.SysLoad.Update(m.modelMsg)
m.Header = header.(*ModelHeader)
m.DCUInfo = dcuInfo.(*ModelDCUInfo)
cmds = append(cmds, cmdHeader, dcuHeader, tickCmd())
return m, tea.Batch(cmds...)
m.SysLoad = sysLoad.(*ModelSysLoad)
return m, tickCmd()
case ModelMsg: // 初始化
header, _ := m.Header.Update(m.modelMsg)
dcuInfo, _ := m.DCUInfo.Update(m.modelMsg)
......@@ -157,7 +157,7 @@ func initModelInfo(model *ModelMsg) error {
func updateModelInfo(modelMsg *ModelMsg, index uint64, t time.Time) {
modelMsg.index = index
modelMsg.t = t
if modelMsg.index%20 == 0 {
if modelMsg.index%60 == 0 {
backend.UpdateDCUInfo(true)
} else {
backend.UpdateDCUInfo(false)
......
......@@ -6,6 +6,7 @@ import (
"get-container/cmd/dcutop/tchart"
"get-container/utils"
"maps"
"strings"
"sync"
"time"
......@@ -18,21 +19,23 @@ import (
const (
SysLoadHeight = 5 // 固定图表高度
SysLoadWidth = 77 // 固定图表宽度,不包含左右的边框
SysLoadCap = 500 // 记录
SysLoadCap = 600 // 记录
)
// 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
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
topLine string
bottomLine string
style lipgloss.Style
width int // 组件总宽度
}
type SysLoadInfo struct {
......@@ -50,6 +53,7 @@ type SysLoadInfo struct {
func NewModelSysLoad(width int) *ModelSysLoad {
result := ModelSysLoad{}
result.width = width
result.Cache = make(map[int]backend.DCUInfo)
result.SysInfo = linkedhashmap.New[time.Time, SysLoadInfo]()
result.Keys = binaryheap.NewWith(func(a, b time.Time) int {
......@@ -61,11 +65,14 @@ func NewModelSysLoad(width int) *ModelSysLoad {
}
return 0
})
subLine := width - StaticWidth - 1
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.DCU = New(subLine, SysLoadHeight, 0, 100, map[string]lipgloss.Color{"default": lipgloss.Color("#00ff00ff")})
result.DCUMem = New(subLine, SysLoadHeight, 0, 100, map[string]lipgloss.Color{"default": lipgloss.Color("#eeff00ff")})
result.topLine = myBorder.MiddleLeft + genXAxis(SysLoadWidth) + myBorder.Middle + genXAxis(subLine) + myBorder.MiddleRight
result.bottomLine = "╞" + strings.Repeat(myBorder.Bottom, SysLoadWidth) + "╧" + strings.Repeat(myBorder.Bottom, subLine) + "╡"
result.style = lipgloss.NewStyle()
return &result
}
......@@ -118,10 +125,12 @@ func (m *ModelSysLoad) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
}
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)
memUsed := utils.MemorySize{Num: m.current.MemUsed, Unit: utils.Byte}
load := fmt.Sprintf(" Load Average: %.2f %.2f %.2f", m.current.Load1, m.current.Load5, m.current.Load15)
mem := fmt.Sprintf(" MEM: %s (%.1f%%)", memUsed.HumanReadStr(), m.current.MemUsedPercent)
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)
......@@ -129,13 +138,13 @@ func (m *ModelSysLoad) View() string {
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)
mem = m.style.Border(myBorder, false, true, false).Render(mem)
dcuMem = m.style.Border(myBorder, false, true, false, false).Render(dcuMem)
dcu = m.style.Border(myBorder, false, true, false, false).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)
return lipgloss.JoinVertical(lipgloss.Left, up, m.topLine, down, m.bottomLine)
}
// updateInfo
......@@ -159,7 +168,7 @@ func (m *ModelSysLoad) updateInfo(t time.Time) {
for k, v := range m.Cache {
s.DCUMemUsageAvg += v.MemUsedPerent
s.DCUMemUsage[k] = v.MemUsedPerent
s.DCUMemUsageAvg += v.DCUUTil
s.DCUUsageAvg += v.DCUUTil
s.DCUUsage[k] = v.DCUUTil
}
l := len(m.Cache)
......@@ -188,6 +197,14 @@ func (m *ModelSysLoad) updateInfo(t time.Time) {
m4, _ := m.DCUMem.Update(MyTimeChartMsg{Points: map[string][]tchart.TimePoint{"default": {{Time: t, Value: float64(s.DCUMemUsageAvg)}}}})
m.DCUMem = m4.(*MyTimeChart)
}()
// todo
// 存放数据
m.SysInfo.Put(t, s)
m.Keys.Push(t)
if m.Keys.Size() > SysLoadCap {
delKey, have := m.Keys.Pop()
if have {
m.SysInfo.Remove(delKey)
}
}
wg.Wait()
}
......@@ -99,7 +99,7 @@ func New(width, height int, vmin, vmax float64, dataSet map[string]lipgloss.Colo
copy(result.dataSet[k], initPoints)
}
result.lockDataSet.Unlock()
s := tchart.New(width, height,
s := tchart.New(width, height+1,
tchart.WithLineStyle(runes.ThinLineStyle),
tchart.WithZoneManager(zoneManager),
tchart.WithYRange(vmin, vmax),
......@@ -125,7 +125,7 @@ func New(width, height int, vmin, vmax float64, dataSet map[string]lipgloss.Colo
func (m *MyTimeChart) PutPoint(points map[string][]tchart.TimePoint) {
// 更新chart
s := tchart.New(m.width, m.height,
s := tchart.New(m.width, m.height+1,
tchart.WithLineStyle(runes.ThinLineStyle),
tchart.WithZoneManager(m.zM),
tchart.WithYRange(m.min, m.max),
......@@ -189,6 +189,7 @@ func (m *MyTimeChart) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
func (m *MyTimeChart) View() string {
rl := m.lockChart.RLocker()
rl.Lock()
defer rl.Unlock()
return m.zM.Scan(m.chart.View())
str := m.zM.Scan(m.chart.View())
rl.Unlock()
return str[m.width+1:]
}
......@@ -52,20 +52,24 @@ func TestModel(t *testing.T) {
}
func TestMyTimeChart(t *testing.T) {
chart := New(100, 20, 0.0, 100.0, map[string]lipgloss.Color{"default": lipgloss.Color("#ff2222ff"), "other": lipgloss.Color("#0037ffff")})
chart := New(100, 5, 0.0, 100.0, map[string]lipgloss.Color{"default": lipgloss.Color("#ff2222ff"), "other": lipgloss.Color("#0037ffff")})
chart.Init()
s := chart.View()
t.Logf("%s", s)
t.Logf("\n%s", s)
time.Sleep(time.Second)
now := time.Now()
points := make(map[string][]tchart.TimePoint)
points["default"] = []tchart.TimePoint{{Time: now, Value: 20.0}}
points["other"] = []tchart.TimePoint{{Time: now, Value: 30.0}}
points["default"] = []tchart.TimePoint{{Time: now, Value: 94.0}}
points["other"] = []tchart.TimePoint{{Time: now, Value: 94.0}}
chart.Update(MyTimeChartMsg{Points: points})
_ = len(chart.dataSet)
t.Logf("%s", chart.View())
time.Sleep(time.Second)
now = time.Now()
points = make(map[string][]tchart.TimePoint)
points["default"] = []tchart.TimePoint{{Time: now, Value: 0.0}}
points["other"] = []tchart.TimePoint{{Time: now, Value: 0.0}}
chart.Update(MyTimeChartMsg{Points: points})
t.Logf("\n%s", chart.View())
t.Logf("ok")
}
func TestBinaryHeap(t *testing.T) {
......
......@@ -9,6 +9,34 @@ import (
type StorageCapacityUnit uint64
func (u StorageCapacityUnit) String() string {
switch u {
case Byte:
return "Byte"
case KB:
return "KB"
case MB:
return "MB"
case GB:
return "GB"
case TB:
return "TB"
case PB:
return "PB"
case KiB:
return "KiB"
case MiB:
return "MiB"
case GiB:
return "GiB"
case TiB:
return "TiB"
case PiB:
return "PiB"
}
return "unknow unit"
}
const (
Byte StorageCapacityUnit = 1
KB StorageCapacityUnit = Byte * 1000
......@@ -35,6 +63,22 @@ type MemorySize struct {
Unit StorageCapacityUnit
}
func (s MemorySize) HumanReadStr() string {
total := s.Num * uint64(s.Unit)
units := []StorageCapacityUnit{Byte, KiB, MiB, GiB, TiB, PiB}
var target StorageCapacityUnit
for k, v := range units {
if total/uint64(v) < 1 {
target = units[k-1]
break
} else {
target = v
}
}
num := float64(total) / float64(target)
return fmt.Sprintf("%.3f %s", num, target)
}
func ParseUnit(s string) (StorageCapacityUnit, error) {
s = strings.Trim(strings.TrimSpace(s), "\n")
s = strings.ReplaceAll(s, " ", "")
......@@ -46,7 +90,7 @@ func ParseUnit(s string) (StorageCapacityUnit, error) {
// [KB K '' B]
// [B '' '' B]
matchs := ReUnit.FindStringSubmatch(s)
if matchs == nil || len(matchs) < 4 {
if len(matchs) < 4 {
return 0, fmt.Errorf("invalid storage size unit: %s", s)
}
isI := matchs[2] == "i"
......@@ -91,7 +135,7 @@ func ParseMemorySize(s string) (*MemorySize, error) {
return nil, fmt.Errorf("invalid memory size format: %s", s)
}
matchs := ReStorageSize.FindStringSubmatch(s)
if matchs == nil || len(matchs) < 3 {
if len(matchs) < 3 {
return nil, fmt.Errorf("invalid memory size format: %s", s)
}
......
......@@ -53,3 +53,11 @@ func TestParseMemorySize(t *testing.T) {
}
}
}
func TestHumanReadStr(t *testing.T) {
ms := MemorySize{Num: 1025, Unit: Byte}
t.Log(ms.HumanReadStr())
ms.Num = 1025
ms.Unit = PiB
t.Log(ms.HumanReadStr())
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment