Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
liming6
dcu-process-montor
Commits
72702c5d
Commit
72702c5d
authored
Feb 26, 2026
by
liming6
Browse files
feature hytop添加功耗监控功能
parent
b7bee696
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
839 additions
and
106 deletions
+839
-106
cmd/cmd-wrapper/docker-wrapper/parse-arg.go
cmd/cmd-wrapper/docker-wrapper/parse-arg.go
+0
-53
cmd/hytop/tui/main.go
cmd/hytop/tui/main.go
+130
-43
cmd/hytop/tui/power.go
cmd/hytop/tui/power.go
+671
-0
cmd/hytop/tui/timechart.go
cmd/hytop/tui/timechart.go
+23
-8
cmd/hytop/tui/tui_test.go
cmd/hytop/tui/tui_test.go
+10
-2
utils/utils_test.go
utils/utils_test.go
+5
-0
No files found.
cmd/cmd-wrapper/docker-wrapper/parse-arg.go
deleted
100644 → 0
View file @
b7bee696
package
main
import
(
"log"
"os"
"os/exec"
)
/**
这个工具是对docker命令的一个包装,用于记录docker创建、删除容器的动作,并记录执行的用户
*/
// runWithoutAction 没有任何额外动作,仅执行命令
func
runWithoutAction
(
args
[]
string
)
error
{
var
cmd
*
exec
.
Cmd
if
args
==
nil
||
len
(
args
)
==
0
{
cmd
=
exec
.
Command
(
"docker"
)
}
cmd
=
exec
.
Command
(
"docker"
,
args
...
)
cmd
.
Stdin
=
os
.
Stdin
cmd
.
Stdout
=
os
.
Stdout
cmd
.
Stderr
=
os
.
Stderr
return
cmd
.
Run
()
}
func
main
()
{
args
:=
os
.
Args
[
1
:
]
if
len
(
args
)
==
0
{
err
:=
runWithoutAction
(
args
)
if
err
!=
nil
{
log
.
Fatal
(
err
)
}
os
.
Exit
(
0
)
}
switch
args
[
0
]
{
case
"run"
:
case
"create"
:
case
"rm"
:
default
:
err
:=
runWithoutAction
(
args
)
if
err
!=
nil
{
log
.
Fatal
(
err
)
}
os
.
Exit
(
0
)
}
}
/*
用户
时间
当前文件夹
*/
cmd/hytop/tui/main.go
View file @
72702c5d
...
@@ -16,6 +16,14 @@ const (
...
@@ -16,6 +16,14 @@ const (
DCUTopVersion
=
"1.0.1"
DCUTopVersion
=
"1.0.1"
)
)
var
(
KeyUp
=
tea
.
KeyUp
.
String
()
KeyDown
=
tea
.
KeyDown
.
String
()
KeyLeft
=
tea
.
KeyLeft
.
String
()
KeyRight
=
tea
.
KeyRight
.
String
()
KeySpace
=
tea
.
KeySpace
.
String
()
)
var
(
var
(
HeightLightStyle
=
lipgloss
.
NewStyle
()
.
Background
(
lipgloss
.
Color
(
"#00fffb7a"
))
// 高亮风格
HeightLightStyle
=
lipgloss
.
NewStyle
()
.
Background
(
lipgloss
.
Color
(
"#00fffb7a"
))
// 高亮风格
SelectedStyle
=
lipgloss
.
NewStyle
()
.
Foreground
(
lipgloss
.
Color
(
"#ffb81fe3"
))
// 被选中的风格
SelectedStyle
=
lipgloss
.
NewStyle
()
.
Foreground
(
lipgloss
.
Color
(
"#ffb81fe3"
))
// 被选中的风格
...
@@ -65,6 +73,7 @@ const (
...
@@ -65,6 +73,7 @@ const (
VMTree
ViewMode
=
1
// 进程树视图
VMTree
ViewMode
=
1
// 进程树视图
VMOneProcess
ViewMode
=
2
// 进程详情视图
VMOneProcess
ViewMode
=
2
// 进程详情视图
VMProcessEnv
ViewMode
=
3
// 进程环境变量视图
VMProcessEnv
ViewMode
=
3
// 进程环境变量视图
VMPower
ViewMode
=
4
// 功耗视图
)
)
func
(
pa
ProcessAction
)
String
()
string
{
func
(
pa
ProcessAction
)
String
()
string
{
...
@@ -120,6 +129,7 @@ type ModelMain struct {
...
@@ -120,6 +129,7 @@ type ModelMain struct {
pstree
*
ModelPsTree
// 进程树视图
pstree
*
ModelPsTree
// 进程树视图
processDetail
*
ModelProcessDetail
// 进程详情视图
processDetail
*
ModelProcessDetail
// 进程详情视图
processEnv
*
ModelProcessEnv
// 进程环境变量视图
processEnv
*
ModelProcessEnv
// 进程环境变量视图
powerDetail
*
ModelPowerDetail
// 功耗视图
}
}
func
NewModelMain
(
width
,
height
int
)
ModelMain
{
func
NewModelMain
(
width
,
height
int
)
ModelMain
{
...
@@ -138,6 +148,7 @@ func NewModelMain(width, height int) ModelMain {
...
@@ -138,6 +148,7 @@ func NewModelMain(width, height int) ModelMain {
VM
:
VMMain
,
VM
:
VMMain
,
}
}
result
.
processEnv
=
nil
result
.
processEnv
=
nil
result
.
powerDetail
=
nil
return
result
return
result
}
}
...
@@ -197,6 +208,9 @@ func (m *ModelMain) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
...
@@ -197,6 +208,9 @@ func (m *ModelMain) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
switch
msg
:=
inputMsg
.
(
type
)
{
switch
msg
:=
inputMsg
.
(
type
)
{
case
tea
.
KeyMsg
:
// 键盘事件
case
tea
.
KeyMsg
:
// 键盘事件
switch
msg
.
String
()
{
switch
msg
.
String
()
{
case
"a"
:
cmd
:=
m
.
handleKeyA
(
msg
)
return
m
,
cmd
case
"q"
:
case
"q"
:
return
m
,
tea
.
Quit
return
m
,
tea
.
Quit
case
"ctrl+c"
:
case
"ctrl+c"
:
...
@@ -214,7 +228,7 @@ func (m *ModelMain) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
...
@@ -214,7 +228,7 @@ func (m *ModelMain) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
case
"h"
:
case
"h"
:
m
.
actionMsg
.
HelpView
=
true
m
.
actionMsg
.
HelpView
=
true
return
m
,
nil
return
m
,
nil
case
"l
eft
"
,
"r
ight
"
:
case
KeyL
eft
,
KeyR
ight
:
cmd
:=
m
.
handleKeyLR
(
msg
.
String
())
cmd
:=
m
.
handleKeyLR
(
msg
.
String
())
return
m
,
cmd
return
m
,
cmd
case
"k"
:
case
"k"
:
...
@@ -232,6 +246,9 @@ func (m *ModelMain) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
...
@@ -232,6 +246,9 @@ func (m *ModelMain) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
case
"e"
:
case
"e"
:
cmd
:=
m
.
handleKeyE
()
cmd
:=
m
.
handleKeyE
()
return
m
,
cmd
return
m
,
cmd
case
"p"
:
cmd
:=
m
.
handleKeyP
(
msg
)
return
m
,
cmd
}
}
case
TickMsg
:
// 定时事件
case
TickMsg
:
// 定时事件
updateModelInfo
(
m
.
modelMsg
,
time
.
Time
(
msg
))
updateModelInfo
(
m
.
modelMsg
,
time
.
Time
(
msg
))
...
@@ -249,6 +266,10 @@ func (m *ModelMain) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
...
@@ -249,6 +266,10 @@ func (m *ModelMain) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
detail
,
_
:=
m
.
processDetail
.
Update
(
m
.
modelMsg
)
detail
,
_
:=
m
.
processDetail
.
Update
(
m
.
modelMsg
)
m
.
processDetail
=
detail
.
(
*
ModelProcessDetail
)
m
.
processDetail
=
detail
.
(
*
ModelProcessDetail
)
}
}
if
m
.
powerDetail
!=
nil
{
power
,
_
:=
m
.
powerDetail
.
Update
(
m
.
modelMsg
)
m
.
powerDetail
=
power
.
(
*
ModelPowerDetail
)
}
return
m
,
tickCmd
()
return
m
,
tickCmd
()
case
ModelMsg
:
// 初始化
case
ModelMsg
:
// 初始化
header
,
_
:=
m
.
Header
.
Update
(
m
.
modelMsg
)
header
,
_
:=
m
.
Header
.
Update
(
m
.
modelMsg
)
...
@@ -275,6 +296,36 @@ func (m *ModelMain) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
...
@@ -275,6 +296,36 @@ func (m *ModelMain) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
return
m
,
nil
return
m
,
nil
}
}
func
(
m
*
ModelMain
)
handleKeyA
(
k
tea
.
KeyMsg
)
tea
.
Cmd
{
switch
m
.
actionMsg
.
VM
{
case
VMPower
:
power
,
cmd
:=
m
.
powerDetail
.
Update
(
k
)
m
.
powerDetail
=
power
.
(
*
ModelPowerDetail
)
return
cmd
default
:
return
nil
}
}
func
(
m
*
ModelMain
)
handleKeyP
(
k
tea
.
KeyMsg
)
tea
.
Cmd
{
switch
m
.
actionMsg
.
VM
{
case
VMMain
:
m
.
actionMsg
.
VMOld
=
VMMain
m
.
actionMsg
.
VM
=
VMPower
m
.
powerDetail
=
NewModelPower
(
m
.
height
,
m
.
width
)
m
.
powerDetail
.
Init
()
model
,
cmd
:=
m
.
powerDetail
.
Update
(
m
.
modelMsg
)
m
.
powerDetail
=
model
.
(
*
ModelPowerDetail
)
return
cmd
case
VMPower
:
power
,
cmd
:=
m
.
powerDetail
.
Update
(
k
)
m
.
powerDetail
=
power
.
(
*
ModelPowerDetail
)
return
cmd
default
:
return
nil
}
}
func
(
m
*
ModelMain
)
handleKeyE
()
tea
.
Cmd
{
func
(
m
*
ModelMain
)
handleKeyE
()
tea
.
Cmd
{
// 检查光标是否在某个进程上,若有,且当前为Main或Tree视图,就进入进程环境变量模式
// 检查光标是否在某个进程上,若有,且当前为Main或Tree视图,就进入进程环境变量模式
if
m
.
actionMsg
==
nil
||
m
.
actionMsg
.
PointPid
==
nil
{
if
m
.
actionMsg
==
nil
||
m
.
actionMsg
.
PointPid
==
nil
{
...
@@ -381,11 +432,11 @@ func (m *ModelMain) handleKeyT() tea.Cmd {
...
@@ -381,11 +432,11 @@ func (m *ModelMain) handleKeyT() tea.Cmd {
func
(
m
*
ModelMain
)
handleKeyLR
(
s
string
)
tea
.
Cmd
{
func
(
m
*
ModelMain
)
handleKeyLR
(
s
string
)
tea
.
Cmd
{
if
m
.
processEnv
!=
nil
{
if
m
.
processEnv
!=
nil
{
switch
s
{
switch
s
{
case
tea
.
KeyRight
.
String
()
:
case
KeyRight
:
env
,
_
:=
m
.
processEnv
.
Update
(
tea
.
KeyRight
)
env
,
_
:=
m
.
processEnv
.
Update
(
tea
.
KeyRight
)
m
.
processEnv
=
env
.
(
*
ModelProcessEnv
)
m
.
processEnv
=
env
.
(
*
ModelProcessEnv
)
return
nil
return
nil
case
tea
.
KeyLeft
.
String
()
:
case
KeyLeft
:
env
,
_
:=
m
.
processEnv
.
Update
(
tea
.
KeyLeft
)
env
,
_
:=
m
.
processEnv
.
Update
(
tea
.
KeyLeft
)
m
.
processEnv
=
env
.
(
*
ModelProcessEnv
)
m
.
processEnv
=
env
.
(
*
ModelProcessEnv
)
return
nil
return
nil
...
@@ -398,7 +449,7 @@ func (m *ModelMain) handleKeyLR(s string) tea.Cmd {
...
@@ -398,7 +449,7 @@ func (m *ModelMain) handleKeyLR(s string) tea.Cmd {
}
}
if
m
.
dialog
!=
nil
{
if
m
.
dialog
!=
nil
{
switch
s
{
switch
s
{
case
tea
.
KeyRight
.
String
()
:
case
KeyRight
:
switch
*
m
.
actionMsg
.
Action
{
switch
*
m
.
actionMsg
.
Action
{
case
PAKill
:
case
PAKill
:
*
m
.
actionMsg
.
Action
=
PATerm
*
m
.
actionMsg
.
Action
=
PATerm
...
@@ -409,7 +460,7 @@ func (m *ModelMain) handleKeyLR(s string) tea.Cmd {
...
@@ -409,7 +460,7 @@ func (m *ModelMain) handleKeyLR(s string) tea.Cmd {
default
:
default
:
return
nil
return
nil
}
}
case
tea
.
KeyLeft
.
String
()
:
case
KeyLeft
:
switch
*
m
.
actionMsg
.
Action
{
switch
*
m
.
actionMsg
.
Action
{
case
PATerm
:
case
PATerm
:
*
m
.
actionMsg
.
Action
=
PAKill
*
m
.
actionMsg
.
Action
=
PAKill
...
@@ -517,6 +568,10 @@ func (m *ModelMain) handleKeyEsc() tea.Cmd {
...
@@ -517,6 +568,10 @@ func (m *ModelMain) handleKeyEsc() tea.Cmd {
m
.
actionMsg
.
VM
=
m
.
actionMsg
.
VMOld
m
.
actionMsg
.
VM
=
m
.
actionMsg
.
VMOld
m
.
processEnv
=
nil
m
.
processEnv
=
nil
return
nil
return
nil
case
VMPower
:
m
.
actionMsg
.
VM
=
m
.
actionMsg
.
VMOld
m
.
powerDetail
=
nil
return
nil
default
:
default
:
return
nil
return
nil
}
}
...
@@ -524,33 +579,43 @@ func (m *ModelMain) handleKeyEsc() tea.Cmd {
...
@@ -524,33 +579,43 @@ func (m *ModelMain) handleKeyEsc() tea.Cmd {
// handleKeySpace 处理空格键动作,空格键仅用于选择或取消进程
// handleKeySpace 处理空格键动作,空格键仅用于选择或取消进程
func
(
m
*
ModelMain
)
handleKeySpace
()
tea
.
Cmd
{
func
(
m
*
ModelMain
)
handleKeySpace
()
tea
.
Cmd
{
if
len
(
m
.
modelMsg
.
DCUPidInfo
)
==
0
{
return
nil
}
if
m
.
actionMsg
.
PointPid
==
nil
||
m
.
actionMsg
.
Action
!=
nil
{
return
nil
}
if
m
.
actionMsg
.
VM
!=
VMMain
&&
m
.
actionMsg
.
VM
!=
VMTree
{
return
nil
}
if
m
.
actionMsg
.
SelectPids
==
nil
{
m
.
actionMsg
.
SelectPids
=
make
(
map
[
int32
]
bool
)
}
_
,
have
:=
m
.
actionMsg
.
SelectPids
[
*
m
.
actionMsg
.
PointPid
]
if
have
{
delete
(
m
.
actionMsg
.
SelectPids
,
*
m
.
actionMsg
.
PointPid
)
}
else
{
m
.
actionMsg
.
SelectPids
[
*
m
.
actionMsg
.
PointPid
]
=
true
}
switch
m
.
actionMsg
.
VM
{
switch
m
.
actionMsg
.
VM
{
case
VMMain
:
case
VMMain
,
VMTree
:
m1
,
cmd1
:=
m
.
ProcessInfo
.
Update
(
m
.
actionMsg
)
if
len
(
m
.
modelMsg
.
DCUPidInfo
)
==
0
{
m
.
ProcessInfo
=
m1
.
(
*
ModelProcessInfo
)
return
nil
return
cmd1
}
case
VMTree
:
if
m
.
actionMsg
.
PointPid
==
nil
||
m
.
actionMsg
.
Action
!=
nil
{
m1
,
cmd1
:=
m
.
pstree
.
Update
(
m
.
actionMsg
)
return
nil
m
.
pstree
=
m1
.
(
*
ModelPsTree
)
}
return
cmd1
if
m
.
actionMsg
.
VM
!=
VMMain
&&
m
.
actionMsg
.
VM
!=
VMTree
{
return
nil
}
if
m
.
actionMsg
.
SelectPids
==
nil
{
m
.
actionMsg
.
SelectPids
=
make
(
map
[
int32
]
bool
)
}
_
,
have
:=
m
.
actionMsg
.
SelectPids
[
*
m
.
actionMsg
.
PointPid
]
if
have
{
delete
(
m
.
actionMsg
.
SelectPids
,
*
m
.
actionMsg
.
PointPid
)
}
else
{
m
.
actionMsg
.
SelectPids
[
*
m
.
actionMsg
.
PointPid
]
=
true
}
switch
m
.
actionMsg
.
VM
{
case
VMMain
:
m1
,
cmd1
:=
m
.
ProcessInfo
.
Update
(
m
.
actionMsg
)
m
.
ProcessInfo
=
m1
.
(
*
ModelProcessInfo
)
return
cmd1
case
VMTree
:
m1
,
cmd1
:=
m
.
pstree
.
Update
(
m
.
actionMsg
)
m
.
pstree
=
m1
.
(
*
ModelPsTree
)
return
cmd1
default
:
return
nil
}
case
VMPower
:
power
,
_
:=
m
.
powerDetail
.
Update
(
tea
.
KeySpace
)
m
.
powerDetail
=
power
.
(
*
ModelPowerDetail
)
return
nil
default
:
default
:
return
nil
return
nil
}
}
...
@@ -579,6 +644,8 @@ func (m *ModelMain) View() string {
...
@@ -579,6 +644,8 @@ func (m *ModelMain) View() string {
return
m
.
processDetail
.
View
()
return
m
.
processDetail
.
View
()
case
VMProcessEnv
:
case
VMProcessEnv
:
return
m
.
processEnv
.
View
()
return
m
.
processEnv
.
View
()
case
VMPower
:
return
m
.
powerDetail
.
View
()
default
:
default
:
return
""
return
""
}
}
...
@@ -624,14 +691,14 @@ func updateModelInfo(modelMsg *ModelMsg, t time.Time) error {
...
@@ -624,14 +691,14 @@ func updateModelInfo(modelMsg *ModelMsg, t time.Time) error {
}
}
func
(
m
*
ModelMain
)
handleKeyUp
()
tea
.
Cmd
{
func
(
m
*
ModelMain
)
handleKeyUp
()
tea
.
Cmd
{
if
len
(
m
.
modelMsg
.
DCUPidInfo
)
==
0
{
return
nil
}
if
m
.
actionMsg
.
Action
!=
nil
{
return
nil
}
switch
m
.
actionMsg
.
VM
{
switch
m
.
actionMsg
.
VM
{
case
VMMain
:
case
VMMain
:
if
len
(
m
.
modelMsg
.
DCUPidInfo
)
==
0
{
return
nil
}
if
m
.
actionMsg
.
Action
!=
nil
{
return
nil
}
processes
:=
make
([]
backend
.
DCUProcessInfo
,
0
,
16
)
processes
:=
make
([]
backend
.
DCUProcessInfo
,
0
,
16
)
keys
:=
make
([]
int
,
0
,
16
)
keys
:=
make
([]
int
,
0
,
16
)
for
k
:=
range
m
.
modelMsg
.
DCUPidInfo
{
for
k
:=
range
m
.
modelMsg
.
DCUPidInfo
{
...
@@ -677,6 +744,12 @@ func (m *ModelMain) handleKeyUp() tea.Cmd {
...
@@ -677,6 +744,12 @@ func (m *ModelMain) handleKeyUp() tea.Cmd {
m
.
SysLoad
=
m3
.
(
*
ModelSysLoad
)
m
.
SysLoad
=
m3
.
(
*
ModelSysLoad
)
return
tea
.
Batch
(
cmd1
,
cmd2
,
cmd3
)
return
tea
.
Batch
(
cmd1
,
cmd2
,
cmd3
)
case
VMTree
:
case
VMTree
:
if
len
(
m
.
modelMsg
.
DCUPidInfo
)
==
0
{
return
nil
}
if
m
.
actionMsg
.
Action
!=
nil
{
return
nil
}
if
len
(
m
.
actionMsg
.
PidTreePids
)
==
0
{
if
len
(
m
.
actionMsg
.
PidTreePids
)
==
0
{
return
nil
return
nil
}
}
...
@@ -701,6 +774,10 @@ func (m *ModelMain) handleKeyUp() tea.Cmd {
...
@@ -701,6 +774,10 @@ func (m *ModelMain) handleKeyUp() tea.Cmd {
env
,
_
:=
m
.
processEnv
.
Update
(
tea
.
KeyUp
)
env
,
_
:=
m
.
processEnv
.
Update
(
tea
.
KeyUp
)
m
.
processEnv
=
env
.
(
*
ModelProcessEnv
)
m
.
processEnv
=
env
.
(
*
ModelProcessEnv
)
return
nil
return
nil
case
VMPower
:
power
,
_
:=
m
.
powerDetail
.
Update
(
tea
.
KeyUp
)
m
.
powerDetail
=
power
.
(
*
ModelPowerDetail
)
return
nil
default
:
default
:
return
nil
return
nil
}
}
...
@@ -708,14 +785,14 @@ func (m *ModelMain) handleKeyUp() tea.Cmd {
...
@@ -708,14 +785,14 @@ func (m *ModelMain) handleKeyUp() tea.Cmd {
}
}
func
(
m
*
ModelMain
)
handleKeyDown
()
tea
.
Cmd
{
func
(
m
*
ModelMain
)
handleKeyDown
()
tea
.
Cmd
{
if
len
(
m
.
modelMsg
.
DCUPidInfo
)
==
0
{
return
nil
}
if
m
.
actionMsg
.
Action
!=
nil
{
return
nil
}
switch
m
.
actionMsg
.
VM
{
switch
m
.
actionMsg
.
VM
{
case
VMMain
:
case
VMMain
:
if
len
(
m
.
modelMsg
.
DCUPidInfo
)
==
0
{
return
nil
}
if
m
.
actionMsg
.
Action
!=
nil
{
return
nil
}
processes
:=
make
([]
backend
.
DCUProcessInfo
,
0
,
16
)
processes
:=
make
([]
backend
.
DCUProcessInfo
,
0
,
16
)
keys
:=
make
([]
int
,
0
,
16
)
keys
:=
make
([]
int
,
0
,
16
)
for
key
:=
range
m
.
modelMsg
.
DCUPidInfo
{
for
key
:=
range
m
.
modelMsg
.
DCUPidInfo
{
...
@@ -764,6 +841,12 @@ func (m *ModelMain) handleKeyDown() tea.Cmd {
...
@@ -764,6 +841,12 @@ func (m *ModelMain) handleKeyDown() tea.Cmd {
m
.
SysLoad
=
m3
.
(
*
ModelSysLoad
)
m
.
SysLoad
=
m3
.
(
*
ModelSysLoad
)
return
tea
.
Batch
(
cmd1
,
cmd2
,
cmd3
)
return
tea
.
Batch
(
cmd1
,
cmd2
,
cmd3
)
case
VMTree
:
case
VMTree
:
if
len
(
m
.
modelMsg
.
DCUPidInfo
)
==
0
{
return
nil
}
if
m
.
actionMsg
.
Action
!=
nil
{
return
nil
}
if
len
(
m
.
actionMsg
.
PidTreePids
)
==
0
{
if
len
(
m
.
actionMsg
.
PidTreePids
)
==
0
{
return
nil
return
nil
}
}
...
@@ -788,6 +871,10 @@ func (m *ModelMain) handleKeyDown() tea.Cmd {
...
@@ -788,6 +871,10 @@ func (m *ModelMain) handleKeyDown() tea.Cmd {
env
,
_
:=
m
.
processEnv
.
Update
(
tea
.
KeyDown
)
env
,
_
:=
m
.
processEnv
.
Update
(
tea
.
KeyDown
)
m
.
processEnv
=
env
.
(
*
ModelProcessEnv
)
m
.
processEnv
=
env
.
(
*
ModelProcessEnv
)
return
nil
return
nil
case
VMPower
:
power
,
_
:=
m
.
powerDetail
.
Update
(
tea
.
KeyDown
)
m
.
powerDetail
=
power
.
(
*
ModelPowerDetail
)
return
nil
default
:
default
:
return
nil
return
nil
}
}
...
...
cmd/hytop/tui/power.go
0 → 100644
View file @
72702c5d
package
tui
import
(
"errors"
"fmt"
"get-container/cmd/hytop/tchart"
"slices"
"strings"
"sync"
"time"
tea
"github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
linkedlist
"github.com/emirpasic/gods/v2/lists/doublylinkedlist"
)
/*
所有dcu的平均功耗表常态维护
一旦选中了dcu,选中dcu的平均功耗表常态维护
ModelPowerDetail.Charts中key为非负数的值,如果存在就更新数据
*/
// ModelPowerDetail 展示DCU功率消耗的Model
type
ModelPowerDetail
struct
{
PowerCap
map
[
int
]
float32
// dcu功耗墙,即最大功耗,单位为瓦
DataCache
map
[
int
]
*
linkedlist
.
List
[
tchart
.
TimePoint
]
// 功率数据,key为dcu index,value为dcu功率
DCUDataCache
map
[
int
]
*
[
4
]
float32
// 记录dcu与功耗相关的其他信息,key为dcu index,value为dcu的温度、使用率、内存使用率和功率
height
,
width
int
// 屏幕高度、宽度
Charts
map
[
int
]
*
ChartAndArea
// 图表,key为dcu index,key为-1表示平均值图表,-2表示选中的dcu的平均值图表
Pids
map
[
int
]
int
// 记录哪些dcu在被使用
selectedDCU
map
[
int
]
bool
// 记录哪些dcu被选中
cursor
*
int
// 光标指向的dcu index
dcuIndex
[]
int
// dcu index
DisplaySummary
bool
// 显示概要信息
DisplayAvg
bool
// 显示功率平均值
lock
sync
.
RWMutex
// 保护DataCache和Charts的锁
ScreenSplit
map
[
int
][][]
ScreenArea
// 记录屏幕尺寸
}
type
ChartAndArea
struct
{
chart
*
MyTimeChart
area
ScreenArea
h
,
l
int
}
func
NewModelPower
(
height
,
width
int
)
*
ModelPowerDetail
{
result
:=
ModelPowerDetail
{
PowerCap
:
make
(
map
[
int
]
float32
),
DataCache
:
make
(
map
[
int
]
*
linkedlist
.
List
[
tchart
.
TimePoint
]),
DCUDataCache
:
make
(
map
[
int
]
*
[
4
]
float32
),
height
:
height
,
width
:
width
,
Pids
:
make
(
map
[
int
]
int
),
Charts
:
make
(
map
[
int
]
*
ChartAndArea
),
selectedDCU
:
make
(
map
[
int
]
bool
),
dcuIndex
:
make
([]
int
,
0
,
16
),
cursor
:
nil
,
ScreenSplit
:
make
(
map
[
int
][][]
ScreenArea
),
DisplaySummary
:
false
,
DisplayAvg
:
false
,
lock
:
sync
.
RWMutex
{},
}
return
&
result
}
const
(
ModelPowerTitle
=
"DCU Pids Temp(°C) HCU(%) Mem(%) PwrCap(W) AvgPwr(W)
\n
"
)
var
(
MPStyle
=
lipgloss
.
NewStyle
()
.
Border
(
lipgloss
.
NormalBorder
(),
false
,
false
,
false
,
true
)
)
func
(
m
*
ModelPowerDetail
)
Init
()
tea
.
Cmd
{
for
i
:=
1
;
i
<
10
;
i
++
{
m
.
ScreenSplit
[
i
]
=
m
.
SplitScreen
(
i
)
}
return
nil
}
func
(
m
*
ModelPowerDetail
)
Update
(
imsg
tea
.
Msg
)
(
tea
.
Model
,
tea
.
Cmd
)
{
switch
msg
:=
imsg
.
(
type
)
{
case
*
ModelMsg
:
// 更新数据
var
powerTotal
float32
=
0
threshold
:=
msg
.
t
.
Add
(
time
.
Second
*
(
time
.
Duration
(
m
.
width
*
-
2
)))
m
.
dcuIndex
=
m
.
dcuIndex
[
:
0
]
var
pwrCap
float32
=
0
info
,
lock
:=
msg
.
DCUInfo
.
GetQuitInfo
()
extAvg
:=
m
.
DisplayAvg
&&
len
(
m
.
selectedDCU
)
>
0
var
selectedTotal
float32
=
0
m
.
lock
.
Lock
()
for
k
,
v
:=
range
info
{
tmp
:=
v
.
PwrCap
.
OrElse
(
0
)
m
.
PowerCap
[
k
]
=
tmp
if
tmp
>
0
{
pwrCap
=
tmp
}
l
,
have
:=
m
.
DataCache
[
k
]
if
!
have
{
l
=
linkedlist
.
New
[
tchart
.
TimePoint
]()
}
tmp
=
v
.
PwrAvg
.
OrElse
(
0
)
tp
:=
tchart
.
TimePoint
{
Time
:
msg
.
t
,
Value
:
float64
(
tmp
)}
l
.
Append
(
tp
)
chart
,
have
:=
m
.
Charts
[
k
]
if
have
{
chart
.
chart
.
PutPoint
([]
tchart
.
TimePoint
{
tp
})
}
powerTotal
+=
tmp
if
extAvg
&&
m
.
selectedDCU
[
k
]
{
selectedTotal
+=
tmp
}
if
!
have
{
m
.
DataCache
[
k
]
=
l
}
// 剔除多余的点
for
{
point
,
have
:=
l
.
Get
(
0
)
if
!
have
{
break
}
if
point
.
Time
.
Before
(
threshold
)
{
l
.
Remove
(
0
)
}
else
{
break
}
}
d
,
have
:=
m
.
DCUDataCache
[
k
]
if
!
have
{
d
:=
&
[
4
]
float32
{
v
.
Temp
.
OrElse
(
0
),
v
.
DCUUTil
.
OrElse
(
0
),
v
.
MemUsedPerent
.
OrElse
(
0
),
v
.
PwrAvg
.
OrElse
(
0
)}
m
.
DCUDataCache
[
k
]
=
d
}
else
{
d
[
0
]
=
v
.
Temp
.
OrElse
(
0
)
d
[
1
]
=
v
.
DCUUTil
.
OrElse
(
0
)
d
[
2
]
=
v
.
MemUsedPerent
.
OrElse
(
0
)
d
[
3
]
=
v
.
PwrAvg
.
OrElse
(
0
)
}
m
.
dcuIndex
=
append
(
m
.
dcuIndex
,
k
)
}
lock
.
Unlock
()
slices
.
Sort
(
m
.
dcuIndex
)
powerTotal
/=
float32
(
len
(
m
.
dcuIndex
))
selectedTotal
/=
float32
(
len
(
m
.
selectedDCU
))
l
,
have
:=
m
.
DataCache
[
-
1
]
if
!
have
{
l
=
linkedlist
.
New
[
tchart
.
TimePoint
]()
m
.
DataCache
[
-
1
]
=
l
}
l
.
Append
(
tchart
.
TimePoint
{
Time
:
msg
.
t
,
Value
:
float64
(
powerTotal
)})
for
{
point
,
have
:=
l
.
Get
(
0
)
if
!
have
{
break
}
if
point
.
Time
.
Before
(
threshold
)
{
l
.
Remove
(
0
)
}
else
{
break
}
}
l
,
have
=
m
.
DataCache
[
-
2
]
if
!
have
{
l
=
linkedlist
.
New
[
tchart
.
TimePoint
]()
m
.
DataCache
[
-
2
]
=
l
}
for
{
point
,
have
:=
l
.
Get
(
0
)
if
!
have
{
break
}
if
point
.
Time
.
Before
(
threshold
)
{
l
.
Remove
(
0
)
}
else
{
break
}
}
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"
)})
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
}
chart
.
chart
.
PutPoint
([]
tchart
.
TimePoint
{{
Time
:
msg
.
t
,
Value
:
float64
(
selectedTotal
)}})
m
.
lock
.
Unlock
()
m
.
PowerCap
[
-
1
]
=
pwrCap
for
k
:=
range
m
.
Pids
{
m
.
Pids
[
k
]
=
0
}
for
k
,
v
:=
range
msg
.
DCUPidInfo
{
m
.
Pids
[
k
]
=
len
(
v
)
}
case
tea
.
KeyType
:
m
.
HandleKey
(
msg
.
String
())
case
tea
.
KeyMsg
:
m
.
HandleKey
(
msg
.
String
())
}
return
m
,
nil
}
// HandleKey 处理按键事件
func
(
m
*
ModelPowerDetail
)
HandleKey
(
key
string
)
{
switch
key
{
case
"p"
:
// 显示概要信息
m
.
DisplaySummary
=
!
m
.
DisplaySummary
case
"a"
:
// 显示平均功率
m
.
DisplayAvg
=
!
m
.
DisplayAvg
case
KeySpace
:
// 选中/取消dcu
if
!
m
.
DisplaySummary
{
break
}
if
m
.
cursor
==
nil
{
break
}
a
,
b
:=
m
.
selectedDCU
[
*
m
.
cursor
]
selected
:=
a
&&
b
if
selected
{
// 已经选中了,现在取消
delete
(
m
.
selectedDCU
,
*
m
.
cursor
)
m
.
UpdateCharts
(
*
m
.
cursor
,
false
)
}
else
{
// 判断
if
len
(
m
.
selectedDCU
)
>=
8
{
break
}
m
.
selectedDCU
[
*
m
.
cursor
]
=
true
m
.
UpdateCharts
(
*
m
.
cursor
,
true
)
}
case
KeyUp
:
if
!
m
.
DisplaySummary
{
break
}
if
m
.
cursor
==
nil
{
index
:=
m
.
dcuIndex
[
len
(
m
.
dcuIndex
)
-
1
]
m
.
cursor
=
&
index
}
else
{
index
:=
slices
.
Index
(
m
.
dcuIndex
,
*
m
.
cursor
)
if
index
==
-
1
{
*
m
.
cursor
=
m
.
dcuIndex
[
len
(
m
.
dcuIndex
)
-
1
]
}
else
{
if
index
>
0
{
*
m
.
cursor
=
m
.
dcuIndex
[
index
-
1
]
}
}
}
case
KeyDown
:
if
!
m
.
DisplaySummary
{
break
}
if
m
.
cursor
==
nil
{
index
:=
m
.
dcuIndex
[
0
]
m
.
cursor
=
&
index
}
else
{
index
:=
slices
.
Index
(
m
.
dcuIndex
,
*
m
.
cursor
)
if
index
==
-
1
{
*
m
.
cursor
=
m
.
dcuIndex
[
0
]
}
else
{
if
index
<
(
len
(
m
.
dcuIndex
)
-
1
)
{
*
m
.
cursor
=
m
.
dcuIndex
[
index
+
1
]
}
}
}
}
}
// SummaryInfo 生成概要信息
func
(
m
*
ModelPowerDetail
)
SummaryInfo
()
string
{
sb
:=
strings
.
Builder
{}
sb
.
WriteString
(
ModelPowerTitle
)
tmp
:=
len
(
m
.
dcuIndex
)
-
1
for
k
,
v
:=
range
m
.
dcuIndex
{
d
:=
m
.
DCUDataCache
[
v
]
a
,
b
:=
m
.
selectedDCU
[
v
]
selected
:=
a
&&
b
cursorOn
:=
(
m
.
cursor
!=
nil
&&
*
m
.
cursor
==
v
)
if
!
selected
&&
!
cursorOn
{
fmt
.
Fprintf
(
&
sb
,
"%3d %4d %8.2f %6.2f %6.2f %9.2f %9.2f"
,
v
,
m
.
Pids
[
v
],
d
[
0
],
d
[
1
],
d
[
2
],
m
.
PowerCap
[
v
],
d
[
3
])
}
else
{
var
style
lipgloss
.
Style
if
selected
&&
cursorOn
{
style
=
HeightSelectedStyle
}
else
if
selected
{
style
=
SelectedStyle
}
else
{
style
=
HeightLightStyle
}
s
:=
fmt
.
Sprintf
(
"%3d %4d %8.2f %6.2f %6.2f %9.2f %9.2f"
,
v
,
m
.
Pids
[
v
],
d
[
0
],
d
[
1
],
d
[
2
],
m
.
PowerCap
[
v
],
d
[
3
])
sb
.
WriteString
(
style
.
Render
(
s
))
}
if
k
<
tmp
{
sb
.
WriteByte
(
'\n'
)
}
}
style
:=
lipgloss
.
NewStyle
()
.
Border
(
lipgloss
.
NormalBorder
())
return
style
.
Render
(
sb
.
String
())
}
// UpdateCharts 解析数据,生成图表结构信息
func
(
m
*
ModelPowerDetail
)
UpdateCharts
(
index
int
,
isAdd
bool
)
{
dcuNum
:=
len
(
m
.
selectedDCU
)
dcus
:=
make
([]
int
,
0
,
dcuNum
)
for
k
,
v
:=
range
m
.
selectedDCU
{
if
v
{
dcus
=
append
(
dcus
,
k
)
}
}
dcuNum
=
len
(
dcus
)
slices
.
Sort
(
dcus
)
m
.
lock
.
Lock
()
if
isAdd
{
// 添加dcu
for
k
,
v
:=
range
dcus
{
area
,
h
,
l
,
err
:=
m
.
getScreenArea
(
dcuNum
,
k
)
if
err
!=
nil
{
panic
(
fmt
.
Sprintf
(
"%v"
,
err
))
}
chart
,
have
:=
m
.
Charts
[
v
]
if
!
have
{
// 需要新建图表
c
:=
createChart
(
area
,
float64
(
m
.
PowerCap
[
v
]),
0
)
chart
=
&
ChartAndArea
{
chart
:
c
,
area
:
area
,
h
:
h
,
l
:
l
,
}
m
.
Charts
[
v
]
=
chart
chart
.
chart
.
PutPoint
(
m
.
DataCache
[
v
]
.
Values
())
continue
}
// 判断图表尺寸是否一致
if
chart
.
area
.
Equal
(
&
area
)
{
// 一致,仅修改位置即可
chart
.
h
=
h
chart
.
l
=
l
}
else
{
// 尺寸不一致,需要重新生成图表
chart
.
chart
=
createChart
(
area
,
float64
(
m
.
PowerCap
[
v
]),
0
)
chart
.
h
=
h
chart
.
l
=
l
chart
.
area
=
area
// 新图表添加数据点
chart
.
chart
.
PutPoint
(
m
.
DataCache
[
v
]
.
Values
())
}
}
}
else
{
// 删除dcu
delete
(
m
.
Charts
,
index
)
for
k
,
v
:=
range
dcus
{
area
,
h
,
l
,
err
:=
m
.
getScreenArea
(
dcuNum
,
k
)
if
err
!=
nil
{
panic
(
fmt
.
Sprintf
(
"%v"
,
err
))
}
chart
:=
m
.
Charts
[
v
]
if
chart
.
area
.
Equal
(
&
area
)
{
// 尺寸一致,无需重新生成,仅修改位置即可
chart
.
h
=
h
chart
.
l
=
l
continue
}
// 尺寸不一致,需要重新生成图表
chart
.
chart
=
createChart
(
area
,
float64
(
m
.
PowerCap
[
v
]),
0
)
chart
.
h
=
h
chart
.
l
=
l
chart
.
area
=
area
// 新图表添加数据点
chart
.
chart
.
PutPoint
(
m
.
DataCache
[
v
]
.
Values
())
}
}
// 更新选中dcu的平均值图表
if
dcuNum
!=
0
{
l
:=
linkedlist
.
New
[
tchart
.
TimePoint
]()
var
points
[]
tchart
.
TimePoint
for
_
,
v
:=
range
dcus
{
p
:=
m
.
DataCache
[
v
]
.
Values
()
if
points
==
nil
{
points
=
p
continue
}
for
k
:=
range
p
{
v
:=
p
[
k
]
.
Value
+
points
[
k
]
.
Value
points
[
k
]
.
Value
=
v
}
}
for
k
:=
range
points
{
points
[
k
]
.
Value
/=
float64
(
dcuNum
)
}
l
.
Append
(
points
...
)
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"
)})
chart
=
&
ChartAndArea
{
chart
:
c
,
area
:
m
.
ScreenSplit
[
1
][
0
][
0
],
h
:
0
,
l
:
0
}
m
.
Charts
[
-
2
]
=
chart
}
chart
.
chart
.
ResetPutPoint
(
points
)
}
m
.
lock
.
Unlock
()
}
// RenderCharts 渲染图表
func
(
m
*
ModelPowerDetail
)
RenderCharts
()
string
{
dcuSelected
:=
make
([]
int
,
0
,
len
(
m
.
selectedDCU
))
for
k
,
v
:=
range
m
.
selectedDCU
{
if
v
{
dcuSelected
=
append
(
dcuSelected
,
k
)
}
}
slices
.
Sort
(
dcuSelected
)
selectNum
:=
len
(
dcuSelected
)
// 显示全部平均功率
if
selectNum
==
0
||
(
selectNum
==
len
(
m
.
dcuIndex
)
&&
m
.
DisplayAvg
)
{
x
:=
genXAxis
(
m
.
width
)
chart
:=
m
.
Charts
[
-
1
]
.
chart
.
View
()
return
chart
+
"
\n
"
+
x
}
else
if
m
.
DisplayAvg
{
// 显示选中dcu的平均功率图表
x
:=
genXAxis
(
m
.
width
)
chart
:=
m
.
Charts
[
-
2
]
.
chart
.
View
()
return
chart
+
"
\n
"
+
x
}
else
{
// 显示选中dcu的图表
areas
:=
m
.
ScreenSplit
[
selectNum
]
pa
,
err
:=
parseAreas
(
areas
)
if
err
!=
nil
{
panic
(
err
.
Error
())
}
strs
:=
make
([]
string
,
0
,
selectNum
)
for
_
,
v
:=
range
dcuSelected
{
chart
:=
m
.
Charts
[
v
]
.
chart
str
:=
chart
.
View
()
str
=
StackPosition
(
fmt
.
Sprintf
(
"DCU: %d"
,
v
),
str
,
lipgloss
.
Top
,
lipgloss
.
Left
)
str
+=
"
\n
"
str
+=
genXAxis
(
chart
.
width
)
strs
=
append
(
strs
,
MPStyle
.
Render
(
str
))
}
lines
:=
make
(
map
[
int
]
string
)
for
k
,
v
:=
range
strs
{
po
:=
pa
[
k
]
l
,
have
:=
lines
[
po
[
0
]]
if
!
have
{
l
=
v
lines
[
po
[
0
]]
=
l
}
else
{
lines
[
po
[
0
]]
=
lipgloss
.
JoinHorizontal
(
lipgloss
.
Top
,
l
,
v
)
}
}
return
lipgloss
.
JoinVertical
(
lipgloss
.
Left
,
lines
[
0
],
lines
[
1
],
lines
[
2
])
}
}
func
(
m
*
ModelPowerDetail
)
View
()
string
{
if
m
.
DisplaySummary
{
summary
:=
m
.
SummaryInfo
()
charts
:=
m
.
RenderCharts
()
return
StackPosition
(
summary
,
charts
,
lipgloss
.
Center
,
lipgloss
.
Center
)
}
else
{
return
m
.
RenderCharts
()
}
}
// getScreenArea 获取指定索引的屏幕区域尺寸、所在的行、列
func
(
m
*
ModelPowerDetail
)
getScreenArea
(
num
,
index
int
)
(
ScreenArea
,
int
,
int
,
error
)
{
if
num
>
9
||
num
<=
0
{
return
ScreenArea
{},
0
,
0
,
errors
.
New
(
"num out of range"
)
}
if
index
<
0
||
index
>=
num
{
return
ScreenArea
{},
0
,
0
,
errors
.
New
(
"index out of range"
)
}
if
m
.
ScreenSplit
==
nil
{
return
ScreenArea
{},
0
,
0
,
errors
.
New
(
"Model PowerDetail not init"
)
}
target
:=
m
.
ScreenSplit
[
num
]
switch
num
{
case
1
:
return
target
[
0
][
0
],
0
,
0
,
nil
case
2
:
switch
index
{
case
0
:
return
target
[
0
][
0
],
0
,
0
,
nil
case
1
:
return
target
[
1
][
0
],
1
,
0
,
nil
}
case
3
,
4
:
h
:=
index
/
2
l
:=
index
%
2
return
target
[
h
][
l
],
h
,
l
,
nil
case
5
,
6
,
8
,
9
:
h
:=
index
/
3
l
:=
index
%
3
return
target
[
h
][
l
],
h
,
l
,
nil
case
7
:
switch
index
{
case
0
,
1
,
2
:
return
target
[
0
][
index
],
0
,
index
,
nil
case
3
,
4
:
return
target
[
1
][
index
-
3
],
1
,
index
-
3
,
nil
case
5
,
6
:
return
target
[
2
][
index
-
5
],
2
,
index
-
5
,
nil
}
}
return
ScreenArea
{},
0
,
0
,
errors
.
New
(
"Model PowerDetail unknown error"
)
}
// SplitScreen 分割屏幕,得到每个区域的尺寸,返回结果为二维切片,第一维是行,第二维是列
func
(
m
*
ModelPowerDetail
)
SplitScreen
(
num
int
)
[][]
ScreenArea
{
if
num
<=
0
{
return
nil
}
result
:=
make
([][]
ScreenArea
,
0
,
3
)
result
=
append
(
result
,
make
([]
ScreenArea
,
0
,
3
))
switch
num
{
case
1
:
result
[
0
]
=
append
(
result
[
0
],
NewScreenArea
(
m
.
width
,
m
.
height
))
case
2
:
result
=
append
(
result
,
make
([]
ScreenArea
,
0
,
1
))
result
[
0
]
=
append
(
result
[
0
],
NewScreenArea
(
m
.
width
,
m
.
height
/
2
))
result
[
1
]
=
append
(
result
[
1
],
NewScreenArea
(
m
.
width
,
m
.
height
/
2
))
if
m
.
height
%
2
!=
0
{
result
[
0
][
0
]
.
h
++
}
case
3
:
result
=
append
(
result
,
make
([]
ScreenArea
,
0
,
1
))
result
[
0
]
=
append
(
result
[
0
],
NewScreenArea
(
m
.
width
/
2
,
m
.
height
/
2
),
NewScreenArea
(
m
.
width
/
2
,
m
.
height
/
2
))
result
[
1
]
=
append
(
result
[
1
],
NewScreenArea
(
m
.
width
,
m
.
height
/
2
))
if
m
.
width
%
2
!=
0
{
result
[
0
][
0
]
.
w
++
}
if
m
.
height
%
2
!=
0
{
result
[
0
][
0
]
.
h
++
result
[
0
][
1
]
.
h
++
}
case
4
:
result
=
append
(
result
,
make
([]
ScreenArea
,
0
,
1
))
result
[
0
]
=
append
(
result
[
0
],
NewScreenArea
(
m
.
width
/
2
,
m
.
height
/
2
),
NewScreenArea
(
m
.
width
/
2
,
m
.
height
/
2
))
result
[
1
]
=
append
(
result
[
1
],
NewScreenArea
(
m
.
width
/
2
,
m
.
height
/
2
),
NewScreenArea
(
m
.
width
/
2
,
m
.
height
/
2
))
if
m
.
width
%
2
!=
0
{
result
[
0
][
0
]
.
w
++
result
[
1
][
0
]
.
w
++
}
if
m
.
height
%
2
!=
0
{
result
[
0
][
0
]
.
h
++
result
[
0
][
1
]
.
h
++
}
case
5
:
// 两行,3,2
result
=
append
(
result
,
make
([]
ScreenArea
,
0
,
2
))
result
[
0
]
=
append
(
result
[
0
],
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
2
),
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
2
),
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
2
))
result
[
1
]
=
append
(
result
[
1
],
NewScreenArea
(
m
.
width
/
2
,
m
.
height
/
2
),
NewScreenArea
(
m
.
width
/
2
,
m
.
height
/
2
))
if
m
.
width
%
2
!=
0
{
result
[
1
][
0
]
.
w
++
}
for
l
:=
0
;
l
<
m
.
width
%
3
;
l
++
{
result
[
0
][
l
]
.
w
++
}
if
m
.
height
%
2
!=
0
{
for
l
:=
range
result
[
0
]
{
result
[
0
][
l
]
.
h
++
}
}
case
6
:
// 两行,3,3
result
=
append
(
result
,
make
([]
ScreenArea
,
0
,
3
))
result
[
0
]
=
append
(
result
[
0
],
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
2
),
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
2
),
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
2
))
result
[
1
]
=
append
(
result
[
1
],
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
2
),
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
2
),
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
2
))
for
h
:=
range
result
{
for
l
:=
0
;
l
<
m
.
width
%
3
;
l
++
{
result
[
h
][
l
]
.
w
++
}
}
if
m
.
height
%
2
!=
0
{
for
l
:=
range
result
[
0
]
{
result
[
0
][
l
]
.
h
++
}
}
case
7
:
// 三行,3,2,2
result
=
append
(
result
,
make
([]
ScreenArea
,
0
,
2
),
make
([]
ScreenArea
,
0
,
2
))
result
[
0
]
=
append
(
result
[
0
],
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
3
),
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
3
),
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
3
))
result
[
1
]
=
append
(
result
[
1
],
NewScreenArea
(
m
.
width
/
2
,
m
.
height
/
3
),
NewScreenArea
(
m
.
width
/
2
,
m
.
height
/
3
))
result
[
2
]
=
append
(
result
[
2
],
NewScreenArea
(
m
.
width
/
2
,
m
.
height
/
3
),
NewScreenArea
(
m
.
width
/
2
,
m
.
height
/
3
))
for
h
:=
0
;
h
<
m
.
height
%
3
;
h
++
{
for
l
:=
range
result
[
h
]
{
result
[
h
][
l
]
.
h
++
}
}
for
l
:=
0
;
l
<
m
.
width
%
3
;
l
++
{
result
[
0
][
l
]
.
w
++
}
if
m
.
width
%
2
!=
0
{
result
[
1
][
0
]
.
w
++
result
[
1
][
1
]
.
w
++
result
[
2
][
0
]
.
w
++
result
[
2
][
1
]
.
w
++
}
case
8
:
// 三行,3,3,2
result
=
append
(
result
,
make
([]
ScreenArea
,
0
,
3
),
make
([]
ScreenArea
,
0
,
2
))
result
[
0
]
=
append
(
result
[
0
],
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
3
),
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
3
),
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
3
))
result
[
1
]
=
append
(
result
[
1
],
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
3
),
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
3
),
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
3
))
result
[
2
]
=
append
(
result
[
2
],
NewScreenArea
(
m
.
width
/
2
,
m
.
height
/
3
),
NewScreenArea
(
m
.
width
/
2
,
m
.
height
/
3
))
for
h
:=
0
;
h
<
m
.
height
%
3
;
h
++
{
for
l
:=
range
result
[
h
]
{
result
[
h
][
l
]
.
h
++
}
}
for
h
:=
range
result
[
:
1
]
{
for
l
:=
0
;
l
<
m
.
width
%
3
;
l
++
{
result
[
h
][
l
]
.
w
++
}
}
if
m
.
width
%
2
!=
0
{
result
[
2
][
0
]
.
w
++
}
case
9
:
result
=
append
(
result
,
make
([]
ScreenArea
,
0
,
3
),
make
([]
ScreenArea
,
0
,
3
))
result
[
0
]
=
append
(
result
[
0
],
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
3
),
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
3
),
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
3
))
result
[
1
]
=
append
(
result
[
1
],
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
3
),
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
3
),
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
3
))
result
[
2
]
=
append
(
result
[
2
],
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
3
),
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
3
),
NewScreenArea
(
m
.
width
/
3
,
m
.
height
/
3
))
for
h
:=
0
;
h
<
m
.
height
%
3
;
h
++
{
for
l
:=
range
result
[
h
]
{
result
[
h
][
l
]
.
h
++
}
}
for
h
:=
range
result
{
for
l
:=
0
;
l
<
m
.
width
%
3
;
l
++
{
result
[
h
][
l
]
.
w
++
}
}
default
:
return
nil
}
return
result
}
type
ScreenArea
struct
{
w
,
h
int
}
func
(
sa
*
ScreenArea
)
Equal
(
other
*
ScreenArea
)
bool
{
return
sa
.
h
==
other
.
h
&&
sa
.
w
==
other
.
w
}
func
NewScreenArea
(
w
,
h
int
)
ScreenArea
{
return
ScreenArea
{
w
:
w
,
h
:
h
,
}
}
func
createChart
(
area
ScreenArea
,
max
,
min
float64
)
*
MyTimeChart
{
return
NewTimeChart
(
area
.
w
-
1
,
area
.
h
-
1
,
min
,
max
,
[]
lipgloss
.
Color
{
lipgloss
.
Color
(
"#ff0000"
),
lipgloss
.
Color
(
"#00ff00ff"
)})
}
func
parseAreas
(
areas
[][]
ScreenArea
)
(
map
[
int
][
2
]
int
,
error
)
{
if
areas
==
nil
{
return
nil
,
errors
.
New
(
"error args is null"
)
}
index
:=
0
result
:=
make
(
map
[
int
][
2
]
int
)
for
kout
,
vout
:=
range
areas
{
for
kin
:=
range
vout
{
result
[
index
]
=
[
2
]
int
{
kout
,
kin
}
index
++
}
}
return
result
,
nil
}
cmd/hytop/tui/timechart.go
View file @
72702c5d
...
@@ -109,7 +109,7 @@ type MyTimeChart struct {
...
@@ -109,7 +109,7 @@ type MyTimeChart struct {
color
[]
lipgloss
.
Color
color
[]
lipgloss
.
Color
}
}
// New 新建图表,
其中dataSet的Key为数据集名称,value为数据集的颜色
// New 新建图表,
color的长度如果为1,则图表的颜色为color[0],如果color的长度为heigh,则图表的颜色随高度变化
func
NewTimeChart
(
width
,
height
int
,
vmin
,
vmax
float64
,
color
[]
lipgloss
.
Color
)
*
MyTimeChart
{
func
NewTimeChart
(
width
,
height
int
,
vmin
,
vmax
float64
,
color
[]
lipgloss
.
Color
)
*
MyTimeChart
{
result
:=
MyTimeChart
{}
result
:=
MyTimeChart
{}
result
.
max
=
vmax
result
.
max
=
vmax
...
@@ -122,10 +122,11 @@ func NewTimeChart(width, height int, vmin, vmax float64, color []lipgloss.Color)
...
@@ -122,10 +122,11 @@ func NewTimeChart(width, height int, vmin, vmax float64, color []lipgloss.Color)
result
.
lockPoints
.
Lock
()
result
.
lockPoints
.
Lock
()
result
.
points
=
linkedlist
.
New
[
tchart
.
TimePoint
]()
result
.
points
=
linkedlist
.
New
[
tchart
.
TimePoint
]()
now
:=
time
.
Now
()
now
:=
time
.
Now
()
// 用最小值填充空白点
t
:=
result
.
width
*
2
+
1
t
:=
result
.
width
*
2
+
1
tmpPoints
:=
make
([]
tchart
.
TimePoint
,
0
,
t
)
tmpPoints
:=
make
([]
tchart
.
TimePoint
,
0
,
t
)
for
i
:=
range
t
{
for
i
:=
range
t
{
tmpPoints
=
append
(
tmpPoints
,
tchart
.
TimePoint
{
Time
:
now
.
Add
(
time
.
Duration
(
-
i
)
*
time
.
Second
)})
tmpPoints
=
append
(
tmpPoints
,
tchart
.
TimePoint
{
Time
:
now
.
Add
(
time
.
Duration
(
-
i
)
*
time
.
Second
)
,
Value
:
result
.
min
})
}
}
result
.
lockPoints
.
Unlock
()
result
.
lockPoints
.
Unlock
()
s
:=
tchart
.
New
(
width
,
height
+
1
,
s
:=
tchart
.
New
(
width
,
height
+
1
,
...
@@ -136,10 +137,15 @@ func NewTimeChart(width, height int, vmin, vmax float64, color []lipgloss.Color)
...
@@ -136,10 +137,15 @@ func NewTimeChart(width, height int, vmin, vmax float64, color []lipgloss.Color)
tchart
.
WithTimeSeries
(
tmpPoints
),
tchart
.
WithTimeSeries
(
tmpPoints
),
)
)
result
.
chart
=
&
s
result
.
chart
=
&
s
result
.
color
=
color
if
len
(
color
)
==
2
{
result
.
color
=
GenGradientColor
(
color
[
0
],
color
[
1
],
height
)
}
else
{
result
.
color
=
color
}
return
&
result
return
&
result
}
}
// SortPoints 对不是自动填充的点进行排序
func
(
m
*
MyTimeChart
)
SortPoints
()
{
func
(
m
*
MyTimeChart
)
SortPoints
()
{
m
.
points
.
Sort
(
func
(
x
,
y
tchart
.
TimePoint
)
int
{
m
.
points
.
Sort
(
func
(
x
,
y
tchart
.
TimePoint
)
int
{
if
x
.
Time
.
After
(
y
.
Time
)
{
if
x
.
Time
.
After
(
y
.
Time
)
{
...
@@ -152,17 +158,22 @@ func (m *MyTimeChart) SortPoints() {
...
@@ -152,17 +158,22 @@ func (m *MyTimeChart) SortPoints() {
})
})
}
}
// RemoveUselessPoint 删除无用的点(即超出x轴范围的点)
func
(
m
*
MyTimeChart
)
RemoveUselessPoint
()
{
func
(
m
*
MyTimeChart
)
RemoveUselessPoint
()
{
m
.
SortPoints
()
m
.
SortPoints
()
// th为时间阈值,在此之前的点需要删除
th
:=
time
.
Now
()
.
Add
(
time
.
Duration
(
m
.
width
*-
2
)
*
time
.
Second
)
th
:=
time
.
Now
()
.
Add
(
time
.
Duration
(
m
.
width
*-
2
)
*
time
.
Second
)
for
{
for
{
// 如果没有点,则直接退出
t
,
b
:=
m
.
points
.
Get
(
0
)
t
,
b
:=
m
.
points
.
Get
(
0
)
if
!
b
{
if
!
b
{
break
break
}
}
if
t
.
Time
.
Before
(
th
)
{
if
t
.
Time
.
Before
(
th
)
{
// 如果第一个点在阈值之前,就删除
m
.
points
.
Remove
(
0
)
m
.
points
.
Remove
(
0
)
}
else
{
}
else
{
// 否则就退出
break
break
}
}
}
}
...
@@ -190,6 +201,7 @@ func (m *MyTimeChart) PutPoint(points []tchart.TimePoint) {
...
@@ -190,6 +201,7 @@ func (m *MyTimeChart) PutPoint(points []tchart.TimePoint) {
}
}
}
}
}
}
tmpPoint
=
append
(
tmpPoint
,
points
...
)
tmpPoint
=
append
(
tmpPoint
,
points
...
)
sort
.
Slice
(
tmpPoint
,
func
(
i
,
j
int
)
bool
{
sort
.
Slice
(
tmpPoint
,
func
(
i
,
j
int
)
bool
{
return
tmpPoint
[
i
]
.
Time
.
Before
(
tmpPoint
[
j
]
.
Time
)
return
tmpPoint
[
i
]
.
Time
.
Before
(
tmpPoint
[
j
]
.
Time
)
...
@@ -455,14 +467,17 @@ func (m *MyTimeChart) View() string {
...
@@ -455,14 +467,17 @@ func (m *MyTimeChart) View() string {
for
k
,
v
:=
range
runes
{
for
k
,
v
:=
range
runes
{
resultLines
[
k
]
=
string
(
v
)
resultLines
[
k
]
=
string
(
v
)
}
}
if
len
(
m
.
color
)
==
height
{
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
()
style
:=
lipgloss
.
NewStyle
()
for
k
,
v
:=
range
resultLines
{
for
k
,
v
:=
range
resultLines
{
resultLines
[
k
]
=
style
.
Foreground
(
m
.
color
[
k
])
.
Render
(
v
)
resultLines
[
k
]
=
style
.
Foreground
(
m
.
color
[
k
])
.
Render
(
v
)
}
}
}
else
if
len
(
m
.
color
)
==
1
{
return
strings
.
Join
(
resultLines
,
"
\n
"
)
style
:=
lipgloss
.
NewStyle
()
.
Foreground
(
m
.
color
[
0
])
default
:
return
style
.
Render
(
strings
.
Join
(
resultLines
,
"
\n
"
)
)
return
strings
.
Join
(
resultLines
,
"
\n
"
)
}
}
return
strings
.
Join
(
resultLines
,
"
\n
"
)
}
}
cmd/hytop/tui/tui_test.go
View file @
72702c5d
...
@@ -97,15 +97,23 @@ asd`
...
@@ -97,15 +97,23 @@ asd`
ss
:=
style
.
Render
(
str
)
ss
:=
style
.
Render
(
str
)
for
_
,
v
:=
range
strings
.
Split
(
ss
,
"
\n
"
)
{
for
_
,
v
:=
range
strings
.
Split
(
ss
,
"
\n
"
)
{
t
.
Logf
(
"w: %d"
,
lipgloss
.
Width
(
v
))
t
.
Logf
(
"w: %d"
,
lipgloss
.
Width
(
v
))
t
.
Log
(
ansi
.
TruncateLeft
(
v
,
2
,
""
))
t
.
Log
(
ansi
.
TruncateLeft
(
v
,
2
,
""
))
}
}
}
}
func
TestNewProcessEnv
(
t
*
testing
.
T
)
{
func
TestNewProcessEnv
(
t
*
testing
.
T
)
{
pe
:=
NewModelProcessEnv
(
10
,
50
,
1
,
nil
)
pe
:=
NewModelProcessEnv
(
10
,
50
,
1
,
nil
)
t
.
Log
(
pe
.
lineNum
)
t
.
Log
(
pe
.
lineNum
)
t
.
Log
(
pe
.
View
())
t
.
Log
(
pe
.
View
())
pe
.
Update
(
tea
.
KeyRight
)
pe
.
Update
(
tea
.
KeyRight
)
t
.
Log
(
pe
.
View
())
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
)
}
utils/utils_test.go
View file @
72702c5d
...
@@ -232,3 +232,8 @@ func TestReg(t *testing.T) {
...
@@ -232,3 +232,8 @@ func TestReg(t *testing.T) {
}
}
}
}
func
TestSpf
(
t
*
testing
.
T
)
{
s
:=
[]
int
{
1
,
2
,
3
,
4
}
t
.
Log
(
strings
.
ReplaceAll
(
fmt
.
Sprintf
(
"%v"
,
s
),
" "
,
","
))
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment