diff --git a/config.yml b/config.yml index e979c0f..b90969a 100644 --- a/config.yml +++ b/config.yml @@ -1,79 +1,86 @@ runcharts: - - title: SEARCH ENGINE RESPONSE TIME (sec) - position: - w: 0 - h: 0 - size: - w: 50 - h: 14 - scale: 3 - items: - - label: GOOGLE - script: curl -o /dev/null -s -w '%{time_total}' https://www.google.com - - label: YAHOO - script: curl -o /dev/null -s -w '%{time_total}' https://search.yahoo.com - - label: BING - script: curl -o /dev/null -s -w '%{time_total}' https://www.bing.com - - title: SEARCH ENGINE RESPONSE TIME 2 (sec) - refresh-rate-ms: 5000 - position: - w: 0 - h: 14 - size: - w: 17 - h: 10 - legend: - enabled: true - details: false - items: - - label: GOOGLE - script: curl -o /dev/null -s -w '%{time_total}' https://www.google.com - - label: YAHOO - script: curl -o /dev/null -s -w '%{time_total}' https://search.yahoo.com - - label: BING - script: curl -o /dev/null -s -w '%{time_total}' https://www.bing.com - - title: MONGO COLLECTIONS COUNT - position: - w: 17 - h: 14 - size: - w: 17 - h: 10 - scale: 0 - items: - - label: ACTIVE - script: mongo --quiet --host=localhost blog --eval "db.getCollection('posts').find({status:'ACTIVE'}).itcount()" - - label: INACTIVE - script: mongo --quiet --host=localhost blog --eval "db.getCollection('posts').find({status:'INACTIVE'}).itcount()" +- title: SEARCH ENGINE RESPONSE TIME (sec) + position: + w: 0 + h: 0 + size: + w: 37 + h: 14 + scale: 3 + items: + - label: GOOGLE + script: curl -o /dev/null -s -w '%{time_total}' https://www.google.com + - label: YAHOO + script: curl -o /dev/null -s -w '%{time_total}' https://search.yahoo.com + - label: BING + script: curl -o /dev/null -s -w '%{time_total}' https://www.bing.com +- title: SEARCH ENGINE RESPONSE TIME 2 (sec) + refresh-rate-ms: 5000 + position: + w: 37 + h: 0 + size: + w: 13 + h: 14 + legend: + enabled: true + details: false + items: + - label: GOOGLE + script: curl -o /dev/null -s -w '%{time_total}' https://www.google.com + - label: YAHOO + script: curl -o /dev/null -s -w '%{time_total}' https://search.yahoo.com + - label: BING + script: curl -o /dev/null -s -w '%{time_total}' https://www.bing.com +- title: MONGO COLLECTIONS COUNT + position: + w: 17 + h: 14 + size: + w: 17 + h: 10 + scale: 0 + items: + - label: ACTIVE + script: mongo --quiet --host=localhost blog --eval "db.getCollection('posts').find({status:'ACTIVE'}).itcount()" + - label: INACTIVE + script: mongo --quiet --host=localhost blog --eval "db.getCollection('posts').find({status:'INACTIVE'}).itcount()" barcharts: - - title: ANCHOR CONTEXT EVENTS - refresh-rate-ms: 300 - position: - w: 30 - h: 10 - size: - w: 16 - h: 4 - items: - - label: ACTIVE - script: mongo --quiet --host=localhost blog --eval "db.getCollection('posts').find({status:'ACTIVE'}).itcount()" - - label: INACTIVE - script: mongo --quiet --host=localhost blog --eval "db.getCollection('posts').find({status:'INACTIVE'}).itcount()" +- title: DB EVENTS + refresh-rate-ms: 1000 + position: + w: 0 + h: 14 + size: + w: 17 + h: 10 + scale: 0 + items: + - label: ACTIVE + script: mongo --quiet --host=localhost blog --eval "db.getCollection('posts').find({status:'ACTIVE'}).itcount()" + - label: INACTIVE + script: mongo --quiet --host=localhost blog --eval "db.getCollection('posts').find({status:'INACTIVE'}).itcount()" + - label: HUACTIVE + script: echo 0 + - label: ACTIVE2 + script: mongo --quiet --host=localhost blog --eval "db.getCollection('posts').find({status:'ACTIVE'}).itcount()" + - label: INACTIVE2 + script: mongo --quiet --host=localhost blog --eval "db.getCollection('posts').find({status:'INACTIVE'}).itcount()" asciiboxes: - - title: COUNT - position: - w: 34 - h: 14 - size: - w: 16 - h: 4 - script: date +%r - - title: MODE - position: - w: 34 - h: 18 - size: - w: 16 - h: 6 - script: date +%r - font: 3d +- title: COUNT + position: + w: 34 + h: 14 + size: + w: 16 + h: 4 + script: date +%r +- title: MODE + position: + w: 34 + h: 18 + size: + w: 16 + h: 6 + script: date +%r + font: 3d diff --git a/config/config.go b/config/config.go index dbf878e..c360008 100644 --- a/config/config.go +++ b/config/config.go @@ -108,6 +108,12 @@ func (c *Config) findComponent(componentType ComponentType, componentTitle strin return &c.RunCharts[i].ComponentConfig } } + case TypeBarChart: + for i, component := range c.BarCharts { + if component.Title == componentTitle { + return &c.BarCharts[i].ComponentConfig + } + } case TypeAsciiBox: for i, component := range c.AsciiBoxes { if component.Title == componentTitle { diff --git a/widgets/barchart/barchart.go b/widgets/barchart/barchart.go index 223b8e1..ac287f3 100644 --- a/widgets/barchart/barchart.go +++ b/widgets/barchart/barchart.go @@ -21,12 +21,14 @@ type BarChart struct { bars []Bar scale int maxValue float64 + count int64 } type Bar struct { label string color ui.Color value float64 + delta float64 } func NewBarChart(title string, scale int) *BarChart { @@ -46,8 +48,9 @@ func (b *BarChart) AddBar(label string, color ui.Color) { func (b *BarChart) ConsumeSample(sample data.Sample) { - float, err := strconv.ParseFloat(sample.Value, 64) + b.count++ + float, err := strconv.ParseFloat(sample.Value, 64) if err != nil { // TODO visual notification + check sample.Error } @@ -59,28 +62,50 @@ func (b *BarChart) ConsumeSample(sample data.Sample) { } } + bar := b.bars[index] + bar.delta = float - bar.value + bar.value = float + b.bars[index] = bar + if float > b.maxValue { b.maxValue = float } - bar := b.bars[index] - bar.value = float - b.bars[index] = bar + // normalize bars height once in a while + if b.count%500 == 0 { + b.reselectMaxValue() + } +} + +func (b *BarChart) reselectMaxValue() { + maxValue := -math.MaxFloat64 + for _, bar := range b.bars { + if bar.value > maxValue { + maxValue = bar.value + } + } + b.maxValue = maxValue } func (b *BarChart) Draw(buf *ui.Buffer) { b.Block.Draw(buf) - barWidth := b.Inner.Dx() / len(b.bars) - barXCoordinate := b.Inner.Min.X + barWidth := (b.Inner.Dx() - 2*barIndent - len(b.bars)) / len(b.bars) + barXCoordinate := b.Inner.Min.X + barIndent labelStyle := ui.NewStyle(console.ColorWhite) for _, bar := range b.bars { + // draw bar height := int((bar.value / b.maxValue) * float64(b.Inner.Dy()-1)) - for x := barXCoordinate; x < ui.MinInt(barXCoordinate+barWidth, b.Inner.Max.X); x++ { - for y := b.Inner.Max.Y - 2; y > (b.Inner.Max.Y-2)-height; y-- { + if height <= 1 { + height = 2 + } + + maxYCoordinate := b.Inner.Max.Y - height + for x := barXCoordinate; x < ui.MinInt(barXCoordinate+barWidth, b.Inner.Max.X-barIndent); x++ { + for y := b.Inner.Max.Y - 2; y >= maxYCoordinate; y-- { c := ui.NewCell(barSymbol, ui.NewStyle(bar.color)) buf.SetCell(c, image.Pt(x, y)) } @@ -93,18 +118,20 @@ func (b *BarChart) Draw(buf *ui.Buffer) { buf.SetString( bar.label, labelStyle, - image.Pt(labelXCoordinate, b.Inner.Max.Y-1), - ) + image.Pt(labelXCoordinate, b.Inner.Max.Y-1)) - // draw value - numberXCoordinate := barXCoordinate + int(float64(barWidth)/2) - if numberXCoordinate <= b.Inner.Max.X { - buf.SetString( - formatValue(bar.value, b.scale), - labelStyle, - image.Pt(numberXCoordinate, b.Inner.Max.Y-2), - ) + // draw value & delta + value := formatValue(bar.value, b.scale) + if bar.delta != 0 { + value = fmt.Sprintf("%s / %s", value, formatValueWithSign(bar.delta, b.scale)) } + valueXCoordinate := barXCoordinate + + int(float64(barWidth)/2) - + int(float64(rw.StringWidth(value))/2) + buf.SetString( + value, + labelStyle, + image.Pt(valueXCoordinate, maxYCoordinate-1)) barXCoordinate += barWidth + barIndent } @@ -119,3 +146,14 @@ func formatValue(value float64, scale int) string { return fmt.Sprintf(format, value) } } + +// TODO extract to utils +func formatValueWithSign(value float64, scale int) string { + if value == 0 { + return " 0" + } else if value > 0 { + return "+" + formatValue(value, scale) + } else { + return formatValue(value, scale) + } +}