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
70a649e8
Commit
70a649e8
authored
Nov 26, 2025
by
liming6
Browse files
feature 添加组件的低亮度模式
parent
3e5adb86
Changes
8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
687 additions
and
123 deletions
+687
-123
cmd/hytop/Todo.md
cmd/hytop/Todo.md
+1
-4
cmd/hytop/tui/dcuinfo.go
cmd/hytop/tui/dcuinfo.go
+50
-27
cmd/hytop/tui/header.go
cmd/hytop/tui/header.go
+53
-15
cmd/hytop/tui/main.go
cmd/hytop/tui/main.go
+80
-11
cmd/hytop/tui/process.go
cmd/hytop/tui/process.go
+47
-12
cmd/hytop/tui/sysload.go
cmd/hytop/tui/sysload.go
+235
-51
cmd/hytop/tui/timechart.go
cmd/hytop/tui/timechart.go
+198
-3
utils/utils_test.go
utils/utils_test.go
+23
-0
No files found.
cmd/hytop/Todo.md
View file @
70a649e8
...
@@ -6,7 +6,4 @@
...
@@ -6,7 +6,4 @@
优化抓取数据逻辑,避免卡顿
优化抓取数据逻辑,避免卡顿
-
减少无效数据的收集
-
减少无效数据的收集
-
多goroutine收集数据,收集数据的goroutine不阻塞tui相关goroutine的运行
-
多goroutine收集数据,收集数据的goroutine不阻塞tui相关goroutine的运行
-
优化数据结构,降低时间复杂度
尝试使用.so文件抓取系统信息,而非使用二进制文件的输出,以提高采集效率
集成docker相关功能,同时注意错误隔断,避免无用错误影响主逻辑(docker容器重启会影响获取docker容器相关信息,会返回err)
cmd/hytop/tui/dcuinfo.go
View file @
70a649e8
...
@@ -16,12 +16,16 @@ import (
...
@@ -16,12 +16,16 @@ import (
)
)
type
ModelDCUInfo
struct
{
type
ModelDCUInfo
struct
{
width
,
height
int
// 屏幕尺寸
pro
progress
.
Model
// 进度条
pro
progress
.
Model
// 进度条
proLowLeight
progress
.
Model
// 低亮度进度条
proLowLeight
progress
.
Model
// 低亮度进度条
proWidth
int
// 进度条宽度
proWidth
int
// 进度条宽度
modMsg
*
ModelMsg
modMsg
*
ModelMsg
// 缓存的模型信息
width
,
height
int
actionMsg
*
ActionMsg
// 缓存的动作信息
actionMsg
*
ActionMsg
darkModule
bool
// 是否为黑暗模式
top
,
middle
,
bottom
string
// 缓存的边框
topL
,
middleL
,
bottomL
string
// 缓存的低亮度边框
subLin
int
// 边框更新标志
}
}
const
(
const
(
...
@@ -39,7 +43,7 @@ var (
...
@@ -39,7 +43,7 @@ var (
)
)
func
NewModelDCUInfo
(
w
,
h
int
)
*
ModelDCUInfo
{
func
NewModelDCUInfo
(
w
,
h
int
)
*
ModelDCUInfo
{
return
&
ModelDCUInfo
{
width
:
w
,
height
:
h
}
return
&
ModelDCUInfo
{
width
:
w
,
height
:
h
,
darkModule
:
false
}
}
}
func
(
m
*
ModelDCUInfo
)
Init
()
tea
.
Cmd
{
func
(
m
*
ModelDCUInfo
)
Init
()
tea
.
Cmd
{
...
@@ -68,6 +72,11 @@ func (m *ModelDCUInfo) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
...
@@ -68,6 +72,11 @@ func (m *ModelDCUInfo) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
return
m
,
nil
return
m
,
nil
case
*
ActionMsg
:
case
*
ActionMsg
:
m
.
actionMsg
=
msg
m
.
actionMsg
=
msg
if
msg
.
Action
!=
nil
{
m
.
darkModule
=
true
}
else
{
m
.
darkModule
=
false
}
return
m
,
nil
return
m
,
nil
}
}
return
m
,
nil
return
m
,
nil
...
@@ -80,14 +89,16 @@ func (m *ModelDCUInfo) View() string {
...
@@ -80,14 +89,16 @@ func (m *ModelDCUInfo) View() string {
lineWidth
:=
0
lineWidth
:=
0
qmap
,
qlock
:=
m
.
modMsg
.
DCUInfo
.
GetQuitInfo
()
qmap
,
qlock
:=
m
.
modMsg
.
DCUInfo
.
GetQuitInfo
()
smap
,
slock
:=
m
.
modMsg
.
DCUInfo
.
GetSlowInfo
()
smap
,
slock
:=
m
.
modMsg
.
DCUInfo
.
GetSlowInfo
()
defer
slock
.
Unlock
()
defer
qlock
.
Unlock
()
strBuilder
:=
strings
.
Builder
{}
strBuilder
:=
strings
.
Builder
{}
strCache
:=
strings
.
Builder
{}
strCache
:=
strings
.
Builder
{}
var
targetDCU
int
=
-
1
var
targetDCU
int
=
-
1
if
m
.
actionMsg
!=
nil
&&
m
.
actionMsg
.
TargetDCUIndex
!=
nil
{
if
m
.
actionMsg
!=
nil
&&
m
.
actionMsg
.
TargetDCUIndex
!=
nil
{
targetDCU
=
*
m
.
actionMsg
.
TargetDCUIndex
targetDCU
=
*
m
.
actionMsg
.
TargetDCUIndex
}
}
var
borderStr
string
=
myBorder
.
Left
if
m
.
darkModule
{
borderStr
=
LowLeightStyle
.
Render
(
borderStr
)
}
infos
:=
make
([]
string
,
0
)
infos
:=
make
([]
string
,
0
)
for
i
:=
range
64
{
for
i
:=
range
64
{
qinfo
,
haveq
:=
qmap
[
i
]
qinfo
,
haveq
:=
qmap
[
i
]
...
@@ -95,8 +106,8 @@ func (m *ModelDCUInfo) View() string {
...
@@ -95,8 +106,8 @@ func (m *ModelDCUInfo) View() string {
if
!
(
haveq
||
haves
)
{
if
!
(
haveq
||
haves
)
{
continue
continue
}
}
isLowStyle
:=
(
targetDCU
!=
qinfo
.
Id
&&
targetDCU
!=
-
1
)
isLowStyle
:=
(
targetDCU
!=
qinfo
.
Id
&&
targetDCU
!=
-
1
)
||
m
.
darkModule
strBuilder
.
WriteString
(
myB
order
.
Left
)
strBuilder
.
WriteString
(
b
order
Str
)
strCache
.
WriteByte
(
' '
)
strCache
.
WriteByte
(
' '
)
strCache
.
WriteString
(
FormatStr
(
strconv
.
Itoa
(
qinfo
.
Id
),
4
,
lipgloss
.
Left
))
strCache
.
WriteString
(
FormatStr
(
strconv
.
Itoa
(
qinfo
.
Id
),
4
,
lipgloss
.
Left
))
...
@@ -113,7 +124,7 @@ func (m *ModelDCUInfo) View() string {
...
@@ -113,7 +124,7 @@ func (m *ModelDCUInfo) View() string {
strBuilder
.
WriteString
(
strCache
.
String
())
strBuilder
.
WriteString
(
strCache
.
String
())
}
}
strCache
.
Reset
()
strCache
.
Reset
()
strBuilder
.
WriteString
(
myB
order
.
Left
)
strBuilder
.
WriteString
(
b
order
Str
)
strCache
.
WriteByte
(
' '
)
strCache
.
WriteByte
(
' '
)
strCache
.
WriteString
(
FormatStr
(
qinfo
.
BusId
,
13
,
lipgloss
.
Left
))
strCache
.
WriteString
(
FormatStr
(
qinfo
.
BusId
,
13
,
lipgloss
.
Left
))
...
@@ -130,7 +141,7 @@ func (m *ModelDCUInfo) View() string {
...
@@ -130,7 +141,7 @@ func (m *ModelDCUInfo) View() string {
strCache
.
WriteByte
(
' '
)
strCache
.
WriteByte
(
' '
)
strCache
.
WriteString
(
FormatStr
(
"Off"
,
16
,
lipgloss
.
Left
))
strCache
.
WriteString
(
FormatStr
(
"Off"
,
16
,
lipgloss
.
Left
))
strBuilder
.
WriteString
(
myB
order
.
Left
)
strBuilder
.
WriteString
(
b
order
Str
)
strCache
.
WriteByte
(
' '
)
strCache
.
WriteByte
(
' '
)
if
sinfo
.
Ecc
.
Load
()
{
if
sinfo
.
Ecc
.
Load
()
{
strCache
.
WriteString
(
FormatStr
(
"On"
,
3
,
lipgloss
.
Right
))
strCache
.
WriteString
(
FormatStr
(
"On"
,
3
,
lipgloss
.
Right
))
...
@@ -145,7 +156,7 @@ func (m *ModelDCUInfo) View() string {
...
@@ -145,7 +156,7 @@ func (m *ModelDCUInfo) View() string {
strBuilder
.
WriteString
(
strCache
.
String
())
strBuilder
.
WriteString
(
strCache
.
String
())
}
}
strCache
.
Reset
()
strCache
.
Reset
()
strBuilder
.
WriteString
(
myB
order
.
Left
)
strBuilder
.
WriteString
(
b
order
Str
)
if
isLowStyle
{
if
isLowStyle
{
strBuilder
.
WriteString
(
LowLeightStyle
.
Render
(
" DCU: "
))
strBuilder
.
WriteString
(
LowLeightStyle
.
Render
(
" DCU: "
))
...
@@ -156,9 +167,9 @@ func (m *ModelDCUInfo) View() string {
...
@@ -156,9 +167,9 @@ func (m *ModelDCUInfo) View() string {
strBuilder
.
WriteString
(
m
.
pro
.
ViewAs
(
float64
(
qinfo
.
DCUUTil
/
100
)))
strBuilder
.
WriteString
(
m
.
pro
.
ViewAs
(
float64
(
qinfo
.
DCUUTil
/
100
)))
strBuilder
.
WriteString
(
fmt
.
Sprintf
(
" %3d%% "
,
int
(
qinfo
.
DCUUTil
)))
strBuilder
.
WriteString
(
fmt
.
Sprintf
(
" %3d%% "
,
int
(
qinfo
.
DCUUTil
)))
}
}
strBuilder
.
WriteString
(
myB
order
.
Left
)
strBuilder
.
WriteString
(
b
order
Str
)
strBuilder
.
WriteByte
(
'\n'
)
strBuilder
.
WriteByte
(
'\n'
)
strBuilder
.
WriteString
(
myB
order
.
Left
)
strBuilder
.
WriteString
(
b
order
Str
)
strCache
.
WriteByte
(
' '
)
strCache
.
WriteByte
(
' '
)
strCache
.
WriteString
(
FormatStr
(
qinfo
.
Fan
,
4
,
lipgloss
.
Left
))
strCache
.
WriteString
(
FormatStr
(
qinfo
.
Fan
,
4
,
lipgloss
.
Left
))
...
@@ -175,7 +186,7 @@ func (m *ModelDCUInfo) View() string {
...
@@ -175,7 +186,7 @@ func (m *ModelDCUInfo) View() string {
strBuilder
.
WriteString
(
strCache
.
String
())
strBuilder
.
WriteString
(
strCache
.
String
())
}
}
strCache
.
Reset
()
strCache
.
Reset
()
strBuilder
.
WriteString
(
myB
order
.
Left
)
strBuilder
.
WriteString
(
b
order
Str
)
if
isLowStyle
{
if
isLowStyle
{
strCache
.
WriteByte
(
' '
)
strCache
.
WriteByte
(
' '
)
...
@@ -189,7 +200,7 @@ func (m *ModelDCUInfo) View() string {
...
@@ -189,7 +200,7 @@ func (m *ModelDCUInfo) View() string {
strBuilder
.
WriteByte
(
' '
)
strBuilder
.
WriteByte
(
' '
)
}
}
strBuilder
.
WriteString
(
myB
order
.
Left
)
strBuilder
.
WriteString
(
b
order
Str
)
if
isLowStyle
{
if
isLowStyle
{
strCache
.
WriteByte
(
' '
)
strCache
.
WriteByte
(
' '
)
...
@@ -207,7 +218,7 @@ func (m *ModelDCUInfo) View() string {
...
@@ -207,7 +218,7 @@ func (m *ModelDCUInfo) View() string {
strBuilder
.
WriteByte
(
' '
)
strBuilder
.
WriteByte
(
' '
)
}
}
strBuilder
.
WriteString
(
myB
order
.
Left
)
strBuilder
.
WriteString
(
b
order
Str
)
if
isLowStyle
{
if
isLowStyle
{
strCache
.
WriteString
(
" Mem: "
)
strCache
.
WriteString
(
" Mem: "
)
...
@@ -222,19 +233,31 @@ func (m *ModelDCUInfo) View() string {
...
@@ -222,19 +233,31 @@ func (m *ModelDCUInfo) View() string {
strBuilder
.
WriteString
(
fmt
.
Sprintf
(
"%3d%%"
,
int
(
100
*
float64
(
qinfo
.
MemUsed
)
/
float64
(
qinfo
.
MemTotal
))))
strBuilder
.
WriteString
(
fmt
.
Sprintf
(
"%3d%%"
,
int
(
100
*
float64
(
qinfo
.
MemUsed
)
/
float64
(
qinfo
.
MemTotal
))))
strBuilder
.
WriteByte
(
' '
)
strBuilder
.
WriteByte
(
' '
)
}
}
strBuilder
.
WriteString
(
myBorder
.
Left
)
strBuilder
.
WriteString
(
borderStr
)
strBuilder
.
WriteByte
(
'\n'
)
infos
=
append
(
infos
,
strBuilder
.
String
())
infos
=
append
(
infos
,
strBuilder
.
String
())
strBuilder
.
Reset
()
strBuilder
.
Reset
()
}
}
slock
.
Unlock
()
qlock
.
Unlock
()
if
len
(
infos
)
>
0
{
if
len
(
infos
)
>
0
{
lineWidth
=
lipgloss
.
Width
(
infos
[
0
])
lineWidth
=
lipgloss
.
Width
(
infos
[
0
])
}
}
subLen
:=
lineWidth
-
StaticWidth
-
1
subLen
:=
max
(
lineWidth
-
StaticWidth
-
1
,
0
)
if
subLen
<=
0
{
if
subLen
!=
m
.
subLin
{
subLen
=
0
top
:=
TopLine
+
strings
.
Repeat
(
"═"
,
subLen
)
+
"╕"
middle
:=
MiddleLine
+
strings
.
Repeat
(
"─"
,
subLen
)
+
"┤"
bottom
:=
BottomLine
+
strings
.
Repeat
(
"═"
,
subLen
)
+
"╡"
m
.
topL
=
LowLeightStyle
.
Render
(
top
)
+
"
\n
"
m
.
middleL
=
LowLeightStyle
.
Render
(
middle
)
+
"
\n
"
m
.
bottomL
=
LowLeightStyle
.
Render
(
bottom
)
+
"
\n
"
m
.
top
=
top
+
"
\n
"
m
.
middle
=
middle
+
"
\n
"
m
.
bottom
=
bottom
+
"
\n
"
}
if
m
.
darkModule
{
return
m
.
topL
+
strings
.
Join
(
infos
,
m
.
middleL
)
+
m
.
bottomL
}
else
{
return
m
.
top
+
strings
.
Join
(
infos
,
m
.
middle
)
+
m
.
bottom
}
}
top
:=
TopLine
+
strings
.
Repeat
(
"═"
,
subLen
)
+
"╕
\n
"
middle
:=
"
\n
"
+
MiddleLine
+
strings
.
Repeat
(
"─"
,
subLen
)
+
"┤
\n
"
bottom
:=
"
\n
"
+
BottomLine
+
strings
.
Repeat
(
"═"
,
subLen
)
+
"╡
\n
"
return
top
+
strings
.
Join
(
infos
,
middle
)
+
bottom
}
}
cmd/hytop/tui/header.go
View file @
70a649e8
...
@@ -10,28 +10,50 @@ import (
...
@@ -10,28 +10,50 @@ import (
)
)
type
ModelHeader
struct
{
type
ModelHeader
struct
{
t
time
.
Time
DCUTopVersion
string
DCUTopVersion
string
SMIVersion
string
SMIVersion
string
DriverVersion
string
DriverVersion
string
darkMode
bool
strBuilder
strings
.
Builder
style
lipgloss
.
Style
static
string
updateStatic
bool
}
}
func
NewModelHeader
()
*
ModelHeader
{
func
NewModelHeader
()
*
ModelHeader
{
return
&
ModelHeader
{}
return
&
ModelHeader
{
darkMode
:
false
,
style
:
lipgloss
.
NewStyle
()
.
Padding
(
0
,
1
),
updateStatic
:
true
,
}
}
}
func
(
mh
*
ModelHeader
)
Init
()
tea
.
Cmd
{
func
(
mh
*
ModelHeader
)
Init
()
tea
.
Cmd
{
mh
.
t
=
time
.
Now
()
return
nil
return
nil
}
}
func
(
mh
*
ModelHeader
)
Update
(
inputMsg
tea
.
Msg
)
(
tea
.
Model
,
tea
.
Cmd
)
{
func
(
mh
*
ModelHeader
)
Update
(
inputMsg
tea
.
Msg
)
(
tea
.
Model
,
tea
.
Cmd
)
{
switch
msg
:=
inputMsg
.
(
type
)
{
switch
msg
:=
inputMsg
.
(
type
)
{
case
*
ModelMsg
:
case
*
ModelMsg
:
mh
.
t
=
msg
.
t
if
mh
.
DCUTopVersion
!=
msg
.
MyVersion
||
mh
.
DriverVersion
!=
msg
.
Version
.
DriverVersion
||
mh
.
SMIVersion
!=
msg
.
Version
.
SMIVersion
{
mh
.
DCUTopVersion
=
msg
.
MyVersion
mh
.
DCUTopVersion
=
msg
.
MyVersion
mh
.
DriverVersion
=
msg
.
Version
.
DriverVersion
mh
.
DriverVersion
=
msg
.
Version
.
DriverVersion
mh
.
SMIVersion
=
msg
.
Version
.
SMIVersion
mh
.
SMIVersion
=
msg
.
Version
.
SMIVersion
hyv
:=
FormatStr
(
fmt
.
Sprintf
(
" hytop: %s"
,
mh
.
DCUTopVersion
),
18
,
lipgloss
.
Left
)
drv
:=
FormatStr
(
fmt
.
Sprintf
(
"Driver Version: %s"
,
mh
.
DriverVersion
),
35
,
lipgloss
.
Left
)
smiv
:=
FormatStr
(
fmt
.
Sprintf
(
"SMI Version: %s"
,
mh
.
SMIVersion
),
24
,
lipgloss
.
Left
)
mh
.
static
=
HeaderBorderStyle
.
Render
(
hyv
+
drv
+
smiv
)
+
Title
mh
.
updateStatic
=
true
}
else
{
mh
.
updateStatic
=
false
}
return
mh
,
nil
case
*
ActionMsg
:
if
msg
.
Action
!=
nil
{
mh
.
darkMode
=
true
}
else
{
mh
.
darkMode
=
false
}
return
mh
,
nil
return
mh
,
nil
}
}
return
mh
,
nil
return
mh
,
nil
...
@@ -41,22 +63,38 @@ const (
...
@@ -41,22 +63,38 @@ const (
Title
=
`
Title
=
`
├───────────────────────────────┬──────────────────────┬──────────────────────┤
├───────────────────────────────┬──────────────────────┬──────────────────────┤
│ DCU Name Performance Level │ Bus-Id DisP.A │ MIG M. ECC │
│ DCU Name Performance Level │ Bus-Id DisP.A │ MIG M. ECC │
│ Fan Temp Pwr:Usage/Cap │ Memory-Usage │ DCU-Util PowerMode │
│ Fan Temp Pwr:Usage/Cap │ Memory-Usage │ DCU-Util PowerMode │`
`
)
)
var
(
var
(
KeyStyle
=
lipgloss
.
NewStyle
()
.
Foreground
(
lipgloss
.
Color
(
"#842cffff"
))
.
Italic
(
true
)
KeyStyle
=
lipgloss
.
NewStyle
()
.
Foreground
(
lipgloss
.
Color
(
"#842cffff"
))
.
Italic
(
true
)
KeyLowLeightStyle
=
lipgloss
.
NewStyle
()
.
Foreground
(
lipgloss
.
Color
(
"#6a32b992"
))
.
Italic
(
true
)
KeyH
,
KeyQ
=
KeyStyle
.
SetString
(
"h"
)
.
String
(),
KeyStyle
.
SetString
(
"q"
)
.
String
()
KeyH
,
KeyQ
=
KeyStyle
.
SetString
(
"h"
)
.
String
(),
KeyStyle
.
SetString
(
"q"
)
.
String
()
KeyHL
,
KeyQL
=
KeyLowLeightStyle
.
SetString
(
"h"
)
.
String
(),
KeyLowLeightStyle
.
SetString
(
"q"
)
.
String
()
Space
=
strings
.
Repeat
(
" "
,
28
)
Space
=
strings
.
Repeat
(
" "
,
28
)
HeaderBorderStyle
=
lipgloss
.
NewStyle
()
.
Border
(
myBorder
,
true
,
true
,
false
,
true
)
HeaderBorderStyle
=
lipgloss
.
NewStyle
()
.
Border
(
myBorder
,
true
,
true
,
false
,
true
)
)
)
func
(
mh
*
ModelHeader
)
View
()
string
{
func
(
mh
*
ModelHeader
)
View
()
string
{
header
:=
fmt
.
Sprintf
(
"%s%s(Press %s for help or %s for quit)
\n
"
,
mh
.
t
.
Format
(
"2006-01-02 15:04:05"
),
Space
,
KeyH
,
KeyQ
)
mh
.
strBuilder
.
Reset
()
style
:=
lipgloss
.
NewStyle
()
.
Padding
(
0
,
1
)
if
mh
.
darkMode
{
hyv
:=
style
.
Width
(
18
)
.
Render
(
fmt
.
Sprintf
(
"hytop: %s"
,
mh
.
DCUTopVersion
))
mh
.
strBuilder
.
WriteString
(
time
.
Now
()
.
Format
(
"2006-01-02 15:04:05"
))
drv
:=
style
.
Width
(
35
)
.
Render
(
fmt
.
Sprintf
(
"Driver Version: %s"
,
mh
.
DriverVersion
))
mh
.
strBuilder
.
WriteString
(
Space
)
smiv
:=
style
.
Width
(
24
)
.
Render
(
fmt
.
Sprintf
(
"SMI Version: %s"
,
mh
.
SMIVersion
))
mh
.
strBuilder
.
WriteString
(
"(Press "
)
return
header
+
HeaderBorderStyle
.
Render
(
hyv
+
drv
+
smiv
)
+
Title
header
:=
LowLeightStyle
.
Render
(
mh
.
strBuilder
.
String
())
mh
.
strBuilder
.
Reset
()
mh
.
strBuilder
.
WriteString
(
header
)
mh
.
strBuilder
.
WriteString
(
KeyHL
)
mh
.
strBuilder
.
WriteString
(
LowLeightStyle
.
Render
(
" for help or "
))
mh
.
strBuilder
.
WriteString
(
KeyQL
)
mh
.
strBuilder
.
WriteString
(
LowLeightStyle
.
Render
(
" for quit)"
))
mh
.
strBuilder
.
WriteByte
(
'\n'
)
header
=
mh
.
strBuilder
.
String
()
return
header
+
LowLeightStyle
.
SetString
(
mh
.
static
)
.
String
()
+
"
\n
"
}
else
{
mh
.
strBuilder
.
WriteString
(
fmt
.
Sprintf
(
"%s%s(Press %s for help or %s for quit)
\n
"
,
time
.
Now
()
.
Format
(
"2006-01-02 15:04:05"
),
Space
,
KeyH
,
KeyQ
))
mh
.
strBuilder
.
WriteString
(
mh
.
static
)
mh
.
strBuilder
.
WriteByte
(
'\n'
)
return
mh
.
strBuilder
.
String
()
}
}
}
cmd/hytop/tui/main.go
View file @
70a649e8
...
@@ -4,7 +4,6 @@ import (
...
@@ -4,7 +4,6 @@ import (
"get-container/cmd/hytop/backend"
"get-container/cmd/hytop/backend"
"get-container/gpu"
"get-container/gpu"
"get-container/utils"
"get-container/utils"
"syscall"
"time"
"time"
tea
"github.com/charmbracelet/bubbletea"
tea
"github.com/charmbracelet/bubbletea"
...
@@ -35,11 +34,19 @@ type ModelMsg struct {
...
@@ -35,11 +34,19 @@ type ModelMsg struct {
}
}
type
TickMsg
time
.
Time
type
TickMsg
time
.
Time
type
ProcessAction
int
const
(
PAKill
ProcessAction
=
9
PAInt
ProcessAction
=
2
PATerm
ProcessAction
=
15
PANone
ProcessAction
=
0
)
// ActionMsg 动作消息
// ActionMsg 动作消息
type
ActionMsg
struct
{
type
ActionMsg
struct
{
SelectPids
map
[
int32
]
bool
// 选择的pid进程
SelectPids
map
[
int32
]
bool
// 选择的pid进程
Action
*
syscall
.
Signal
// 对选择的pid的动作
Action
*
ProcessAction
// 对选择的pid的动作
PidView
*
int32
// 进程视图指标的pid号,为null表示没有进入进程指标视图
PidView
*
int32
// 进程视图指标的pid号,为null表示没有进入进程指标视图
PointPid
*
int32
// 指针指向的pid,为null表示没有选择进程
PointPid
*
int32
// 指针指向的pid,为null表示没有选择进程
PidEnvView
*
int32
// 进程环境变量视图的pid号,为null表示不进入进程环境变量视图
PidEnvView
*
int32
// 进程环境变量视图的pid号,为null表示不进入进程环境变量视图
...
@@ -124,7 +131,8 @@ func (m *ModelMain) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
...
@@ -124,7 +131,8 @@ func (m *ModelMain) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
case
"h"
:
case
"h"
:
return
m
,
tea
.
Quit
return
m
,
tea
.
Quit
case
"k"
:
case
"k"
:
return
m
,
tea
.
Quit
cmd
:=
m
.
handleKeyK
()
return
m
,
cmd
case
"esc"
:
case
"esc"
:
cmd
:=
m
.
handleKeyEsc
()
cmd
:=
m
.
handleKeyEsc
()
return
m
,
cmd
return
m
,
cmd
...
@@ -168,7 +176,57 @@ func (m *ModelMain) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
...
@@ -168,7 +176,57 @@ func (m *ModelMain) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
return
m
,
nil
return
m
,
nil
}
}
func
(
m
*
ModelMain
)
handleKeyK
()
tea
.
Cmd
{
if
m
.
actionMsg
==
nil
{
return
nil
}
if
m
.
actionMsg
.
PointPid
==
nil
&&
m
.
actionMsg
.
SelectPids
==
nil
{
return
nil
}
var
pa
ProcessAction
=
PANone
if
m
.
actionMsg
.
Action
==
nil
{
m
.
actionMsg
.
Action
=
&
pa
}
header
,
_
:=
m
.
Header
.
Update
(
m
.
actionMsg
)
dcuInfo
,
_
:=
m
.
DCUInfo
.
Update
(
m
.
actionMsg
)
sysLoad
,
_
:=
m
.
SysLoad
.
Update
(
m
.
actionMsg
)
pidinfo
,
_
:=
m
.
ProcessInfo
.
Update
(
m
.
actionMsg
)
m
.
Header
=
header
.
(
*
ModelHeader
)
m
.
DCUInfo
=
dcuInfo
.
(
*
ModelDCUInfo
)
m
.
SysLoad
=
sysLoad
.
(
*
ModelSysLoad
)
m
.
ProcessInfo
=
pidinfo
.
(
*
ModelProcessInfo
)
return
nil
}
func
(
m
*
ModelMain
)
handleKeyEsc
()
tea
.
Cmd
{
func
(
m
*
ModelMain
)
handleKeyEsc
()
tea
.
Cmd
{
if
m
.
actionMsg
==
nil
{
return
nil
}
if
m
.
actionMsg
.
Action
!=
nil
{
m
.
actionMsg
.
Action
=
nil
header
,
_
:=
m
.
Header
.
Update
(
m
.
actionMsg
)
dcuInfo
,
_
:=
m
.
DCUInfo
.
Update
(
m
.
actionMsg
)
sysLoad
,
_
:=
m
.
SysLoad
.
Update
(
m
.
actionMsg
)
pidinfo
,
_
:=
m
.
ProcessInfo
.
Update
(
m
.
actionMsg
)
m
.
Header
=
header
.
(
*
ModelHeader
)
m
.
DCUInfo
=
dcuInfo
.
(
*
ModelDCUInfo
)
m
.
SysLoad
=
sysLoad
.
(
*
ModelSysLoad
)
m
.
ProcessInfo
=
pidinfo
.
(
*
ModelProcessInfo
)
return
nil
}
if
m
.
actionMsg
.
PointPid
!=
nil
||
m
.
actionMsg
.
SelectPids
!=
nil
||
m
.
actionMsg
.
TargetDCUIndex
!=
nil
{
m
.
actionMsg
.
PointPid
=
nil
m
.
actionMsg
.
SelectPids
=
nil
m
.
actionMsg
.
TargetDCUIndex
=
nil
header
,
_
:=
m
.
Header
.
Update
(
m
.
actionMsg
)
dcuInfo
,
_
:=
m
.
DCUInfo
.
Update
(
m
.
actionMsg
)
sysLoad
,
_
:=
m
.
SysLoad
.
Update
(
m
.
actionMsg
)
pidinfo
,
_
:=
m
.
ProcessInfo
.
Update
(
m
.
actionMsg
)
m
.
Header
=
header
.
(
*
ModelHeader
)
m
.
DCUInfo
=
dcuInfo
.
(
*
ModelDCUInfo
)
m
.
SysLoad
=
sysLoad
.
(
*
ModelSysLoad
)
m
.
ProcessInfo
=
pidinfo
.
(
*
ModelProcessInfo
)
}
return
nil
return
nil
}
}
...
@@ -256,6 +314,9 @@ func (m *ModelMain) handleKeyUp() tea.Cmd {
...
@@ -256,6 +314,9 @@ func (m *ModelMain) handleKeyUp() tea.Cmd {
if
m
.
modelMsg
.
DCUPidInfo
==
nil
{
if
m
.
modelMsg
.
DCUPidInfo
==
nil
{
return
nil
return
nil
}
}
if
m
.
actionMsg
.
Action
!=
nil
{
return
nil
}
processes
:=
make
([]
backend
.
DCUProcessInfo
,
0
)
processes
:=
make
([]
backend
.
DCUProcessInfo
,
0
)
for
index
:=
range
64
{
for
index
:=
range
64
{
p
,
have
:=
m
.
modelMsg
.
DCUPidInfo
[
index
]
p
,
have
:=
m
.
modelMsg
.
DCUPidInfo
[
index
]
...
@@ -289,11 +350,14 @@ func (m *ModelMain) handleKeyUp() tea.Cmd {
...
@@ -289,11 +350,14 @@ func (m *ModelMain) handleKeyUp() tea.Cmd {
m
.
actionMsg
.
PointPid
=
&
pid
m
.
actionMsg
.
PointPid
=
&
pid
idx
:=
processes
[
index
]
.
DCU
idx
:=
processes
[
index
]
.
DCU
m
.
actionMsg
.
TargetDCUIndex
=
&
idx
m
.
actionMsg
.
TargetDCUIndex
=
&
idx
m1
,
cmd1
:=
m
.
ProcessInfo
.
Update
(
m
.
actionMsg
)
m1
,
cmd1
:=
m
.
ProcessInfo
.
Update
(
m
.
actionMsg
)
m2
,
cmd2
:=
m
.
DCUInfo
.
Update
(
m
.
actionMsg
)
m2
,
cmd2
:=
m
.
DCUInfo
.
Update
(
m
.
actionMsg
)
m3
,
cmd3
:=
m
.
SysLoad
.
Update
(
m
.
actionMsg
)
m
.
ProcessInfo
=
m1
.
(
*
ModelProcessInfo
)
m
.
ProcessInfo
=
m1
.
(
*
ModelProcessInfo
)
m
.
DCUInfo
=
m2
.
(
*
ModelDCUInfo
)
m
.
DCUInfo
=
m2
.
(
*
ModelDCUInfo
)
return
tea
.
Batch
(
cmd1
,
cmd2
)
m
.
SysLoad
=
m3
.
(
*
ModelSysLoad
)
return
tea
.
Batch
(
cmd1
,
cmd2
,
cmd3
)
}
}
func
(
m
*
ModelMain
)
handleKeyDown
()
tea
.
Cmd
{
func
(
m
*
ModelMain
)
handleKeyDown
()
tea
.
Cmd
{
...
@@ -303,6 +367,9 @@ func (m *ModelMain) handleKeyDown() tea.Cmd {
...
@@ -303,6 +367,9 @@ func (m *ModelMain) handleKeyDown() tea.Cmd {
if
m
.
modelMsg
.
DCUPidInfo
==
nil
{
if
m
.
modelMsg
.
DCUPidInfo
==
nil
{
return
nil
return
nil
}
}
if
m
.
actionMsg
.
Action
!=
nil
{
return
nil
}
processes
:=
make
([]
backend
.
DCUProcessInfo
,
0
)
processes
:=
make
([]
backend
.
DCUProcessInfo
,
0
)
for
index
:=
range
64
{
for
index
:=
range
64
{
p
,
have
:=
m
.
modelMsg
.
DCUPidInfo
[
index
]
p
,
have
:=
m
.
modelMsg
.
DCUPidInfo
[
index
]
...
@@ -341,7 +408,9 @@ func (m *ModelMain) handleKeyDown() tea.Cmd {
...
@@ -341,7 +408,9 @@ func (m *ModelMain) handleKeyDown() tea.Cmd {
m
.
actionMsg
.
TargetDCUIndex
=
&
idx
m
.
actionMsg
.
TargetDCUIndex
=
&
idx
m1
,
cmd1
:=
m
.
ProcessInfo
.
Update
(
m
.
actionMsg
)
m1
,
cmd1
:=
m
.
ProcessInfo
.
Update
(
m
.
actionMsg
)
m2
,
cmd2
:=
m
.
DCUInfo
.
Update
(
m
.
actionMsg
)
m2
,
cmd2
:=
m
.
DCUInfo
.
Update
(
m
.
actionMsg
)
m3
,
cmd3
:=
m
.
SysLoad
.
Update
(
m
.
actionMsg
)
m
.
ProcessInfo
=
m1
.
(
*
ModelProcessInfo
)
m
.
ProcessInfo
=
m1
.
(
*
ModelProcessInfo
)
m
.
DCUInfo
=
m2
.
(
*
ModelDCUInfo
)
m
.
DCUInfo
=
m2
.
(
*
ModelDCUInfo
)
return
tea
.
Batch
(
cmd1
,
cmd2
)
m
.
SysLoad
=
m3
.
(
*
ModelSysLoad
)
return
tea
.
Batch
(
cmd1
,
cmd2
,
cmd3
)
}
}
cmd/hytop/tui/process.go
View file @
70a649e8
...
@@ -11,7 +11,7 @@ import (
...
@@ -11,7 +11,7 @@ import (
)
)
type
ModelProcessInfo
struct
{
type
ModelProcessInfo
struct
{
Title
string
Title
[
2
]
string
DoubleMiddleLine
string
DoubleMiddleLine
string
MiddleLine
string
MiddleLine
string
BottomLine
string
BottomLine
string
...
@@ -52,7 +52,22 @@ func (m *ModelProcessInfo) Init() tea.Cmd {
...
@@ -52,7 +52,22 @@ func (m *ModelProcessInfo) Init() tea.Cmd {
sb
.
WriteString
(
strings
.
Repeat
(
" "
,
m
.
width
-
2
-
lipgloss
.
Width
(
ModelProcessInfoTitle
)))
sb
.
WriteString
(
strings
.
Repeat
(
" "
,
m
.
width
-
2
-
lipgloss
.
Width
(
ModelProcessInfoTitle
)))
sb
.
WriteString
(
myBorder
.
Right
)
sb
.
WriteString
(
myBorder
.
Right
)
sb
.
WriteByte
(
'\n'
)
sb
.
WriteByte
(
'\n'
)
m
.
Title
=
sb
.
String
()
m
.
Title
[
0
]
=
sb
.
String
()
sb
.
Reset
()
sb
.
WriteString
(
myBorder
.
Left
)
sb
.
WriteString
(
Processes
)
uah
=
fmt
.
Sprintf
(
"%s@%s "
,
backend
.
User
,
backend
.
HostName
)
space
=
strings
.
Repeat
(
" "
,
m
.
width
-
lipgloss
.
Width
(
Processes
)
-
lipgloss
.
Width
(
uah
)
-
2
)
sb
.
WriteString
(
space
)
sb
.
WriteString
(
uah
)
sb
.
WriteString
(
myBorder
.
Right
)
sb
.
WriteByte
(
'\n'
)
sb
.
WriteString
(
myBorder
.
Left
)
sb
.
WriteString
(
ModelProcessInfoTitle
)
sb
.
WriteString
(
strings
.
Repeat
(
" "
,
m
.
width
-
2
-
lipgloss
.
Width
(
ModelProcessInfoTitle
)))
sb
.
WriteString
(
myBorder
.
Right
)
m
.
Title
[
1
]
=
LowLeightStyle
.
Render
(
sb
.
String
())
+
"
\n
"
m
.
DoubleMiddleLine
=
"╞"
+
strings
.
Repeat
(
"═"
,
m
.
width
-
2
)
+
"╡
\n
"
m
.
DoubleMiddleLine
=
"╞"
+
strings
.
Repeat
(
"═"
,
m
.
width
-
2
)
+
"╡
\n
"
m
.
MiddleLine
=
myBorder
.
MiddleLeft
+
strings
.
Repeat
(
"─"
,
m
.
width
-
2
)
+
myBorder
.
MiddleRight
+
"
\n
"
m
.
MiddleLine
=
myBorder
.
MiddleLeft
+
strings
.
Repeat
(
"─"
,
m
.
width
-
2
)
+
myBorder
.
MiddleRight
+
"
\n
"
m
.
BottomLine
=
myBorder
.
BottomLeft
+
strings
.
Repeat
(
"═"
,
m
.
width
-
2
)
+
myBorder
.
BottomRight
m
.
BottomLine
=
myBorder
.
BottomLeft
+
strings
.
Repeat
(
"═"
,
m
.
width
-
2
)
+
myBorder
.
BottomRight
...
@@ -61,6 +76,10 @@ func (m *ModelProcessInfo) Init() tea.Cmd {
...
@@ -61,6 +76,10 @@ func (m *ModelProcessInfo) Init() tea.Cmd {
}
}
func
(
m
*
ModelProcessInfo
)
View
()
string
{
func
(
m
*
ModelProcessInfo
)
View
()
string
{
darkMode
:=
false
if
m
.
actionMsg
!=
nil
&&
m
.
actionMsg
.
Action
!=
nil
{
darkMode
=
true
}
haveProcess
:=
false
haveProcess
:=
false
var
heightLightPid
int32
=
0
var
heightLightPid
int32
=
0
if
m
.
actionMsg
!=
nil
&&
m
.
actionMsg
.
PointPid
!=
nil
{
if
m
.
actionMsg
!=
nil
&&
m
.
actionMsg
.
PointPid
!=
nil
{
...
@@ -117,7 +136,10 @@ func (m *ModelProcessInfo) View() string {
...
@@ -117,7 +136,10 @@ func (m *ModelProcessInfo) View() string {
selected
=
true
selected
=
true
}
}
}
}
if
heightLightPid
==
process
.
Info
.
Pid
{
if
darkMode
{
selected
=
false
}
if
heightLightPid
==
process
.
Info
.
Pid
&&
!
darkMode
{
if
selected
{
if
selected
{
sb
.
WriteString
(
HeightSelectedStyle
.
Render
(
sbInner
.
String
()))
sb
.
WriteString
(
HeightSelectedStyle
.
Render
(
sbInner
.
String
()))
}
else
{
}
else
{
...
@@ -130,7 +152,6 @@ func (m *ModelProcessInfo) View() string {
...
@@ -130,7 +152,6 @@ func (m *ModelProcessInfo) View() string {
sb
.
WriteString
(
sbInner
.
String
())
sb
.
WriteString
(
sbInner
.
String
())
}
}
}
}
sbInner
.
Reset
()
sbInner
.
Reset
()
sb
.
WriteString
(
myBorder
.
Right
)
sb
.
WriteString
(
myBorder
.
Right
)
sb
.
WriteByte
(
'\n'
)
sb
.
WriteByte
(
'\n'
)
...
@@ -140,7 +161,20 @@ func (m *ModelProcessInfo) View() string {
...
@@ -140,7 +161,20 @@ func (m *ModelProcessInfo) View() string {
lines
=
append
(
lines
,
m
.
MiddleLine
)
lines
=
append
(
lines
,
m
.
MiddleLine
)
}
}
sb
.
WriteString
(
m
.
Title
)
if
darkMode
{
sb
.
WriteString
(
m
.
Title
[
1
])
sb
.
WriteString
(
m
.
DoubleMiddleLine
)
if
!
haveProcess
{
sb
.
WriteString
(
m
.
NoProceseLine
)
}
else
{
for
_
,
v
:=
range
lines
[
:
len
(
lines
)
-
1
]
{
sb
.
WriteString
(
v
)
}
}
sb
.
WriteString
(
m
.
BottomLine
)
return
LowLeightStyle
.
Render
(
sb
.
String
())
}
else
{
sb
.
WriteString
(
m
.
Title
[
0
])
sb
.
WriteString
(
m
.
DoubleMiddleLine
)
sb
.
WriteString
(
m
.
DoubleMiddleLine
)
if
!
haveProcess
{
if
!
haveProcess
{
sb
.
WriteString
(
m
.
NoProceseLine
)
sb
.
WriteString
(
m
.
NoProceseLine
)
...
@@ -151,6 +185,7 @@ func (m *ModelProcessInfo) View() string {
...
@@ -151,6 +185,7 @@ func (m *ModelProcessInfo) View() string {
}
}
sb
.
WriteString
(
m
.
BottomLine
)
sb
.
WriteString
(
m
.
BottomLine
)
return
sb
.
String
()
return
sb
.
String
()
}
}
}
func
(
m
*
ModelProcessInfo
)
Update
(
inputMsg
tea
.
Msg
)
(
tea
.
Model
,
tea
.
Cmd
)
{
func
(
m
*
ModelProcessInfo
)
Update
(
inputMsg
tea
.
Msg
)
(
tea
.
Model
,
tea
.
Cmd
)
{
...
...
cmd/hytop/tui/sysload.go
View file @
70a649e8
...
@@ -7,11 +7,12 @@ import (
...
@@ -7,11 +7,12 @@ import (
"image/color"
"image/color"
"strings"
"strings"
"sync"
"sync"
"sync/atomic"
"time"
"time"
tea
"github.com/charmbracelet/bubbletea"
tea
"github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/lipgloss"
"github.com/emirpasic/gods/v2/maps/
linkedhash
map"
"github.com/emirpasic/gods/v2/maps/
tree
map"
"github.com/emirpasic/gods/v2/trees/binaryheap"
"github.com/emirpasic/gods/v2/trees/binaryheap"
"github.com/lucasb-eyer/go-colorful"
"github.com/lucasb-eyer/go-colorful"
"github.com/muesli/gamut"
"github.com/muesli/gamut"
...
@@ -23,22 +24,33 @@ const (
...
@@ -23,22 +24,33 @@ const (
SysLoadCap
=
600
// 记录
SysLoadCap
=
600
// 记录
)
)
var
(
SysLoadBorder
=
LowLeightStyle
.
Render
(
`│
│
│
│
│`
)
)
// ModelSysLoad 系统负载组件
// ModelSysLoad 系统负载组件
type
ModelSysLoad
struct
{
type
ModelSysLoad
struct
{
SysMem
*
MyTimeChart
SysMem
*
MyTimeChart
SysCPU
*
MyTimeChart
SysCPU
*
MyTimeChart
DCU
*
MyTimeChart
DCU
*
MyTimeChart
DCUMem
*
MyTimeChart
DCUMem
*
MyTimeChart
SysInfo
*
linkedhashmap
.
Map
[
time
.
Time
,
SysLoadInfo
]
sysInfoLock
sync
.
Mutex
// sysinfo和keys的保护锁,防止并发写
Keys
*
binaryheap
.
Heap
[
time
.
Time
]
sysInfo
*
treemap
.
Map
[
time
.
Time
,
SysLoadInfo
]
keys
*
binaryheap
.
Heap
[
time
.
Time
]
current
*
SysLoadInfo
current
*
SysLoadInfo
topLine
string
topLine
,
topL
string
bottomLine
string
bottomLine
,
bottomL
string
style
lipgloss
.
Style
style
lipgloss
.
Style
width
int
// 组件总宽度
width
int
// 组件总宽度
colors
[]
color
.
Color
// 时序图的颜色表
colors
[]
color
.
Color
// 时序图的颜色表
colorsLow
[]
color
.
Color
// 低亮度模式的颜色表
actionMsg
*
ActionMsg
// 动作消息
actionMsg
*
ActionMsg
// 动作消息
modelMsg
*
ModelMsg
// 模型周期性消息
modelMsg
*
ModelMsg
// 模型周期性消息
DCUToShow
atomic
.
Int32
// 要显示的DCU信息的索引,如果为-1,表示显示dcu的平均数据
}
}
type
SysLoadInfo
struct
{
type
SysLoadInfo
struct
{
...
@@ -57,8 +69,16 @@ type SysLoadInfo struct {
...
@@ -57,8 +69,16 @@ type SysLoadInfo struct {
func
NewModelSysLoad
(
width
int
)
*
ModelSysLoad
{
func
NewModelSysLoad
(
width
int
)
*
ModelSysLoad
{
result
:=
ModelSysLoad
{}
result
:=
ModelSysLoad
{}
result
.
width
=
width
result
.
width
=
width
result
.
SysInfo
=
linkedhashmap
.
New
[
time
.
Time
,
SysLoadInfo
]()
result
.
sysInfo
=
treemap
.
NewWith
[
time
.
Time
,
SysLoadInfo
](
func
(
x
,
y
time
.
Time
)
int
{
result
.
Keys
=
binaryheap
.
NewWith
(
func
(
a
,
b
time
.
Time
)
int
{
if
x
.
Before
(
y
)
{
return
-
1
}
if
x
.
After
(
y
)
{
return
1
}
return
0
})
result
.
keys
=
binaryheap
.
NewWith
(
func
(
a
,
b
time
.
Time
)
int
{
if
a
.
After
(
b
)
{
if
a
.
After
(
b
)
{
return
1
return
1
}
}
...
@@ -68,6 +88,8 @@ func NewModelSysLoad(width int) *ModelSysLoad {
...
@@ -68,6 +88,8 @@ func NewModelSysLoad(width int) *ModelSysLoad {
return
0
return
0
})
})
result
.
colors
=
gamut
.
Blends
(
lipgloss
.
Color
(
"#ff0000"
),
lipgloss
.
Color
(
"#00ff00ff"
),
SysLoadHeight
)
result
.
colors
=
gamut
.
Blends
(
lipgloss
.
Color
(
"#ff0000"
),
lipgloss
.
Color
(
"#00ff00ff"
),
SysLoadHeight
)
result
.
colorsLow
=
gamut
.
Blends
(
gamut
.
Darker
(
lipgloss
.
Color
(
"#ff0000"
),
0.8
),
gamut
.
Darker
(
lipgloss
.
Color
(
"#00ff00ff"
),
0.8
),
SysLoadHeight
)
subLine
:=
width
-
StaticWidth
-
1
subLine
:=
width
-
StaticWidth
-
1
result
.
SysMem
=
NewTimeChart
(
SysLoadWidth
,
SysLoadHeight
,
0
,
100
,
result
.
colors
)
result
.
SysMem
=
NewTimeChart
(
SysLoadWidth
,
SysLoadHeight
,
0
,
100
,
result
.
colors
)
result
.
SysCPU
=
NewTimeChart
(
SysLoadWidth
,
SysLoadHeight
,
0
,
100
,
result
.
colors
)
result
.
SysCPU
=
NewTimeChart
(
SysLoadWidth
,
SysLoadHeight
,
0
,
100
,
result
.
colors
)
...
@@ -75,8 +97,12 @@ func NewModelSysLoad(width int) *ModelSysLoad {
...
@@ -75,8 +97,12 @@ func NewModelSysLoad(width int) *ModelSysLoad {
result
.
DCUMem
=
NewTimeChart
(
subLine
,
SysLoadHeight
,
0
,
100
,
result
.
colors
)
result
.
DCUMem
=
NewTimeChart
(
subLine
,
SysLoadHeight
,
0
,
100
,
result
.
colors
)
result
.
topLine
=
myBorder
.
MiddleLeft
+
genXAxis
(
SysLoadWidth
)
+
myBorder
.
Middle
+
genXAxis
(
subLine
)
+
myBorder
.
MiddleRight
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
.
bottomLine
=
"╞"
+
strings
.
Repeat
(
myBorder
.
Bottom
,
SysLoadWidth
)
+
"╧"
+
strings
.
Repeat
(
myBorder
.
Bottom
,
subLine
)
+
"╡"
result
.
style
=
lipgloss
.
NewStyle
()
result
.
topL
=
LowLeightStyle
.
Render
(
"├"
+
genXAxisNoStyle
(
SysLoadWidth
)
+
"┼"
+
genXAxisNoStyle
(
subLine
)
+
"┤"
)
result
.
bottomL
=
LowLeightStyle
.
Render
(
result
.
bottomLine
)
result
.
style
=
lipgloss
.
NewStyle
()
result
.
DCUToShow
.
Store
(
-
1
)
return
&
result
return
&
result
}
}
...
@@ -152,38 +178,122 @@ func (m *ModelSysLoad) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
...
@@ -152,38 +178,122 @@ func (m *ModelSysLoad) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
return
m
,
nil
return
m
,
nil
case
*
ActionMsg
:
case
*
ActionMsg
:
m
.
actionMsg
=
msg
m
.
actionMsg
=
msg
m
.
handleAction
()
return
m
,
nil
return
m
,
nil
}
}
return
m
,
nil
return
m
,
nil
}
}
// handleAction 处理ActionMsg
func
(
m
*
ModelSysLoad
)
handleAction
()
{
if
m
.
actionMsg
==
nil
||
m
.
actionMsg
.
TargetDCUIndex
==
nil
{
return
}
targetIndex
:=
int32
(
*
m
.
actionMsg
.
TargetDCUIndex
)
oldIndex
:=
m
.
DCUToShow
.
Swap
(
targetIndex
)
if
oldIndex
==
targetIndex
{
// 没有变
return
}
wg
:=
sync
.
WaitGroup
{}
wg
.
Add
(
1
)
index
:=
int
(
targetIndex
)
i
:=
m
.
sysInfo
.
Iterator
()
mem
:=
make
([]
tchart
.
TimePoint
,
0
,
m
.
sysInfo
.
Size
())
usage
:=
make
([]
tchart
.
TimePoint
,
0
,
m
.
sysInfo
.
Size
())
for
{
if
!
i
.
Next
()
{
break
}
key
:=
i
.
Key
()
var
valM
,
valU
float32
vU
,
haveU
:=
i
.
Value
()
.
DCUUsage
[
index
]
vM
,
haveM
:=
i
.
Value
()
.
DCUMemUsage
[
index
]
if
haveU
{
valU
=
vU
}
else
{
valU
=
0
}
if
haveM
{
valM
=
vM
}
else
{
valM
=
0
}
mem
=
append
(
mem
,
tchart
.
TimePoint
{
Time
:
key
,
Value
:
float64
(
valM
)})
usage
=
append
(
usage
,
tchart
.
TimePoint
{
Time
:
key
,
Value
:
float64
(
valU
)})
}
go
func
()
{
defer
wg
.
Done
()
m3
,
_
:=
m
.
DCU
.
Update
(
NewTimeCharMsg
(
usage
,
true
))
m
.
DCU
=
m3
.
(
*
MyTimeChart
)
}()
m4
,
_
:=
m
.
DCUMem
.
Update
(
NewTimeCharMsg
(
mem
,
true
))
m
.
DCUMem
=
m4
.
(
*
MyTimeChart
)
wg
.
Wait
()
}
func
(
m
*
ModelSysLoad
)
View
()
string
{
func
(
m
*
ModelSysLoad
)
View
()
string
{
darkMode
:=
false
if
m
.
actionMsg
!=
nil
&&
m
.
actionMsg
.
Action
!=
nil
{
darkMode
=
true
}
memUsed
:=
utils
.
MemorySize
{
Num
:
m
.
current
.
MemUsed
,
Unit
:
utils
.
Byte
}
memUsed
:=
utils
.
MemorySize
{
Num
:
m
.
current
.
MemUsed
,
Unit
:
utils
.
Byte
}
load
:=
fmt
.
Sprintf
(
" Load Average: %.2f %.2f %.2f
\n
CPU: %.1f%%"
,
m
.
current
.
Load1
,
m
.
current
.
Load5
,
m
.
current
.
Load15
,
m
.
current
.
CPUPercent
)
load
:=
fmt
.
Sprintf
(
" Load Average: %.2f %.2f %.2f
\n
CPU: %.1f%%"
,
m
.
current
.
Load1
,
m
.
current
.
Load5
,
m
.
current
.
Load15
,
m
.
current
.
CPUPercent
)
mem
:=
fmt
.
Sprintf
(
" MEM: %s (%.1f%%)"
,
memUsed
.
HumanReadStr
(
1
),
m
.
current
.
MemUsedPercent
)
mem
:=
fmt
.
Sprintf
(
" MEM: %s (%.1f%%)"
,
memUsed
.
HumanReadStr
(
1
),
m
.
current
.
MemUsedPercent
)
dcuMem
:=
fmt
.
Sprintf
(
" AVG DCU MEM: %.1f%%"
,
m
.
current
.
DCUMemUsageAvg
)
if
darkMode
{
dcu
:=
fmt
.
Sprintf
(
" AVG DCU UTL: %.1f%%"
,
m
.
current
.
DCUUsageAvg
)
load
=
LowLeightStyle
.
Render
(
load
)
mem
=
LowLeightStyle
.
Render
(
mem
)
}
var
dcuMem
,
dcu
string
if
m
.
actionMsg
==
nil
||
m
.
actionMsg
.
TargetDCUIndex
==
nil
{
dcuMem
=
fmt
.
Sprintf
(
" DCU AVG MEM: %.1f%%"
,
m
.
current
.
DCUMemUsageAvg
)
dcu
=
fmt
.
Sprintf
(
" DCU AVG UTL: %.1f%%"
,
m
.
current
.
DCUUsageAvg
)
}
else
{
index
:=
int
(
*
m
.
actionMsg
.
TargetDCUIndex
)
if
index
==
-
1
{
dcuMem
=
fmt
.
Sprintf
(
" DCU AVG MEM: %.1f%%"
,
m
.
current
.
DCUMemUsageAvg
)
dcu
=
fmt
.
Sprintf
(
" DCU AVG UTL: %.1f%%"
,
m
.
current
.
DCUUsageAvg
)
}
else
{
dcuMem
=
fmt
.
Sprintf
(
" DCU %2d MEM: %.1f%%"
,
index
,
m
.
current
.
DCUMemUsage
[
index
])
dcu
=
fmt
.
Sprintf
(
" DCU %2d UTL: %.1f%%"
,
index
,
m
.
current
.
DCUUsage
[
index
])
}
}
if
darkMode
{
dcuMem
=
LowLeightStyle
.
Render
(
dcuMem
)
dcu
=
LowLeightStyle
.
Render
(
dcu
)
}
if
darkMode
{
load
=
StackPosition
(
load
,
m
.
SysCPU
.
ViewWithColor
(
m
.
colorsLow
),
lipgloss
.
Top
,
lipgloss
.
Left
)
mem
=
StackPosition
(
mem
,
m
.
SysMem
.
ViewWithColor
(
m
.
colorsLow
),
lipgloss
.
Bottom
,
lipgloss
.
Left
)
dcuMem
=
StackPosition
(
dcuMem
,
m
.
DCUMem
.
ViewWithColor
(
m
.
colorsLow
),
lipgloss
.
Top
,
lipgloss
.
Left
)
dcu
=
StackPosition
(
dcu
,
m
.
DCU
.
ViewWithColor
(
m
.
colorsLow
),
lipgloss
.
Bottom
,
lipgloss
.
Left
)
load
=
lipgloss
.
JoinHorizontal
(
lipgloss
.
Top
,
SysLoadBorder
,
load
,
SysLoadBorder
)
mem
=
lipgloss
.
JoinHorizontal
(
lipgloss
.
Top
,
SysLoadBorder
,
mem
,
SysLoadBorder
)
dcuMem
=
lipgloss
.
JoinHorizontal
(
lipgloss
.
Top
,
dcuMem
,
SysLoadBorder
)
dcu
=
lipgloss
.
JoinHorizontal
(
lipgloss
.
Top
,
dcu
,
SysLoadBorder
)
}
else
{
load
=
StackPosition
(
load
,
m
.
SysCPU
.
View
(),
lipgloss
.
Top
,
lipgloss
.
Left
)
load
=
StackPosition
(
load
,
m
.
SysCPU
.
View
(),
lipgloss
.
Top
,
lipgloss
.
Left
)
mem
=
StackPosition
(
mem
,
m
.
SysMem
.
View
(),
lipgloss
.
Bottom
,
lipgloss
.
Left
)
mem
=
StackPosition
(
mem
,
m
.
SysMem
.
View
(),
lipgloss
.
Bottom
,
lipgloss
.
Left
)
s
:=
m
.
DCUMem
.
View
()
dcuMem
=
StackPosition
(
dcuMem
,
m
.
DCUMem
.
View
(),
lipgloss
.
Top
,
lipgloss
.
Left
)
s
=
lipgloss
.
NewStyle
()
.
Foreground
(
lipgloss
.
Color
(
"rgba(158, 143, 28, 0.86)"
))
.
Render
(
s
)
dcuMem
=
StackPosition
(
dcuMem
,
s
,
lipgloss
.
Top
,
lipgloss
.
Left
)
dcu
=
StackPosition
(
dcu
,
m
.
DCU
.
View
(),
lipgloss
.
Bottom
,
lipgloss
.
Left
)
dcu
=
StackPosition
(
dcu
,
m
.
DCU
.
View
(),
lipgloss
.
Bottom
,
lipgloss
.
Left
)
load
=
m
.
style
.
Border
(
myBorder
,
false
,
true
,
false
)
.
Render
(
load
)
load
=
m
.
style
.
Border
(
myBorder
,
false
,
true
,
false
)
.
Render
(
load
)
mem
=
m
.
style
.
Border
(
myBorder
,
false
,
true
,
false
)
.
Render
(
mem
)
mem
=
m
.
style
.
Border
(
myBorder
,
false
,
true
,
false
)
.
Render
(
mem
)
dcuMem
=
m
.
style
.
Border
(
myBorder
,
false
,
true
,
false
,
false
)
.
Render
(
dcuMem
)
dcuMem
=
m
.
style
.
Border
(
myBorder
,
false
,
true
,
false
,
false
)
.
Render
(
dcuMem
)
dcu
=
m
.
style
.
Border
(
myBorder
,
false
,
true
,
false
,
false
)
.
Render
(
dcu
)
dcu
=
m
.
style
.
Border
(
myBorder
,
false
,
true
,
false
,
false
)
.
Render
(
dcu
)
}
up
:=
lipgloss
.
JoinHorizontal
(
lipgloss
.
Top
,
load
,
dcuMem
)
up
:=
lipgloss
.
JoinHorizontal
(
lipgloss
.
Top
,
load
,
dcuMem
)
down
:=
lipgloss
.
JoinHorizontal
(
lipgloss
.
Top
,
mem
,
dcu
)
down
:=
lipgloss
.
JoinHorizontal
(
lipgloss
.
Top
,
mem
,
dcu
)
if
darkMode
{
return
lipgloss
.
JoinVertical
(
lipgloss
.
Left
,
up
,
m
.
topL
,
down
,
m
.
bottomL
)
+
"
\n
"
}
return
lipgloss
.
JoinVertical
(
lipgloss
.
Left
,
up
,
m
.
topLine
,
down
,
m
.
bottomLine
)
+
"
\n
"
return
lipgloss
.
JoinVertical
(
lipgloss
.
Left
,
up
,
m
.
topLine
,
down
,
m
.
bottomLine
)
+
"
\n
"
}
}
// updateInfo
// updateInfo
向ModelSysLoad中添加数据
func
(
m
*
ModelSysLoad
)
updateInfo
(
t
*
ModelMsg
)
{
func
(
m
*
ModelSysLoad
)
updateInfo
(
t
*
ModelMsg
)
{
sysInfo
,
_
:=
utils
.
GetSysInfo
()
sysInfo
,
_
:=
utils
.
GetSysInfo
()
s
:=
SysLoadInfo
{}
s
:=
SysLoadInfo
{}
...
@@ -213,37 +323,111 @@ func (m *ModelSysLoad) updateInfo(t *ModelMsg) {
...
@@ -213,37 +323,111 @@ func (m *ModelSysLoad) updateInfo(t *ModelMsg) {
lock
.
Unlock
()
lock
.
Unlock
()
s
.
DCUMemUsageAvg
/=
float32
(
l
)
s
.
DCUMemUsageAvg
/=
float32
(
l
)
s
.
DCUUsageAvg
/=
float32
(
l
)
s
.
DCUUsageAvg
/=
float32
(
l
)
s
.
DCUMemUsage
[
-
1
]
=
s
.
DCUMemUsageAvg
s
.
DCUUsage
[
-
1
]
=
s
.
DCUUsageAvg
m
.
current
=
&
s
m
.
current
=
&
s
needUpdate
,
index
:=
false
,
-
1
if
m
.
actionMsg
!=
nil
&&
m
.
actionMsg
.
TargetDCUIndex
!=
nil
{
index
=
*
m
.
actionMsg
.
TargetDCUIndex
// 需要进入特定DCU的图表
oldIndex
:=
m
.
DCUToShow
.
Swap
(
int32
(
index
))
if
oldIndex
!=
int32
(
*
m
.
actionMsg
.
TargetDCUIndex
)
{
// 需要更新
needUpdate
=
true
}
}
else
{
oldIndex
:=
m
.
DCUToShow
.
Swap
(
-
1
)
if
oldIndex
!=
-
1
{
// 需要更新
needUpdate
=
true
}
}
wg
:=
sync
.
WaitGroup
{}
wg
:=
sync
.
WaitGroup
{}
wg
.
Add
(
4
)
wg
.
Add
(
4
)
go
func
()
{
go
func
()
{
defer
wg
.
Done
()
defer
wg
.
Done
()
m1
,
_
:=
m
.
SysMem
.
Update
(
My
TimeChar
t
Msg
{
[]
tchart
.
TimePoint
{{
Time
:
t
.
t
,
Value
:
s
.
MemUsedPercent
}}
}
)
m1
,
_
:=
m
.
SysMem
.
Update
(
New
TimeCharMsg
(
[]
tchart
.
TimePoint
{{
Time
:
t
.
t
,
Value
:
s
.
MemUsedPercent
}}
,
false
)
)
m
.
SysMem
=
m1
.
(
*
MyTimeChart
)
m
.
SysMem
=
m1
.
(
*
MyTimeChart
)
}()
}()
go
func
()
{
go
func
()
{
defer
wg
.
Done
()
defer
wg
.
Done
()
m2
,
_
:=
m
.
SysCPU
.
Update
(
My
TimeChar
t
Msg
{
[]
tchart
.
TimePoint
{{
Time
:
t
.
t
,
Value
:
s
.
CPUPercent
}}
}
)
m2
,
_
:=
m
.
SysCPU
.
Update
(
New
TimeCharMsg
(
[]
tchart
.
TimePoint
{{
Time
:
t
.
t
,
Value
:
s
.
CPUPercent
}}
,
false
)
)
m
.
SysCPU
=
m2
.
(
*
MyTimeChart
)
m
.
SysCPU
=
m2
.
(
*
MyTimeChart
)
}()
}()
// 存放数据
m
.
sysInfoLock
.
Lock
()
m
.
sysInfo
.
Put
(
t
.
t
,
s
)
m
.
keys
.
Push
(
t
.
t
)
if
m
.
keys
.
Size
()
>
SysLoadCap
{
delKey
,
have
:=
m
.
keys
.
Pop
()
if
have
{
m
.
sysInfo
.
Remove
(
delKey
)
}
}
m
.
sysInfoLock
.
Unlock
()
if
needUpdate
{
// 准备全量数据
i
:=
m
.
sysInfo
.
Iterator
()
mem
:=
make
([]
tchart
.
TimePoint
,
0
,
m
.
sysInfo
.
Size
())
usage
:=
make
([]
tchart
.
TimePoint
,
0
,
m
.
sysInfo
.
Size
())
for
{
if
!
i
.
Next
()
{
break
}
key
:=
i
.
Key
()
var
valM
,
valU
float32
vU
,
haveU
:=
i
.
Value
()
.
DCUUsage
[
index
]
vM
,
haveM
:=
i
.
Value
()
.
DCUMemUsage
[
index
]
if
haveU
{
valU
=
vU
}
else
{
valU
=
0
}
if
haveM
{
valM
=
vM
}
else
{
valM
=
0
}
mem
=
append
(
mem
,
tchart
.
TimePoint
{
Time
:
key
,
Value
:
float64
(
valM
)})
usage
=
append
(
usage
,
tchart
.
TimePoint
{
Time
:
key
,
Value
:
float64
(
valU
)})
}
go
func
()
{
go
func
()
{
defer
wg
.
Done
()
defer
wg
.
Done
()
m3
,
_
:=
m
.
DCU
.
Update
(
My
TimeChar
t
Msg
{[]
tchart
.
TimePoint
{{
Time
:
t
.
t
,
Value
:
float64
(
s
.
DCUUsageAvg
)}}}
)
m3
,
_
:=
m
.
DCU
.
Update
(
New
TimeCharMsg
(
usage
,
true
)
)
m
.
DCU
=
m3
.
(
*
MyTimeChart
)
m
.
DCU
=
m3
.
(
*
MyTimeChart
)
}()
}()
go
func
()
{
go
func
()
{
defer
wg
.
Done
()
defer
wg
.
Done
()
m4
,
_
:=
m
.
DCUMem
.
Update
(
My
TimeChar
t
Msg
{[]
tchart
.
TimePoint
{{
Time
:
t
.
t
,
Value
:
float64
(
s
.
DCUMemUsageAvg
)}}}
)
m4
,
_
:=
m
.
DCUMem
.
Update
(
New
TimeCharMsg
(
mem
,
true
)
)
m
.
DCUMem
=
m4
.
(
*
MyTimeChart
)
m
.
DCUMem
=
m4
.
(
*
MyTimeChart
)
}()
}()
// 存放数据
}
else
{
m
.
SysInfo
.
Put
(
t
.
t
,
s
)
// 无需更新,仅追加数据
m
.
Keys
.
Push
(
t
.
t
)
var
valM
,
valU
float32
if
m
.
Keys
.
Size
()
>
SysLoadCap
{
vU
,
haveU
:=
s
.
DCUUsage
[
index
]
delKey
,
have
:=
m
.
Keys
.
Pop
()
vM
,
haveM
:=
s
.
DCUMemUsage
[
index
]
if
have
{
if
haveU
{
m
.
SysInfo
.
Remove
(
delKey
)
valU
=
vU
}
else
{
valU
=
0
}
}
if
haveM
{
valM
=
vM
}
else
{
valM
=
0
}
go
func
()
{
defer
wg
.
Done
()
m3
,
_
:=
m
.
DCU
.
Update
(
NewTimeCharMsg
([]
tchart
.
TimePoint
{{
Time
:
t
.
t
,
Value
:
float64
(
valU
)}},
false
))
m
.
DCU
=
m3
.
(
*
MyTimeChart
)
}()
go
func
()
{
defer
wg
.
Done
()
m4
,
_
:=
m
.
DCUMem
.
Update
(
NewTimeCharMsg
([]
tchart
.
TimePoint
{{
Time
:
t
.
t
,
Value
:
float64
(
valM
)}},
false
))
m
.
DCUMem
=
m4
.
(
*
MyTimeChart
)
}()
}
}
wg
.
Wait
()
wg
.
Wait
()
}
}
...
...
cmd/hytop/tui/timechart.go
View file @
70a649e8
...
@@ -57,9 +57,46 @@ func genXAxis(l int) string {
...
@@ -57,9 +57,46 @@ func genXAxis(l int) string {
return
result
return
result
}
}
func
genXAxisNoStyle
(
l
int
)
string
{
t60
:=
l
/
30
t30
:=
l
>=
18
var
result
string
if
t30
{
result
=
A
+
strings
.
Repeat
(
"─"
,
14
)
result
=
"30s"
+
result
}
else
{
return
strings
.
Repeat
(
"─"
,
l
)
}
// 长度不超过33
if
l
<
33
{
return
strings
.
Repeat
(
"─"
,
l
-
18
)
+
result
}
for
i
:=
1
;
i
<=
t60
+
1
;
i
++
{
timeStr
:=
strconv
.
Itoa
(
i
*
60
)
+
"s"
timeStrLen
:=
len
(
timeStr
)
targetLen
:=
timeStrLen
+
i
*
30
if
l
<
targetLen
{
// 不渲染标记,仅增加轴长度
result
=
strings
.
Repeat
(
"─"
,
l
-
lipgloss
.
Width
(
result
))
+
result
break
}
// 渲染标记
result
=
timeStr
+
A
+
strings
.
Repeat
(
"─"
,
targetLen
-
lipgloss
.
Width
(
result
)
-
timeStrLen
-
1
)
+
result
}
return
result
}
// MyTimeChartMsg 时间流表消息,用于插入数据
// MyTimeChartMsg 时间流表消息,用于插入数据
type
MyTimeChartMsg
struct
{
type
MyTimeChartMsg
struct
{
Points
[]
tchart
.
TimePoint
Points
[]
tchart
.
TimePoint
// 待添加的数据点
Reset
bool
// 添加数据点前是否清除原有数据点
}
func
NewTimeCharMsg
(
point
[]
tchart
.
TimePoint
,
reset
bool
)
MyTimeChartMsg
{
return
MyTimeChartMsg
{
Points
:
point
,
Reset
:
reset
,
}
}
}
// MyTimeChart 特化的时间流表,时间区域就是宽度的2倍,单位是秒
// MyTimeChart 特化的时间流表,时间区域就是宽度的2倍,单位是秒
...
@@ -103,7 +140,9 @@ func NewTimeChart(width, height int, vmin, vmax float64, color []color.Color) *M
...
@@ -103,7 +140,9 @@ func NewTimeChart(width, height int, vmin, vmax float64, color []color.Color) *M
return
&
result
return
&
result
}
}
// PutPoint 添加若干数据点
func
(
m
*
MyTimeChart
)
PutPoint
(
points
[]
tchart
.
TimePoint
)
{
func
(
m
*
MyTimeChart
)
PutPoint
(
points
[]
tchart
.
TimePoint
)
{
m
.
lockPoints
.
Lock
()
ops
:=
append
(
m
.
points
,
points
...
)
ops
:=
append
(
m
.
points
,
points
...
)
// 排序
// 排序
sort
.
Slice
(
ops
,
func
(
i
,
j
int
)
bool
{
sort
.
Slice
(
ops
,
func
(
i
,
j
int
)
bool
{
...
@@ -117,7 +156,7 @@ func (m *MyTimeChart) PutPoint(points []tchart.TimePoint) {
...
@@ -117,7 +156,7 @@ func (m *MyTimeChart) PutPoint(points []tchart.TimePoint) {
break
break
}
}
}
}
nps
:=
append
(
make
([]
tchart
.
TimePoint
,
0
),
ops
[
targetIndex
:
]
...
)
nps
:=
append
(
make
([]
tchart
.
TimePoint
,
0
,
targetIndex
+
1
),
ops
[
targetIndex
:
]
...
)
m
.
points
=
nps
m
.
points
=
nps
s
:=
tchart
.
New
(
m
.
width
,
m
.
height
+
1
,
s
:=
tchart
.
New
(
m
.
width
,
m
.
height
+
1
,
tchart
.
WithLineStyle
(
runes
.
ThinLineStyle
),
tchart
.
WithLineStyle
(
runes
.
ThinLineStyle
),
...
@@ -126,6 +165,62 @@ func (m *MyTimeChart) PutPoint(points []tchart.TimePoint) {
...
@@ -126,6 +165,62 @@ func (m *MyTimeChart) PutPoint(points []tchart.TimePoint) {
tchart
.
WithXYSteps
(
0
,
0
),
tchart
.
WithXYSteps
(
0
,
0
),
tchart
.
WithTimeSeries
(
m
.
points
),
tchart
.
WithTimeSeries
(
m
.
points
),
)
)
m
.
lockPoints
.
Unlock
()
// 插入数据
m
.
chart
=
nil
m
.
chart
=
&
s
m
.
chart
.
DrawXYAxisAndLabel
()
m
.
chart
.
DrawBrailleAll
()
}
// ResetPutPoint 清除原有的数据,然后插入数据
func
(
m
*
MyTimeChart
)
ResetPutPoint
(
points
[]
tchart
.
TimePoint
)
{
// 清除原有的点
m
.
lockPoints
.
Lock
()
m
.
points
=
nil
// 排序输入数据
sort
.
Slice
(
points
,
func
(
i
,
j
int
)
bool
{
return
points
[
i
]
.
Time
.
Before
(
points
[
j
]
.
Time
)
})
// 补充数据 或 剔除数据
now
:=
time
.
Now
()
threshold
:=
now
.
Add
(
time
.
Duration
(
m
.
width
*-
2
)
*
time
.
Second
)
first
:=
points
[
0
]
.
Time
if
first
.
Before
(
threshold
)
{
// 需要剔除数据
index
:=
0
for
k
,
v
:=
range
points
{
if
v
.
Time
.
Before
(
threshold
)
{
continue
}
else
{
index
=
k
break
}
}
m
.
points
=
append
(
make
([]
tchart
.
TimePoint
,
0
,
len
(
points
[
index
:
])),
points
[
index
:
]
...
)
}
else
if
first
.
After
(
threshold
)
{
// 需要补充数据
m
.
points
=
make
([]
tchart
.
TimePoint
,
0
,
len
(
points
)
+
2
*
m
.
width
+
1
)
max
:=
m
.
width
*
2
for
i
:=
1
;
i
<
max
;
i
++
{
ta
:=
now
.
Add
(
time
.
Second
*
time
.
Duration
(
-
i
))
if
!
ta
.
Before
(
threshold
)
{
m
.
points
=
append
(
m
.
points
,
tchart
.
TimePoint
{
Time
:
ta
,
Value
:
m
.
min
})
}
else
{
break
}
}
m
.
points
=
append
(
m
.
points
,
points
...
)
}
s
:=
tchart
.
New
(
m
.
width
,
m
.
height
+
1
,
tchart
.
WithLineStyle
(
runes
.
ThinLineStyle
),
tchart
.
WithZoneManager
(
m
.
zM
),
tchart
.
WithYRange
(
m
.
min
,
m
.
max
),
tchart
.
WithXYSteps
(
0
,
0
),
tchart
.
WithTimeSeries
(
m
.
points
),
)
m
.
lockPoints
.
Unlock
()
// 插入数据
// 插入数据
m
.
chart
=
nil
m
.
chart
=
nil
m
.
chart
=
&
s
m
.
chart
=
&
s
...
@@ -142,12 +237,112 @@ func (m *MyTimeChart) Init() tea.Cmd {
...
@@ -142,12 +237,112 @@ func (m *MyTimeChart) Init() tea.Cmd {
func
(
m
*
MyTimeChart
)
Update
(
inputMsg
tea
.
Msg
)
(
tea
.
Model
,
tea
.
Cmd
)
{
func
(
m
*
MyTimeChart
)
Update
(
inputMsg
tea
.
Msg
)
(
tea
.
Model
,
tea
.
Cmd
)
{
switch
msg
:=
inputMsg
.
(
type
)
{
switch
msg
:=
inputMsg
.
(
type
)
{
case
MyTimeChartMsg
:
case
MyTimeChartMsg
:
if
msg
.
Reset
{
m
.
ResetPutPoint
(
msg
.
Points
)
}
else
{
m
.
PutPoint
(
msg
.
Points
)
m
.
PutPoint
(
msg
.
Points
)
}
return
m
,
nil
return
m
,
nil
}
}
return
m
,
nil
return
m
,
nil
}
}
func
(
m
*
MyTimeChart
)
ViewWithColor
(
color
[]
color
.
Color
)
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
:=
make
([]
string
,
height
)
for
k
,
v
:=
range
runes
{
resultLines
[
k
]
=
string
(
v
)
}
if
len
(
color
)
==
height
{
for
k
,
v
:=
range
resultLines
{
c
,
_
:=
colorful
.
MakeColor
(
color
[
k
])
resultLines
[
k
]
=
lipgloss
.
NewStyle
()
.
Foreground
(
lipgloss
.
Color
(
c
.
Hex
()))
.
Render
(
v
)
}
}
return
strings
.
Join
(
resultLines
,
"
\n
"
)
}
func
(
m
*
MyTimeChart
)
View
()
string
{
func
(
m
*
MyTimeChart
)
View
()
string
{
str
:=
m
.
zM
.
Scan
(
m
.
chart
.
View
())
str
:=
m
.
zM
.
Scan
(
m
.
chart
.
View
())
sb
:=
strings
.
Builder
{}
sb
:=
strings
.
Builder
{}
...
...
utils/utils_test.go
View file @
70a649e8
...
@@ -5,6 +5,9 @@ import (
...
@@ -5,6 +5,9 @@ import (
"strings"
"strings"
"sync/atomic"
"sync/atomic"
"testing"
"testing"
"time"
"github.com/emirpasic/gods/v2/maps/treemap"
)
)
func
TestRegexp
(
t
*
testing
.
T
)
{
func
TestRegexp
(
t
*
testing
.
T
)
{
...
@@ -111,4 +114,24 @@ func TestChartype(t *testing.T) {
...
@@ -111,4 +114,24 @@ func TestChartype(t *testing.T) {
t
.
Log
(
r
)
t
.
Log
(
r
)
t
.
Log
(
CharTypeOr
(
GetCharType
(
' '
),
GetCharType
(
' '
)))
t
.
Log
(
CharTypeOr
(
GetCharType
(
' '
),
GetCharType
(
' '
)))
t
.
Log
(
GetCharType
(
'⢠'
))
t
.
Log
(
GetCharType
(
'⢠'
))
s
:=
treemap
.
NewWith
[
time
.
Time
,
int
](
func
(
x
,
y
time
.
Time
)
int
{
if
x
.
Before
(
y
)
{
return
-
1
}
if
x
.
After
(
y
)
{
return
1
}
return
0
})
s
.
Put
(
time
.
Now
(),
3
)
s
.
Put
(
time
.
Now
()
.
Add
(
time
.
Second
),
2
)
s
.
Put
(
time
.
Now
()
.
Add
(
-
2
*
time
.
Second
),
9
)
i
:=
s
.
Iterator
()
for
{
if
!
i
.
Next
()
{
break
}
t
.
Logf
(
"%v: %d"
,
i
.
Key
(),
i
.
Value
())
}
}
}
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