114 lines
2.6 KiB
Go
114 lines
2.6 KiB
Go
package ui
|
|
|
|
import (
|
|
"math"
|
|
"sync"
|
|
|
|
"github.com/gdamore/tcell/v2"
|
|
"github.com/rivo/tview"
|
|
)
|
|
|
|
type rateSample struct {
|
|
inBitsPerSecond float64
|
|
outBitsPerSecond float64
|
|
}
|
|
|
|
// RateGraph draws inbound and outbound bit-rate history.
|
|
type RateGraph struct {
|
|
*tview.Box
|
|
mu sync.Mutex
|
|
samples []rateSample
|
|
maxSamples int
|
|
}
|
|
|
|
// NewRateGraph creates a graph primitive with bounded history.
|
|
func NewRateGraph(maxSamples int) *RateGraph {
|
|
graph := &RateGraph{
|
|
Box: tview.NewBox().SetBorder(true).SetTitle(" Interface traffic "),
|
|
maxSamples: maxSamples,
|
|
}
|
|
return graph
|
|
}
|
|
|
|
// AddSample appends one inbound/outbound sample to the graph.
|
|
func (g *RateGraph) AddSample(inBitsPerSecond float64, outBitsPerSecond float64) {
|
|
g.mu.Lock()
|
|
defer g.mu.Unlock()
|
|
g.samples = append(g.samples, rateSample{
|
|
inBitsPerSecond: inBitsPerSecond,
|
|
outBitsPerSecond: outBitsPerSecond,
|
|
})
|
|
if len(g.samples) > g.maxSamples {
|
|
g.samples = g.samples[len(g.samples)-g.maxSamples:]
|
|
}
|
|
}
|
|
|
|
// Draw renders the graph primitive.
|
|
func (g *RateGraph) Draw(screen tcell.Screen) {
|
|
g.Box.DrawForSubclass(screen, g)
|
|
x, y, width, height := g.GetInnerRect()
|
|
if width <= 0 || height <= 0 {
|
|
return
|
|
}
|
|
|
|
samples := g.copySamples()
|
|
if len(samples) == 0 {
|
|
return
|
|
}
|
|
|
|
maxRate := maxSampleRate(samples)
|
|
if maxRate <= 0 {
|
|
maxRate = 1
|
|
}
|
|
|
|
label := "in: green out: blue"
|
|
for i, r := range label {
|
|
if i >= width {
|
|
break
|
|
}
|
|
screen.SetContent(x+i, y, r, nil, tcell.StyleDefault.Foreground(tcell.ColorGray))
|
|
}
|
|
|
|
start := 0
|
|
if len(samples) > width {
|
|
start = len(samples) - width
|
|
}
|
|
plotHeight := height - 1
|
|
for col, sample := range samples[start:] {
|
|
drawPoint(screen, x+col, y+1, plotHeight, sample.inBitsPerSecond, maxRate, tcell.ColorGreen)
|
|
drawPoint(screen, x+col, y+1, plotHeight, sample.outBitsPerSecond, maxRate, tcell.ColorDodgerBlue)
|
|
}
|
|
}
|
|
|
|
func (g *RateGraph) copySamples() []rateSample {
|
|
g.mu.Lock()
|
|
defer g.mu.Unlock()
|
|
samples := make([]rateSample, len(g.samples))
|
|
copy(samples, g.samples)
|
|
return samples
|
|
}
|
|
|
|
func maxSampleRate(samples []rateSample) float64 {
|
|
var maxRate float64
|
|
for _, sample := range samples {
|
|
maxRate = math.Max(maxRate, sample.inBitsPerSecond)
|
|
maxRate = math.Max(maxRate, sample.outBitsPerSecond)
|
|
}
|
|
return maxRate
|
|
}
|
|
|
|
func drawPoint(screen tcell.Screen, x int, y int, height int, value float64, maxRate float64, color tcell.Color) {
|
|
if height <= 0 {
|
|
return
|
|
}
|
|
ratio := value / maxRate
|
|
if ratio < 0 {
|
|
ratio = 0
|
|
}
|
|
if ratio > 1 {
|
|
ratio = 1
|
|
}
|
|
row := height - 1 - int(math.Round(ratio*float64(height-1)))
|
|
screen.SetContent(x, y+row, '●', nil, tcell.StyleDefault.Foreground(color))
|
|
}
|