Commit 0b52579d authored by liming6's avatar liming6
Browse files

fix 功耗监视器添加刻度线

parent 72702c5d
...@@ -35,6 +35,7 @@ type ModelPowerDetail struct { ...@@ -35,6 +35,7 @@ type ModelPowerDetail struct {
DisplayAvg bool // 显示功率平均值 DisplayAvg bool // 显示功率平均值
lock sync.RWMutex // 保护DataCache和Charts的锁 lock sync.RWMutex // 保护DataCache和Charts的锁
ScreenSplit map[int][][]ScreenArea // 记录屏幕尺寸 ScreenSplit map[int][][]ScreenArea // 记录屏幕尺寸
title string // 标题字符串
} }
type ChartAndArea struct { type ChartAndArea struct {
...@@ -72,9 +73,20 @@ var ( ...@@ -72,9 +73,20 @@ var (
) )
func (m *ModelPowerDetail) Init() tea.Cmd { func (m *ModelPowerDetail) Init() tea.Cmd {
m.height--
for i := 1; i < 10; i++ { for i := 1; i < 10; i++ {
m.ScreenSplit[i] = m.SplitScreen(i) m.ScreenSplit[i] = m.SplitScreen(i)
} }
m.height++
m.title = "DCU Power Monitor"
s := "Press <P> and <Space> to select dcu, Parse <A> to show average"
extLen := m.width - len(m.title) - len(s)
if extLen > 0 {
m.title += strings.Repeat(" ", extLen) + s
}
m.title = HeightLightStyle.Render(m.title) + "\n"
return nil return nil
} }
...@@ -177,18 +189,18 @@ func (m *ModelPowerDetail) Update(imsg tea.Msg) (tea.Model, tea.Cmd) { ...@@ -177,18 +189,18 @@ func (m *ModelPowerDetail) Update(imsg tea.Msg) (tea.Model, tea.Cmd) {
} }
chart, have := m.Charts[-1] chart, have := m.Charts[-1]
if !have { if !have {
c := NewTimeChart(m.width, m.height-1, 0, float64(pwrCap), []lipgloss.Color{lipgloss.Color("#ff0000"), lipgloss.Color("#00ff00ff")}) c := NewTimeChart(m.width-1, m.height-2, 0, float64(pwrCap), []lipgloss.Color{lipgloss.Color("#ff0000"), lipgloss.Color("#00ff00ff")})
chart = &ChartAndArea{chart: c, area: m.ScreenSplit[1][0][0], h: 0, l: 0} chart = &ChartAndArea{chart: c, area: m.ScreenSplit[1][0][0], h: 0, l: 0}
m.Charts[-1] = chart m.Charts[-1] = chart
} }
chart.chart.PutPoint([]tchart.TimePoint{{Time: msg.t, Value: float64(powerTotal)}}) chart.chart.PutPoint([]tchart.TimePoint{{Time: msg.t, Value: float64(powerTotal)}})
chart, have = m.Charts[-2] if extAvg {
if !have { chart, have = m.Charts[-2]
c := NewTimeChart(m.width, m.height-1, 0, float64(pwrCap), []lipgloss.Color{lipgloss.Color("#ff0000"), lipgloss.Color("#00ff00ff")}) if have {
chart = &ChartAndArea{chart: c, area: m.ScreenSplit[1][0][0], h: 0, l: 0} chart.chart.PutPoint([]tchart.TimePoint{{Time: msg.t, Value: float64(selectedTotal)}})
m.Charts[-2] = chart m.Charts[-2] = chart
}
} }
chart.chart.PutPoint([]tchart.TimePoint{{Time: msg.t, Value: float64(selectedTotal)}})
m.lock.Unlock() m.lock.Unlock()
m.PowerCap[-1] = pwrCap m.PowerCap[-1] = pwrCap
for k := range m.Pids { for k := range m.Pids {
...@@ -396,11 +408,13 @@ func (m *ModelPowerDetail) UpdateCharts(index int, isAdd bool) { ...@@ -396,11 +408,13 @@ func (m *ModelPowerDetail) UpdateCharts(index int, isAdd bool) {
m.DataCache[-2] = l m.DataCache[-2] = l
chart, have := m.Charts[-2] chart, have := m.Charts[-2]
if !have { if !have {
c := NewTimeChart(m.width, m.height-1, 0, float64(m.PowerCap[-1]), []lipgloss.Color{lipgloss.Color("#ff0000"), lipgloss.Color("#00ff00ff")}) c := NewTimeChart(m.width-1, m.height-2, 0, float64(m.PowerCap[-1]), []lipgloss.Color{lipgloss.Color("#ff0000"), lipgloss.Color("#00ff00ff")})
chart = &ChartAndArea{chart: c, area: m.ScreenSplit[1][0][0], h: 0, l: 0} chart = &ChartAndArea{chart: c, area: m.ScreenSplit[1][0][0], h: 0, l: 0}
m.Charts[-2] = chart m.Charts[-2] = chart
} }
chart.chart.ResetPutPoint(points) chart.chart.ResetPutPoint(points)
} else {
delete(m.Charts, -2)
} }
m.lock.Unlock() m.lock.Unlock()
} }
...@@ -415,16 +429,23 @@ func (m *ModelPowerDetail) RenderCharts() string { ...@@ -415,16 +429,23 @@ func (m *ModelPowerDetail) RenderCharts() string {
} }
slices.Sort(dcuSelected) slices.Sort(dcuSelected)
selectNum := len(dcuSelected) selectNum := len(dcuSelected)
mstr := LowLeightStyle.Render(fmt.Sprintf("%.0fW", m.PowerCap[-1]/2))
// 显示全部平均功率 // 显示全部平均功率
if selectNum == 0 || (selectNum == len(m.dcuIndex) && m.DisplayAvg) { if selectNum == 0 || (selectNum == len(m.dcuIndex) && m.DisplayAvg) {
x := genXAxis(m.width) chart := m.Charts[-1].chart
chart := m.Charts[-1].chart.View() x := genXAxis(chart.width, B)
return chart + "\n" + x str := StackPosition(mstr, chart.ViewWithMiddleLine(), lipgloss.Center, lipgloss.Left) + "\n" + x
str = StackPosition("DCU: All Avg", str, lipgloss.Top, lipgloss.Left)
str = MPStyle.Render(str)
return str
} else if m.DisplayAvg { } else if m.DisplayAvg {
// 显示选中dcu的平均功率图表 // 显示选中dcu的平均功率图表
x := genXAxis(m.width) chart := m.Charts[-2].chart
chart := m.Charts[-2].chart.View() x := genXAxis(chart.width, B)
return chart + "\n" + x str := StackPosition(mstr, chart.ViewWithMiddleLine(), lipgloss.Center, lipgloss.Left) + "\n" + x
strDCU := strings.ReplaceAll(strings.Trim(fmt.Sprintf("%v", dcuSelected), "[]"), " ", ",")
str = StackPosition(fmt.Sprintf("DCU: %s Avg", strDCU), str, lipgloss.Top, lipgloss.Left)
return MPStyle.Render(str)
} else { } else {
// 显示选中dcu的图表 // 显示选中dcu的图表
areas := m.ScreenSplit[selectNum] areas := m.ScreenSplit[selectNum]
...@@ -435,10 +456,10 @@ func (m *ModelPowerDetail) RenderCharts() string { ...@@ -435,10 +456,10 @@ func (m *ModelPowerDetail) RenderCharts() string {
strs := make([]string, 0, selectNum) strs := make([]string, 0, selectNum)
for _, v := range dcuSelected { for _, v := range dcuSelected {
chart := m.Charts[v].chart chart := m.Charts[v].chart
str := chart.View() str := StackPosition(mstr, chart.ViewWithMiddleLine(), lipgloss.Center, lipgloss.Left)
str = StackPosition(fmt.Sprintf("DCU: %d", v), str, lipgloss.Top, lipgloss.Left) str = StackPosition(fmt.Sprintf("DCU: %d", v), str, lipgloss.Top, lipgloss.Left)
str += "\n" str += "\n"
str += genXAxis(chart.width) str += genXAxis(chart.width, B)
strs = append(strs, MPStyle.Render(str)) strs = append(strs, MPStyle.Render(str))
} }
lines := make(map[int]string) lines := make(map[int]string)
...@@ -452,17 +473,29 @@ func (m *ModelPowerDetail) RenderCharts() string { ...@@ -452,17 +473,29 @@ func (m *ModelPowerDetail) RenderCharts() string {
lines[po[0]] = lipgloss.JoinHorizontal(lipgloss.Top, l, v) lines[po[0]] = lipgloss.JoinHorizontal(lipgloss.Top, l, v)
} }
} }
return lipgloss.JoinVertical(lipgloss.Left, lines[0], lines[1], lines[2]) switch len(lines) {
case 1:
return lines[0]
case 2:
return lipgloss.JoinVertical(lipgloss.Left, lines[0], lines[1])
case 3:
return lipgloss.JoinVertical(lipgloss.Left, lines[0], lines[1], lines[2])
default:
panic("error line number")
}
} }
} }
func (m *ModelPowerDetail) View() string { func (m *ModelPowerDetail) View() string {
rl := m.lock.RLocker()
rl.Lock()
defer rl.Unlock()
if m.DisplaySummary { if m.DisplaySummary {
summary := m.SummaryInfo() summary := m.SummaryInfo()
charts := m.RenderCharts() charts := m.RenderCharts()
return StackPosition(summary, charts, lipgloss.Center, lipgloss.Center) return m.title + StackPosition(summary, charts, lipgloss.Center, lipgloss.Center)
} else { } else {
return m.RenderCharts() return m.title + m.RenderCharts()
} }
} }
...@@ -607,7 +640,7 @@ func (m *ModelPowerDetail) SplitScreen(num int) [][]ScreenArea { ...@@ -607,7 +640,7 @@ func (m *ModelPowerDetail) SplitScreen(num int) [][]ScreenArea {
result[h][l].h++ result[h][l].h++
} }
} }
for h := range result[:1] { for h := range result[:2] {
for l := 0; l < m.width%3; l++ { for l := 0; l < m.width%3; l++ {
result[h][l].w++ result[h][l].w++
} }
......
...@@ -19,6 +19,7 @@ import ( ...@@ -19,6 +19,7 @@ import (
const ( const (
A = "├" A = "├"
B = "└"
) )
var ( var (
...@@ -26,12 +27,16 @@ var ( ...@@ -26,12 +27,16 @@ var (
) )
// genXAxis 生成X轴,参数l是x轴的长度 // genXAxis 生成X轴,参数l是x轴的长度
func genXAxis(l int) string { func genXAxis(l int, s ...string) string {
if s == nil {
s = make([]string, 0, 1)
s = append(s, A)
}
t60 := l / 30 t60 := l / 30
t30 := l >= 18 t30 := l >= 18
var result string var result string
if t30 { if t30 {
result = A + strings.Repeat("─", 14) result = s[0] + strings.Repeat("─", 14)
result = axisFStyle.Render("30s") + result result = axisFStyle.Render("30s") + result
} else { } else {
return strings.Repeat("─", l) return strings.Repeat("─", l)
...@@ -51,7 +56,7 @@ func genXAxis(l int) string { ...@@ -51,7 +56,7 @@ func genXAxis(l int) string {
break break
} }
// 渲染标记 // 渲染标记
result = timeStr + A + strings.Repeat("─", targetLen-lipgloss.Width(result)-timeStrLen-1) + result result = timeStr + s[0] + strings.Repeat("─", targetLen-lipgloss.Width(result)-timeStrLen-1) + result
} }
return result return result
} }
...@@ -107,6 +112,7 @@ type MyTimeChart struct { ...@@ -107,6 +112,7 @@ type MyTimeChart struct {
max, min float64 // y轴的最值 max, min float64 // y轴的最值
lockPoints sync.RWMutex // 保护dataSet的并发读写 lockPoints sync.RWMutex // 保护dataSet的并发读写
color []lipgloss.Color color []lipgloss.Color
middleLine bool // 是否添加中间线
} }
// New 新建图表,color的长度如果为1,则图表的颜色为color[0],如果color的长度为heigh,则图表的颜色随高度变化 // New 新建图表,color的长度如果为1,则图表的颜色为color[0],如果color的长度为heigh,则图表的颜色随高度变化
...@@ -121,6 +127,7 @@ func NewTimeChart(width, height int, vmin, vmax float64, color []lipgloss.Color) ...@@ -121,6 +127,7 @@ func NewTimeChart(width, height int, vmin, vmax float64, color []lipgloss.Color)
result.lockPoints = sync.RWMutex{} result.lockPoints = sync.RWMutex{}
result.lockPoints.Lock() result.lockPoints.Lock()
result.points = linkedlist.New[tchart.TimePoint]() result.points = linkedlist.New[tchart.TimePoint]()
result.middleLine = false
now := time.Now() now := time.Now()
// 用最小值填充空白点 // 用最小值填充空白点
t := result.width*2 + 1 t := result.width*2 + 1
...@@ -284,13 +291,11 @@ func (m *MyTimeChart) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) { ...@@ -284,13 +291,11 @@ func (m *MyTimeChart) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
return m, nil return m, nil
} }
func (m *MyTimeChart) ViewWithColor(color []lipgloss.Color) string { func (m *MyTimeChart) genLines() []string {
str := m.zM.Scan(m.chart.View()) str := m.zM.Scan(m.chart.View())
sb := strings.Builder{} sb := strings.Builder{}
sb.WriteString(str[m.width+1:]) sb.WriteString(str[m.width+1:])
str = sb.String() str = sb.String()
// return str
lines := strings.Split(strings.Trim(str, "\n"), "\n") lines := strings.Split(strings.Trim(str, "\n"), "\n")
runes := make([][]rune, len(lines)) runes := make([][]rune, len(lines))
for k, v := range lines { for k, v := range lines {
...@@ -371,6 +376,12 @@ func (m *MyTimeChart) ViewWithColor(color []lipgloss.Color) string { ...@@ -371,6 +376,12 @@ func (m *MyTimeChart) ViewWithColor(color []lipgloss.Color) string {
for k, v := range runes { for k, v := range runes {
resultLines[k] = string(v) resultLines[k] = string(v)
} }
return resultLines
}
func (m *MyTimeChart) ViewWithColor(color []lipgloss.Color) string {
resultLines := m.genLines()
height := len(resultLines)
style := lipgloss.NewStyle() style := lipgloss.NewStyle()
if len(color) == height { if len(color) == height {
for k, v := range resultLines { for k, v := range resultLines {
...@@ -381,103 +392,83 @@ func (m *MyTimeChart) ViewWithColor(color []lipgloss.Color) string { ...@@ -381,103 +392,83 @@ func (m *MyTimeChart) ViewWithColor(color []lipgloss.Color) string {
} }
func (m *MyTimeChart) View() string { func (m *MyTimeChart) View() string {
str := m.zM.Scan(m.chart.View()) resultLines := m.genLines()
sb := strings.Builder{} height := len(resultLines)
sb.WriteString(str[m.width+1:]) switch len(m.color) {
str = sb.String() case 1:
// return str style := lipgloss.NewStyle().Foreground(m.color[0])
return style.Render(strings.Join(resultLines, "\n"))
lines := strings.Split(strings.Trim(str, "\n"), "\n") case height:
runes := make([][]rune, len(lines)) style := lipgloss.NewStyle()
for k, v := range lines { for k, v := range resultLines {
runes[k] = []rune(v) resultLines[k] = style.Foreground(m.color[k]).Render(v)
}
width := lipgloss.Width(str)
height := lipgloss.Height(str)
for col := range width {
firstLine := -1
var charStat utils.CharType = utils.CharEmpty
for line := range height {
c := runes[line][col]
charType := utils.GetCharType(c)
if charType != utils.CharEmpty && firstLine == -1 {
firstLine = line
}
if firstLine == -1 {
continue
}
if firstLine == line {
// 遇到了一列的第一个非空字符
if t, have := utils.MM[c]; have {
runes[line][col] = t
}
charStat = utils.CharTypeOr(charStat, utils.GetCharType(runes[line][col]))
} else {
// 第一个非空字符下面的字符
switch charType {
case utils.CharEmpty:
switch charStat {
case utils.CharLeft:
runes[line][col] = utils.LeftFullMW
case utils.CharRight:
runes[line][col] = utils.RightFullMW
case utils.CharFull:
runes[line][col] = utils.FullMW
}
if t, have := utils.MM[runes[line][col]]; have {
runes[line][col] = t
}
case utils.CharLeft:
switch utils.CharTypeOr(charStat, utils.CharLeft) {
case utils.CharLeft:
runes[line][col] = utils.LeftFullMW
case utils.CharRight:
runes[line][col] = utils.RightFullMW
case utils.CharFull:
runes[line][col] = utils.FullMW
}
charStat = utils.CharTypeOr(charStat, utils.CharLeft)
if t, have := utils.MM[runes[line][col]]; have {
runes[line][col] = t
}
case utils.CharRight:
switch utils.CharTypeOr(charStat, utils.CharRight) {
case utils.CharLeft:
runes[line][col] = utils.LeftFullMW
case utils.CharRight:
runes[line][col] = utils.RightFullMW
case utils.CharFull:
runes[line][col] = utils.FullMW
}
charStat = utils.CharTypeOr(charStat, utils.CharRight)
if t, have := utils.MM[runes[line][col]]; have {
runes[line][col] = t
}
case utils.CharFull:
charStat = utils.CharFull
if t, have := utils.MM[runes[line][col]]; have {
runes[line][col] = t
}
}
}
} }
return strings.Join(resultLines, "\n")
default:
return strings.Join(resultLines, "\n")
} }
resultLines := make([]string, height) }
for k, v := range runes {
resultLines[k] = string(v) // ViewWithMiddleLine
func (m *MyTimeChart) ViewWithMiddleLine() string {
resultLines := m.genLines()
height := len(resultLines)
targetLine := 0
isMiddle := false
if height%2 == 0 {
targetLine = height/2 - 1
} else {
targetLine = height / 2
isMiddle = true
} }
switch len(m.color) { switch len(m.color) {
case 1: case 1:
style := lipgloss.NewStyle().Foreground(m.color[0]) style := lipgloss.NewStyle().Foreground(m.color[0])
return style.Render(strings.Join(resultLines, "\n")) for k, v := range resultLines {
if k == targetLine {
if isMiddle {
resultLines[k] = style.Strikethrough(true).Render(v)
style.Strikethrough(false)
} else {
resultLines[k] = style.Underline(true).Render(v)
style.Underline(false)
}
} else {
resultLines[k] = style.Render(v)
}
}
return strings.Join(resultLines, "\n")
case height: case height:
style := lipgloss.NewStyle() style := lipgloss.NewStyle()
for k, v := range resultLines { for k, v := range resultLines {
resultLines[k] = style.Foreground(m.color[k]).Render(v) if k == targetLine {
if isMiddle {
resultLines[k] = style.Foreground(m.color[k]).Strikethrough(true).Render(v)
style.Strikethrough(false)
} else {
resultLines[k] = style.Foreground(m.color[k]).Underline(true).Render(v)
style.Underline(false)
}
} else {
resultLines[k] = style.Foreground(m.color[k]).Render(v)
}
} }
return strings.Join(resultLines, "\n") return strings.Join(resultLines, "\n")
default: default:
style := lipgloss.NewStyle()
for k, v := range resultLines {
if k == targetLine {
if isMiddle {
resultLines[k] = style.Strikethrough(true).Render(v)
style.Strikethrough(false)
} else {
resultLines[k] = style.Underline(true).Render(v)
style.Underline(false)
}
} else {
resultLines[k] = style.Render(v)
}
}
return strings.Join(resultLines, "\n") return strings.Join(resultLines, "\n")
} }
} }
...@@ -110,10 +110,27 @@ func TestNewProcessEnv(t *testing.T) { ...@@ -110,10 +110,27 @@ func TestNewProcessEnv(t *testing.T) {
t.Log(pe.View()) t.Log(pe.View())
} }
func TestModelPower(t *testing.T) { func TestTimeChartLine(t *testing.T) {
a := `hello world chart1 := NewTimeChart(100, 20, 0, 100, nil)
time is comd` // chart2 := NewTimeChart(100, 20, 0, 100, nil)
b := "ok" now := time.Now()
s := StackPosition(b, a, lipgloss.Top, lipgloss.Left) points := make([]tchart.TimePoint, 0, 200)
t.Log("\n" + s) for i := range 200 {
points = append(points, tchart.TimePoint{Time: now.Add(time.Duration(-i) * time.Second), Value: rand.Float64() * 100})
}
style1 := lipgloss.NewStyle().Underline(true)
style2 := lipgloss.NewStyle().Strikethrough(true)
chart1.PutPoint(points)
str := chart1.View()
lines := strings.Split(str, "\n")
h := lipgloss.Height(str)
if h%2 == 0 {
lines[h/2-1] = style1.Render(lines[h/2-1])
t.Log(lines[h/2-1])
} else {
lines[h/2] = style2.Render(lines[h/2])
t.Log(lines[h/2])
}
str = strings.Join(lines, "\n")
t.Logf("\n%s\n", str)
} }
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