From 843375bc1428d1a0cbe793d53d06e8ebf316a1d4 Mon Sep 17 00:00:00 2001 From: sqshq Date: Thu, 31 Jan 2019 18:40:05 -0500 Subject: [PATCH] added palette --- config.yml | 12 +++++---- config/config.go | 54 ++++++++++++++++++++++++++++++--------- data/consumer.go | 4 +-- data/item.go | 24 ++++++++++++++++++ data/poller.go | 16 +++++------- main.go | 10 ++------ settings/color.go | 35 +++++++++++++++++++++++++ widgets/runchart.go | 62 ++++++++++++++++++++++----------------------- widgets/settings.go | 7 ----- 9 files changed, 149 insertions(+), 75 deletions(-) create mode 100644 data/item.go create mode 100644 settings/color.go delete mode 100644 widgets/settings.go diff --git a/config.yml b/config.yml index 8e82be3..86a3ee3 100644 --- a/config.yml +++ b/config.yml @@ -1,12 +1,14 @@ -theme: dark / bright +theme: dark run-charts: - title: curl-latency data: - - label: example.com - script: curl -o /dev/null -s -w '%{time_total}' http://example.com - label: google.com script: curl -o /dev/null -s -w '%{time_total}' http://google.com - refresh-rate-ms: 200 + - label: yahoo.com + script: curl -o /dev/null -s -w '%{time_total}' http://yahoo.com + - label: example.com + script: curl -o /dev/null -s -w '%{time_total}' http://example.com + refresh-rate-ms: 300 time-scale-sec: 1 style: dots/lines position: @@ -19,7 +21,7 @@ run-charts: data: - label: posts script: mongo --quiet --host=localhost blog --eval "db.getCollection('post').find({}).size()" | grep 2 - color: red + color: 3 refresh-rate-ms: 200 time-scale-sec: 1 position: diff --git a/config/config.go b/config/config.go index 14581e6..ca21b67 100644 --- a/config/config.go +++ b/config/config.go @@ -1,32 +1,39 @@ package config import ( + "github.com/sqshq/vcmd/data" . "github.com/sqshq/vcmd/layout" + "github.com/sqshq/vcmd/settings" "gopkg.in/yaml.v2" "io/ioutil" "log" ) type Config struct { - RunCharts []RunChart `yaml:"run-charts"` -} - -type DataConfig struct { - Script string `yaml:"script"` - Label string `yaml:"label"` + Theme settings.Theme `yaml:"theme"` + RunCharts []RunChart `yaml:"run-charts"` } type RunChart struct { - Title string `yaml:"title"` - DataConfig []DataConfig `yaml:"data"` - Position Position `yaml:"position"` - Size Size `yaml:"size"` - RefreshRateMs int `yaml:"refresh-rate-ms"` - TimeScaleSec int `yaml:"time-scale-sec"` + Title string `yaml:"title"` + Items []data.Item `yaml:"data"` + Position Position `yaml:"position"` + Size Size `yaml:"size"` + RefreshRateMs int `yaml:"refresh-rate-ms"` + TimeScaleSec int `yaml:"time-scale-sec"` } func Load(location string) *Config { + cfg := readFile(location) + validate(cfg) + setColors(cfg) + + return cfg +} + +func readFile(location string) *Config { + yamlFile, err := ioutil.ReadFile(location) if err != nil { log.Fatalf("Can't read config file: %s", location) @@ -41,3 +48,26 @@ func Load(location string) *Config { return cfg } + +/* + TODO validation + - title uniquness and mandatory within a single type of widget + - label uniqueness and mandatory (if > 1 data bullets) +*/ +func validate(config *Config) { + +} + +func setColors(config *Config) { + + palette := settings.GetPalette(config.Theme) + + for i, chart := range config.RunCharts { + for j, item := range chart.Items { + if item.Color == 0 { + item.Color = palette.Colors[i+j] + chart.Items[j] = item + } + } + } +} diff --git a/data/consumer.go b/data/consumer.go index 0bc8722..375335b 100644 --- a/data/consumer.go +++ b/data/consumer.go @@ -1,6 +1,6 @@ package data type Consumer interface { - ConsumeValue(value string, label string) - ConsumeError(err error) + ConsumeValue(item Item, value string) + ConsumeError(item Item, err error) } diff --git a/data/item.go b/data/item.go new file mode 100644 index 0000000..7145274 --- /dev/null +++ b/data/item.go @@ -0,0 +1,24 @@ +package data + +import ( + ui "github.com/sqshq/termui" + "os/exec" + "strings" +) + +type Item struct { + Script string `yaml:"script"` + Label string `yaml:"label"` + Color ui.Color `yaml:"color"` +} + +func (self *Item) nextValue() (value string, err error) { + + output, err := exec.Command("sh", "-c", self.Script).Output() + + if err != nil { + return "", err + } + + return strings.TrimSpace(string(output)), nil +} diff --git a/data/poller.go b/data/poller.go index 4e3e2be..9e21379 100644 --- a/data/poller.go +++ b/data/poller.go @@ -1,22 +1,19 @@ package data import ( - "os/exec" - "strings" "time" ) type Poller struct { consumer Consumer - script string - label string + item Item pause bool } -func NewPoller(consumer Consumer, script string, label string, rateMs int) Poller { +func NewPoller(consumer Consumer, item Item, rateMs int) Poller { ticker := time.NewTicker(time.Duration(rateMs * int(time.Millisecond))) - poller := Poller{consumer, script, label, false} + poller := Poller{consumer, item, false} go func() { for { @@ -40,12 +37,11 @@ func (self *Poller) poll() { return } - output, err := exec.Command("sh", "-c", self.script).Output() + value, err := self.item.nextValue() if err != nil { - self.consumer.ConsumeError(err) + self.consumer.ConsumeError(self.item, err) } - value := strings.TrimSpace(string(output)) - self.consumer.ConsumeValue(value, self.label) + self.consumer.ConsumeValue(self.item, value) } diff --git a/main.go b/main.go index f09dc3d..5d0e908 100644 --- a/main.go +++ b/main.go @@ -10,14 +10,8 @@ import ( "time" ) -/* - TODO validation - - title uniquness and mandatory within a single type of widget - - label uniqueness and mandatory (if > 1 data bullets) -*/ func main() { - // todo error handling + validation cfg := config.Load("/Users/sqshq/Go/src/github.com/sqshq/vcmd/config.yml") if err := ui.Init(); err != nil { @@ -35,9 +29,9 @@ func main() { chart := widgets.NewRunChart(chartConfig.Title) lout.AddItem(chart, chartConfig.Position, chartConfig.Size) - for _, chartData := range chartConfig.DataConfig { + for _, item := range chartConfig.Items { pollers = append(pollers, - data.NewPoller(chart, chartData.Script, chartData.Label, chartConfig.RefreshRateMs)) + data.NewPoller(chart, item, chartConfig.RefreshRateMs)) } } diff --git a/settings/color.go b/settings/color.go new file mode 100644 index 0000000..541b188 --- /dev/null +++ b/settings/color.go @@ -0,0 +1,35 @@ +package settings + +import ( + "fmt" + ui "github.com/sqshq/termui" +) + +type Theme string + +const ( + ThemeDark Theme = "dark" + ThemeLight Theme = "light" +) + +const ( + ColorOlive ui.Color = 178 + ColorDeepSkyBlue ui.Color = 39 + ColorDeepPink ui.Color = 162 + ColorDarkGrey ui.Color = 240 +) + +type Palette struct { + Colors []ui.Color +} + +func GetPalette(theme Theme) Palette { + switch theme { + case ThemeDark: + return Palette{Colors: []ui.Color{ColorOlive, ColorDeepSkyBlue, ColorDeepPink}} + case ThemeLight: + return Palette{Colors: []ui.Color{ColorOlive, ColorDeepSkyBlue, ColorDeepPink}} + default: + panic(fmt.Sprintf("Following theme is not supported: %v", theme)) + } +} diff --git a/widgets/runchart.go b/widgets/runchart.go index 2cef15d..d31d829 100644 --- a/widgets/runchart.go +++ b/widgets/runchart.go @@ -2,6 +2,8 @@ package widgets import ( "fmt" + "github.com/sqshq/vcmd/data" + "github.com/sqshq/vcmd/settings" "image" "log" "math" @@ -22,7 +24,7 @@ const ( type RunChart struct { Block - items []ChartItem + lines []TimeLine grid ChartGrid mutex *sync.Mutex } @@ -32,10 +34,9 @@ type TimePoint struct { Time time.Time } -type ChartItem struct { - timePoints []TimePoint - label string - color Color +type TimeLine struct { + points []TimePoint + item data.Item } type ChartGrid struct { @@ -62,7 +63,7 @@ func NewRunChart(title string) *RunChart { block.Title = title return &RunChart{ Block: block, - items: []ChartItem{}, + lines: []TimeLine{}, mutex: &sync.Mutex{}, } } @@ -78,7 +79,7 @@ func (self *RunChart) newChartGrid() ChartGrid { paddingWidth: xAxisLabelsGap + xAxisLabelsWidth, maxTimeWidth: self.Inner.Max.X - xAxisLabelsWidth/2 - xAxisLabelsGap, timeExtremum: GetTimeExtremum(linesCount, paddingDuration), - valueExtremum: GetValueExtremum(self.items), + valueExtremum: GetValueExtremum(self.lines), } } @@ -98,7 +99,7 @@ func (self *RunChart) Draw(buf *Buffer) { self.mutex.Unlock() } -func (self *RunChart) ConsumeValue(value string, label string) { +func (self *RunChart) ConsumeValue(item data.Item, value string) { float, err := strconv.ParseFloat(value, 64) if err != nil { @@ -109,44 +110,43 @@ func (self *RunChart) ConsumeValue(value string, label string) { self.mutex.Lock() itemExists := false - for i, item := range self.items { - if item.label == label { - item.timePoints = append(item.timePoints, timePoint) - self.items[i] = item + for i, line := range self.lines { + if line.item.Label == item.Label { + line.points = append(line.points, timePoint) + self.lines[i] = line itemExists = true } } if !itemExists { - item := &ChartItem{ - timePoints: []TimePoint{timePoint}, - label: label, - color: ColorYellow, + item := &TimeLine{ + points: []TimePoint{timePoint}, + item: item, } - self.items = append(self.items, *item) + self.lines = append(self.lines, *item) } self.trimOutOfRangeValues() self.mutex.Unlock() } -func (self *RunChart) ConsumeError(err error) { +func (self *RunChart) ConsumeError(item data.Item, err error) { // TODO visual notification } func (self *RunChart) trimOutOfRangeValues() { - for i, item := range self.items { + for i, item := range self.lines { lastOutOfRangeValueIndex := -1 - for j, timePoint := range item.timePoints { + for j, timePoint := range item.points { if !self.isTimePointInRange(timePoint) { lastOutOfRangeValueIndex = j } } if lastOutOfRangeValueIndex > 0 { - item.timePoints = append(item.timePoints[:0], item.timePoints[lastOutOfRangeValueIndex+1:]...) - self.items[i] = item + item.points = append(item.points[:0], item.points[lastOutOfRangeValueIndex+1:]...) + self.lines[i] = item } } } @@ -156,23 +156,23 @@ func (self *RunChart) renderItems(buf *Buffer, drawArea image.Rectangle) { canvas := NewCanvas() canvas.Rectangle = drawArea - for _, item := range self.items { + for _, line := range self.lines { xToPoint := make(map[int]image.Point) pointsOrder := make([]int, 0) - for _, timePoint := range item.timePoints { + for _, point := range line.points { - if !self.isTimePointInRange(timePoint) { + if !self.isTimePointInRange(point) { continue } - timeDeltaWithGridMaxTime := self.grid.timeExtremum.max.Sub(timePoint.Time) + timeDeltaWithGridMaxTime := self.grid.timeExtremum.max.Sub(point.Time) deltaToPaddingRelation := float64(timeDeltaWithGridMaxTime.Nanoseconds()) / float64(self.grid.paddingDuration.Nanoseconds()) x := self.grid.maxTimeWidth - (int(float64(self.grid.paddingWidth) * deltaToPaddingRelation)) valuePerYDot := (self.grid.valueExtremum.max - self.grid.valueExtremum.min) / float64(drawArea.Dy()-1) - y := int(float64(timePoint.Value-self.grid.valueExtremum.min) / valuePerYDot) + y := int(float64(point.Value-self.grid.valueExtremum.min) / valuePerYDot) if _, exists := xToPoint[x]; exists { continue @@ -201,7 +201,7 @@ func (self *RunChart) renderItems(buf *Buffer, drawArea image.Rectangle) { canvas.Line( braillePoint(previousPoint), braillePoint(currentPoint), - item.color, + line.item.Color, ) } } @@ -232,7 +232,7 @@ func (self *RunChart) plotAxes(buf *Buffer) { 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(ColorDarkGrey)), + NewCell(VERTICAL_DASH, NewStyle(settings.ColorDarkGrey)), image.Pt(self.grid.maxTimeWidth-x*self.grid.paddingWidth, y+self.Inner.Min.Y+1), ) } @@ -267,7 +267,7 @@ func (self *RunChart) plotAxes(buf *Buffer) { } } -func GetValueExtremum(items []ChartItem) ValueExtremum { +func GetValueExtremum(items []TimeLine) ValueExtremum { if len(items) == 0 { return ValueExtremum{0, 0} @@ -276,7 +276,7 @@ func GetValueExtremum(items []ChartItem) ValueExtremum { var max, min = -math.MaxFloat64, math.MaxFloat64 for _, item := range items { - for _, point := range item.timePoints { + for _, point := range item.points { if point.Value > max { max = point.Value } diff --git a/widgets/settings.go b/widgets/settings.go deleted file mode 100644 index f447823..0000000 --- a/widgets/settings.go +++ /dev/null @@ -1,7 +0,0 @@ -package widgets - -import "github.com/sqshq/termui" - -const ( - ColorDarkGrey termui.Color = 240 -)