minor refactoring

This commit is contained in:
sqshq 2019-02-01 00:07:25 -05:00
parent ec79619f38
commit 41b1fa59a5
8 changed files with 164 additions and 96 deletions

View File

@ -1,22 +1,26 @@
theme: dark theme: dark
run-charts: run-charts:
- title: curl-latency - title: CURL-LATENCY
data: data:
- label: google.com - label: GOOGLE
script: curl -o /dev/null -s -w '%{time_total}' http://google.com script: curl -o /dev/null -s -w '%{time_total}' http://google.com
- label: yahoo.com - label: YAHOO
script: curl -o /dev/null -s -w '%{time_total}' http://yahoo.com script: curl -o /dev/null -s -w '%{time_total}' http://yahoo.com
- label: example.com - label: YANDEX
script: curl -o /dev/null -s -w '%{time_total}' http://example.com script: curl -o /dev/null -s -w '%{time_total}' http://yandex.com
refresh-rate-ms: 300 refresh-rate-ms: 300
time-scale-sec: 1 time-scale-sec: 1
decimal-places: 1
legend:
labels: true
details: true
position: position:
x: 0 x: 0
y: 0 y: 0
size: size:
x: 15 x: 15
y: 15 y: 15
- title: mongo-count - title: MONGO-COUNT
data: data:
- label: posts - label: posts
script: mongo --quiet --host=localhost blog --eval "db.getCollection('post').find({}).size()" | grep 2 script: mongo --quiet --host=localhost blog --eval "db.getCollection('post').find({}).size()" | grep 2

View File

@ -2,8 +2,8 @@ package config
import ( import (
"github.com/sqshq/vcmd/data" "github.com/sqshq/vcmd/data"
. "github.com/sqshq/vcmd/layout"
"github.com/sqshq/vcmd/settings" "github.com/sqshq/vcmd/settings"
. "github.com/sqshq/vcmd/widgets"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
"io/ioutil" "io/ioutil"
"log" "log"
@ -11,10 +11,10 @@ import (
type Config struct { type Config struct {
Theme settings.Theme `yaml:"theme"` Theme settings.Theme `yaml:"theme"`
RunCharts []RunChart `yaml:"run-charts"` RunCharts []RunChartConfig `yaml:"run-charts"`
} }
type RunChart struct { type RunChartConfig struct {
Title string `yaml:"title"` Title string `yaml:"title"`
Items []data.Item `yaml:"data"` Items []data.Item `yaml:"data"`
Position Position `yaml:"position"` Position Position `yaml:"position"`

View File

@ -7,13 +7,12 @@ import (
type Poller struct { type Poller struct {
consumer Consumer consumer Consumer
item Item item Item
pause bool
} }
func NewPoller(consumer Consumer, item Item, rateMs int) Poller { func NewPoller(consumer Consumer, item Item, rateMs int) Poller {
ticker := time.NewTicker(time.Duration(rateMs * int(time.Millisecond))) ticker := time.NewTicker(time.Duration(rateMs * int(time.Millisecond)))
poller := Poller{consumer, item, false} poller := Poller{consumer, item}
go func() { go func() {
for { for {
@ -27,16 +26,8 @@ func NewPoller(consumer Consumer, item Item, rateMs int) Poller {
return poller return poller
} }
func (self *Poller) TogglePause() {
self.pause = !self.pause
}
func (self *Poller) poll() { func (self *Poller) poll() {
if self.pause {
return
}
value, err := self.item.nextValue() value, err := self.item.nextValue()
if err != nil { if err != nil {

29
main.go
View File

@ -4,7 +4,7 @@ import (
ui "github.com/sqshq/termui" ui "github.com/sqshq/termui"
"github.com/sqshq/vcmd/config" "github.com/sqshq/vcmd/config"
"github.com/sqshq/vcmd/data" "github.com/sqshq/vcmd/data"
"github.com/sqshq/vcmd/layout" "github.com/sqshq/vcmd/settings"
"github.com/sqshq/vcmd/widgets" "github.com/sqshq/vcmd/widgets"
"log" "log"
"time" "time"
@ -12,6 +12,8 @@ import (
func main() { func main() {
print("\033]0;vcmd\007")
cfg := config.Load("/Users/sqshq/Go/src/github.com/sqshq/vcmd/config.yml") cfg := config.Load("/Users/sqshq/Go/src/github.com/sqshq/vcmd/config.yml")
if err := ui.Init(); err != nil { if err := ui.Init(); err != nil {
@ -22,7 +24,7 @@ func main() {
events := ui.PollEvents() events := ui.PollEvents()
pollers := make([]data.Poller, 0) pollers := make([]data.Poller, 0)
lout := layout.NewLayout(ui.TerminalDimensions()) lout := widgets.NewLayout(ui.TerminalDimensions())
for _, chartConfig := range cfg.RunCharts { for _, chartConfig := range cfg.RunCharts {
@ -36,17 +38,18 @@ func main() {
} }
ticker := time.NewTicker(30 * time.Millisecond) ticker := time.NewTicker(30 * time.Millisecond)
pause := false
for { for {
select { select {
case e := <-events: case e := <-events:
switch e.ID { switch e.ID {
case "q", "<C-c>": case settings.EventQuit, settings.EventExit:
return return
case "<Resize>": case settings.EventResize:
payload := e.Payload.(ui.Resize) payload := e.Payload.(ui.Resize)
lout.ChangeDimensions(payload.Width, payload.Height) lout.ChangeDimensions(payload.Width, payload.Height)
case "<MouseLeft>": case settings.EventMouseClick:
//payload := e.Payload.(ui.Mouse) //payload := e.Payload.(ui.Mouse)
//x, y := payload.X, payload.Y //x, y := payload.X, payload.Y
//log.Printf("x: %v, y: %v", x, y) //log.Printf("x: %v, y: %v", x, y)
@ -54,23 +57,23 @@ func main() {
switch e.Type { switch e.Type {
case ui.KeyboardEvent: case ui.KeyboardEvent:
switch e.ID { switch e.ID {
case "<Left>": case settings.EventKeyboardLeft:
// here we are going to move selection (special type of layout item) // here we are going to move selection (special type of layout item)
//lout.GetItem("").Move(-1, 0) //lout.GetItem("").Move(-1, 0)
case "<Right>": case settings.EventKeyboardRight:
//lout.GetItem(0).Move(1, 0) //lout.GetItem(0).Move(1, 0)
case "<Down>": case settings.EventKeyboardDown:
//lout.GetItem(0).Move(0, 1) //lout.GetItem(0).Move(0, 1)
case "<Up>": case settings.EventKeyboardUp:
//lout.GetItem(0).Move(0, -1) //lout.GetItem(0).Move(0, -1)
case "p": case settings.EventPause:
for _, poller := range pollers { pause = !pause
poller.TogglePause()
}
} }
} }
case <-ticker.C: case <-ticker.C:
if !pause {
ui.Render(lout) ui.Render(lout)
} }
} }
}
} }

15
settings/event.go Normal file
View File

@ -0,0 +1,15 @@
package settings
type Event string
const (
EventPause = "p"
EventQuit = "q"
EventResize = "<Resize>"
EventExit = "<C-c>"
EventMouseClick = "<MouseLeft>"
EventKeyboardLeft = "<Left>"
EventKeyboardRight = "<Right>"
EventKeyboardUp = "<Up>"
EventKeyboardDown = "<Down>"
)

View File

@ -1,4 +1,4 @@
package layout package widgets
import ( import (
. "github.com/sqshq/termui" . "github.com/sqshq/termui"

View File

@ -1,4 +1,4 @@
package layout package widgets
import ( import (
. "github.com/sqshq/termui" . "github.com/sqshq/termui"

View File

@ -20,6 +20,7 @@ const (
xAxisLabelsGap = 2 xAxisLabelsGap = 2
yAxisLabelsWidth = 5 yAxisLabelsWidth = 5
yAxisLabelsGap = 1 yAxisLabelsGap = 1
xAxisLegendWidth = 15
) )
type RunChart struct { type RunChart struct {
@ -79,7 +80,7 @@ func (self *RunChart) newChartGrid() ChartGrid {
paddingWidth: xAxisLabelsGap + xAxisLabelsWidth, paddingWidth: xAxisLabelsGap + xAxisLabelsWidth,
maxTimeWidth: self.Inner.Max.X - xAxisLabelsWidth/2 - xAxisLabelsGap, maxTimeWidth: self.Inner.Max.X - xAxisLabelsWidth/2 - xAxisLabelsGap,
timeExtremum: GetTimeExtremum(linesCount, paddingDuration), timeExtremum: GetTimeExtremum(linesCount, paddingDuration),
valueExtremum: GetValueExtremum(self.lines), valueExtremum: GetChartValueExtremum(self.lines),
} }
} }
@ -88,7 +89,7 @@ func (self *RunChart) Draw(buf *Buffer) {
self.mutex.Lock() self.mutex.Lock()
self.Block.Draw(buf) self.Block.Draw(buf)
self.grid = self.newChartGrid() self.grid = self.newChartGrid()
self.plotAxes(buf) self.renderAxes(buf)
drawArea := image.Rect( drawArea := image.Rect(
self.Inner.Min.X+yAxisLabelsWidth+1, self.Inner.Min.Y, self.Inner.Min.X+yAxisLabelsWidth+1, self.Inner.Min.Y,
@ -96,6 +97,7 @@ func (self *RunChart) Draw(buf *Buffer) {
) )
self.renderItems(buf, drawArea) self.renderItems(buf, drawArea)
self.renderLegend(buf, drawArea)
self.mutex.Unlock() self.mutex.Unlock()
} }
@ -151,7 +153,61 @@ func (self *RunChart) trimOutOfRangeValues() {
} }
} }
func (self *RunChart) renderItems(buf *Buffer, drawArea image.Rectangle) { func (self *RunChart) renderAxes(buffer *Buffer) {
// draw origin cell
buffer.SetCell(
NewCell(BOTTOM_LEFT, NewStyle(ColorWhite)),
image.Pt(self.Inner.Min.X+yAxisLabelsWidth, self.Inner.Max.Y-xAxisLabelsHeight-1),
)
// draw x axis line
for i := yAxisLabelsWidth + 1; i < self.Inner.Dx(); i++ {
buffer.SetCell(
NewCell(HORIZONTAL_DASH, NewStyle(ColorWhite)),
image.Pt(i+self.Inner.Min.X, self.Inner.Max.Y-xAxisLabelsHeight-1),
)
}
// draw grid
for y := 0; y < self.Inner.Dy()-xAxisLabelsHeight-1; y = y + 2 {
for x := 0; x < self.grid.linesCount; x++ {
buffer.SetCell(
NewCell(VERTICAL_DASH, NewStyle(settings.ColorDarkGrey)),
image.Pt(self.grid.maxTimeWidth-x*self.grid.paddingWidth, y+self.Inner.Min.Y+1),
)
}
}
// draw y axis line
for i := 0; i < self.Inner.Dy()-xAxisLabelsHeight-1; i++ {
buffer.SetCell(
NewCell(VERTICAL_DASH, NewStyle(ColorWhite)),
image.Pt(self.Inner.Min.X+yAxisLabelsWidth, i+self.Inner.Min.Y),
)
}
// draw x axis time labels
for i := 0; i < self.grid.linesCount; i++ {
labelTime := self.grid.timeExtremum.max.Add(time.Duration(-i) * time.Second)
buffer.SetString(
labelTime.Format("15:04:05"),
NewStyle(ColorWhite),
image.Pt(self.grid.maxTimeWidth-xAxisLabelsWidth/2-i*(self.grid.paddingWidth), self.Inner.Max.Y-1),
)
}
// draw y axis labels
verticalScale := self.grid.valueExtremum.max - self.grid.valueExtremum.min/float64(self.Inner.Dy()-xAxisLabelsHeight-1)
for i := 1; i*(yAxisLabelsGap+1) <= self.Inner.Dy()-1; i++ {
buffer.SetString(
fmt.Sprintf("%.3f", float64(i)*self.grid.valueExtremum.min*verticalScale*(yAxisLabelsGap+1)),
NewStyle(ColorWhite),
image.Pt(self.Inner.Min.X, self.Inner.Max.Y-(i*(yAxisLabelsGap+1))-2),
)
}
}
func (self *RunChart) renderItems(buffer *Buffer, drawArea image.Rectangle) {
canvas := NewCanvas() canvas := NewCanvas()
canvas.Rectangle = drawArea canvas.Rectangle = drawArea
@ -193,7 +249,7 @@ func (self *RunChart) renderItems(buf *Buffer, drawArea image.Rectangle) {
previousPoint = xToPoint[pointsOrder[i-1]] previousPoint = xToPoint[pointsOrder[i-1]]
} }
//buf.SetCell( //buffer.SetCell(
// NewCell(self.DotRune, NewStyle(SelectColor(self.LineColors, 0))), // NewCell(self.DotRune, NewStyle(SelectColor(self.LineColors, 0))),
// currentPoint, // currentPoint,
//) //)
@ -206,68 +262,47 @@ func (self *RunChart) renderItems(buf *Buffer, drawArea image.Rectangle) {
} }
} }
canvas.Draw(buf) canvas.Draw(buffer)
}
func (self *RunChart) renderLegend(buffer *Buffer, rectangle image.Rectangle) {
for i, line := range self.lines {
extremum := GetLineValueExtremum(line.points)
buffer.SetString(
fmt.Sprintf("•"),
NewStyle(line.item.Color),
image.Pt(self.Inner.Max.X-xAxisLegendWidth-2, self.Inner.Min.Y+1+i*5),
)
buffer.SetString(
fmt.Sprintf("%s", line.item.Label),
NewStyle(line.item.Color),
image.Pt(self.Inner.Max.X-xAxisLegendWidth, self.Inner.Min.Y+1+i*5),
)
buffer.SetString(
fmt.Sprintf("max %.3f", extremum.max),
NewStyle(ColorWhite),
image.Pt(self.Inner.Max.X-xAxisLegendWidth, self.Inner.Min.Y+2+i*5),
)
buffer.SetString(
fmt.Sprintf("min %.3f", extremum.min),
NewStyle(ColorWhite),
image.Pt(self.Inner.Max.X-xAxisLegendWidth, self.Inner.Min.Y+3+i*5),
)
buffer.SetString(
fmt.Sprintf("cur %.3f", line.points[len(line.points)-1].Value),
NewStyle(ColorWhite),
image.Pt(self.Inner.Max.X-xAxisLegendWidth, self.Inner.Min.Y+4+i*5),
)
}
} }
func (self *RunChart) isTimePointInRange(point TimePoint) bool { func (self *RunChart) isTimePointInRange(point TimePoint) bool {
return point.Time.After(self.grid.timeExtremum.min.Add(self.grid.paddingDuration)) return point.Time.After(self.grid.timeExtremum.min.Add(self.grid.paddingDuration))
} }
func (self *RunChart) plotAxes(buf *Buffer) { func GetChartValueExtremum(items []TimeLine) ValueExtremum {
// draw origin cell
buf.SetCell(
NewCell(BOTTOM_LEFT, NewStyle(ColorWhite)),
image.Pt(self.Inner.Min.X+yAxisLabelsWidth, self.Inner.Max.Y-xAxisLabelsHeight-1),
)
// draw x axis line
for i := yAxisLabelsWidth + 1; i < self.Inner.Dx(); i++ {
buf.SetCell(
NewCell(HORIZONTAL_DASH, NewStyle(ColorWhite)),
image.Pt(i+self.Inner.Min.X, self.Inner.Max.Y-xAxisLabelsHeight-1),
)
}
// draw grid
for y := 0; y < self.Inner.Dy()-xAxisLabelsHeight-1; y = y + 2 {
for x := 0; x < self.grid.linesCount; x++ {
buf.SetCell(
NewCell(VERTICAL_DASH, NewStyle(settings.ColorDarkGrey)),
image.Pt(self.grid.maxTimeWidth-x*self.grid.paddingWidth, y+self.Inner.Min.Y+1),
)
}
}
// draw y axis line
for i := 0; i < self.Inner.Dy()-xAxisLabelsHeight-1; i++ {
buf.SetCell(
NewCell(VERTICAL_DASH, NewStyle(ColorWhite)),
image.Pt(self.Inner.Min.X+yAxisLabelsWidth, i+self.Inner.Min.Y),
)
}
// draw x axis time labels
for i := 0; i < self.grid.linesCount; i++ {
labelTime := self.grid.timeExtremum.max.Add(time.Duration(-i) * time.Second)
buf.SetString(
labelTime.Format("15:04:05"),
NewStyle(ColorWhite),
image.Pt(self.grid.maxTimeWidth-xAxisLabelsWidth/2-i*(self.grid.paddingWidth), self.Inner.Max.Y-1),
)
}
// draw y axis labels
verticalScale := self.grid.valueExtremum.max - self.grid.valueExtremum.min/float64(self.Inner.Dy()-xAxisLabelsHeight-1)
for i := 1; i*(yAxisLabelsGap+1) <= self.Inner.Dy()-1; i++ {
buf.SetString(
fmt.Sprintf("%.3f", float64(i)*self.grid.valueExtremum.min*verticalScale*(yAxisLabelsGap+1)),
NewStyle(ColorWhite),
image.Pt(self.Inner.Min.X, self.Inner.Max.Y-(i*(yAxisLabelsGap+1))-2),
)
}
}
func GetValueExtremum(items []TimeLine) ValueExtremum {
if len(items) == 0 { if len(items) == 0 {
return ValueExtremum{0, 0} return ValueExtremum{0, 0}
@ -289,6 +324,26 @@ func GetValueExtremum(items []TimeLine) ValueExtremum {
return ValueExtremum{max: max, min: min} return ValueExtremum{max: max, min: min}
} }
func GetLineValueExtremum(points []TimePoint) ValueExtremum {
if len(points) == 0 {
return ValueExtremum{0, 0}
}
var max, min = -math.MaxFloat64, math.MaxFloat64
for _, point := range points {
if point.Value > max {
max = point.Value
}
if point.Value < min {
min = point.Value
}
}
return ValueExtremum{max: max, min: min}
}
func GetTimeExtremum(linesCount int, paddingDuration time.Duration) TimeExtremum { func GetTimeExtremum(linesCount int, paddingDuration time.Duration) TimeExtremum {
maxTime := time.Now() maxTime := time.Now()
return TimeExtremum{ return TimeExtremum{