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

fix 功耗监视器添加刻度线

parent 72702c5d
......@@ -35,6 +35,7 @@ type ModelPowerDetail struct {
DisplayAvg bool // 显示功率平均值
lock sync.RWMutex // 保护DataCache和Charts的锁
ScreenSplit map[int][][]ScreenArea // 记录屏幕尺寸
title string // 标题字符串
}
type ChartAndArea struct {
......@@ -72,9 +73,20 @@ var (
)
func (m *ModelPowerDetail) Init() tea.Cmd {
m.height--
for i := 1; i < 10; 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
}
......@@ -177,18 +189,18 @@ func (m *ModelPowerDetail) Update(imsg tea.Msg) (tea.Model, tea.Cmd) {
}
chart, have := m.Charts[-1]
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}
m.Charts[-1] = chart
}
chart.chart.PutPoint([]tchart.TimePoint{{Time: msg.t, Value: float64(powerTotal)}})
chart, have = m.Charts[-2]
if !have {
c := NewTimeChart(m.width, m.height-1, 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}
m.Charts[-2] = chart
if extAvg {
chart, have = m.Charts[-2]
if have {
chart.chart.PutPoint([]tchart.TimePoint{{Time: msg.t, Value: float64(selectedTotal)}})
m.Charts[-2] = chart
}
}
chart.chart.PutPoint([]tchart.TimePoint{{Time: msg.t, Value: float64(selectedTotal)}})
m.lock.Unlock()
m.PowerCap[-1] = pwrCap
for k := range m.Pids {
......@@ -396,11 +408,13 @@ func (m *ModelPowerDetail) UpdateCharts(index int, isAdd bool) {
m.DataCache[-2] = l
chart, have := m.Charts[-2]
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}
m.Charts[-2] = chart
}
chart.chart.ResetPutPoint(points)
} else {
delete(m.Charts, -2)
}
m.lock.Unlock()
}
......@@ -415,16 +429,23 @@ func (m *ModelPowerDetail) RenderCharts() string {
}
slices.Sort(dcuSelected)
selectNum := len(dcuSelected)
mstr := LowLeightStyle.Render(fmt.Sprintf("%.0fW", m.PowerCap[-1]/2))
// 显示全部平均功率
if selectNum == 0 || (selectNum == len(m.dcuIndex) && m.DisplayAvg) {
x := genXAxis(m.width)
chart := m.Charts[-1].chart.View()
return chart + "\n" + x
chart := m.Charts[-1].chart
x := genXAxis(chart.width, B)
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 {
// 显示选中dcu的平均功率图表
x := genXAxis(m.width)
chart := m.Charts[-2].chart.View()
return chart + "\n" + x
chart := m.Charts[-2].chart
x := genXAxis(chart.width, B)
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 {
// 显示选中dcu的图表
areas := m.ScreenSplit[selectNum]
......@@ -435,10 +456,10 @@ func (m *ModelPowerDetail) RenderCharts() string {
strs := make([]string, 0, selectNum)
for _, v := range dcuSelected {
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 += "\n"
str += genXAxis(chart.width)
str += genXAxis(chart.width, B)
strs = append(strs, MPStyle.Render(str))
}
lines := make(map[int]string)
......@@ -452,17 +473,29 @@ func (m *ModelPowerDetail) RenderCharts() string {
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 {
rl := m.lock.RLocker()
rl.Lock()
defer rl.Unlock()
if m.DisplaySummary {
summary := m.SummaryInfo()
charts := m.RenderCharts()
return StackPosition(summary, charts, lipgloss.Center, lipgloss.Center)
return m.title + StackPosition(summary, charts, lipgloss.Center, lipgloss.Center)
} else {
return m.RenderCharts()
return m.title + m.RenderCharts()
}
}
......@@ -607,7 +640,7 @@ func (m *ModelPowerDetail) SplitScreen(num int) [][]ScreenArea {
result[h][l].h++
}
}
for h := range result[:1] {
for h := range result[:2] {
for l := 0; l < m.width%3; l++ {
result[h][l].w++
}
......
......@@ -19,6 +19,7 @@ import (
const (
A = "├"
B = "└"
)
var (
......@@ -26,12 +27,16 @@ var (
)
// 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
t30 := l >= 18
var result string
if t30 {
result = A + strings.Repeat("─", 14)
result = s[0] + strings.Repeat("─", 14)
result = axisFStyle.Render("30s") + result
} else {
return strings.Repeat("─", l)
......@@ -51,7 +56,7 @@ func genXAxis(l int) string {
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
}
......@@ -107,6 +112,7 @@ type MyTimeChart struct {
max, min float64 // y轴的最值
lockPoints sync.RWMutex // 保护dataSet的并发读写
color []lipgloss.Color
middleLine bool // 是否添加中间线
}
// New 新建图表,color的长度如果为1,则图表的颜色为color[0],如果color的长度为heigh,则图表的颜色随高度变化
......@@ -121,6 +127,7 @@ func NewTimeChart(width, height int, vmin, vmax float64, color []lipgloss.Color)
result.lockPoints = sync.RWMutex{}
result.lockPoints.Lock()
result.points = linkedlist.New[tchart.TimePoint]()
result.middleLine = false
now := time.Now()
// 用最小值填充空白点
t := result.width*2 + 1
......@@ -284,13 +291,11 @@ func (m *MyTimeChart) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
return m, nil
}
func (m *MyTimeChart) ViewWithColor(color []lipgloss.Color) string {
func (m *MyTimeChart) genLines() []string {
str := m.zM.Scan(m.chart.View())
sb := strings.Builder{}
sb.WriteString(str[m.width+1:])
str = sb.String()
// return str
lines := strings.Split(strings.Trim(str, "\n"), "\n")
runes := make([][]rune, len(lines))
for k, v := range lines {
......@@ -371,6 +376,12 @@ func (m *MyTimeChart) ViewWithColor(color []lipgloss.Color) string {
for k, v := range runes {
resultLines[k] = string(v)
}
return resultLines
}
func (m *MyTimeChart) ViewWithColor(color []lipgloss.Color) string {
resultLines := m.genLines()
height := len(resultLines)
style := lipgloss.NewStyle()
if len(color) == height {
for k, v := range resultLines {
......@@ -381,103 +392,83 @@ func (m *MyTimeChart) ViewWithColor(color []lipgloss.Color) string {
}
func (m *MyTimeChart) View() string {
str := m.zM.Scan(m.chart.View())
sb := strings.Builder{}
sb.WriteString(str[m.width+1:])
str = sb.String()
// return str
lines := strings.Split(strings.Trim(str, "\n"), "\n")
runes := make([][]rune, len(lines))
for k, v := range lines {
runes[k] = []rune(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
}
}
}
resultLines := m.genLines()
height := len(resultLines)
switch len(m.color) {
case 1:
style := lipgloss.NewStyle().Foreground(m.color[0])
return style.Render(strings.Join(resultLines, "\n"))
case height:
style := lipgloss.NewStyle()
for k, v := range resultLines {
resultLines[k] = style.Foreground(m.color[k]).Render(v)
}
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) {
case 1:
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:
style := lipgloss.NewStyle()
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")
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")
}
}
......@@ -110,10 +110,27 @@ func TestNewProcessEnv(t *testing.T) {
t.Log(pe.View())
}
func TestModelPower(t *testing.T) {
a := `hello world
time is comd`
b := "ok"
s := StackPosition(b, a, lipgloss.Top, lipgloss.Left)
t.Log("\n" + s)
func TestTimeChartLine(t *testing.T) {
chart1 := NewTimeChart(100, 20, 0, 100, nil)
// chart2 := NewTimeChart(100, 20, 0, 100, nil)
now := time.Now()
points := make([]tchart.TimePoint, 0, 200)
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