renaming and refactoring
This commit is contained in:
parent
dd146c72f0
commit
b71d5556c2
|
@ -1,5 +1,5 @@
|
||||||
theme: dark / bright
|
theme: dark / bright
|
||||||
line-charts:
|
run-charts:
|
||||||
- title: curl-latency
|
- title: curl-latency
|
||||||
data:
|
data:
|
||||||
- label: example.com
|
- label: example.com
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
LineChartConfigs []LineChartConfig `yaml:"line-charts"`
|
RunCharts []RunChart `yaml:"run-charts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DataConfig struct {
|
type DataConfig struct {
|
||||||
|
@ -16,7 +16,7 @@ type DataConfig struct {
|
||||||
Label string `yaml:"label"`
|
Label string `yaml:"label"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LineChartConfig struct {
|
type RunChart struct {
|
||||||
Title string `yaml:"title"`
|
Title string `yaml:"title"`
|
||||||
DataConfig []DataConfig `yaml:"data"`
|
DataConfig []DataConfig `yaml:"data"`
|
||||||
Position Position `yaml:"position"`
|
Position Position `yaml:"position"`
|
||||||
|
|
|
@ -4,8 +4,8 @@ import (
|
||||||
. "github.com/sqshq/termui"
|
. "github.com/sqshq/termui"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Item struct {
|
type Component struct {
|
||||||
Data Drawable
|
Drawable Drawable
|
||||||
Position Position
|
Position Position
|
||||||
Size Size
|
Size Size
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,12 @@ type Size struct {
|
||||||
Y int `yaml:"y"`
|
Y int `yaml:"y"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Item) MoveItem(x, y int) {
|
func (self *Component) Move(x, y int) {
|
||||||
self.Position.X += x
|
self.Position.X += x
|
||||||
self.Position.Y += y
|
self.Position.Y += y
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Item) ResizeItem(x, y int) {
|
func (self *Component) Resize(x, y int) {
|
||||||
self.Size.X += x
|
self.Size.X += x
|
||||||
self.Size.Y += y
|
self.Size.Y += y
|
||||||
}
|
}
|
|
@ -6,7 +6,7 @@ import (
|
||||||
|
|
||||||
type Layout struct {
|
type Layout struct {
|
||||||
Block
|
Block
|
||||||
items []Item
|
components []Component
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -20,13 +20,13 @@ func NewLayout(width, height int) *Layout {
|
||||||
block.SetRect(0, 0, width, height)
|
block.SetRect(0, 0, width, height)
|
||||||
|
|
||||||
return &Layout{
|
return &Layout{
|
||||||
Block: block,
|
Block: block,
|
||||||
items: make([]Item, 0),
|
components: make([]Component, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Layout) AddItem(drawable Drawable, position Position, size Size) {
|
func (self *Layout) AddItem(drawable Drawable, position Position, size Size) {
|
||||||
self.items = append(self.items, Item{drawable, position, size})
|
self.components = append(self.components, Component{drawable, position, size})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Layout) ChangeDimensions(width, height int) {
|
func (self *Layout) ChangeDimensions(width, height int) {
|
||||||
|
@ -38,14 +38,14 @@ func (self *Layout) Draw(buf *Buffer) {
|
||||||
columnWidth := float64(self.GetRect().Dx()) / columnsCount
|
columnWidth := float64(self.GetRect().Dx()) / columnsCount
|
||||||
rowHeight := float64(self.GetRect().Dy()) / rowsCount
|
rowHeight := float64(self.GetRect().Dy()) / rowsCount
|
||||||
|
|
||||||
for _, item := range self.items {
|
for _, component := range self.components {
|
||||||
|
|
||||||
x1 := float64(item.Position.X) * columnWidth
|
x1 := float64(component.Position.X) * columnWidth
|
||||||
y1 := float64(item.Position.Y) * rowHeight
|
y1 := float64(component.Position.Y) * rowHeight
|
||||||
x2 := x1 + float64(item.Size.X)*columnWidth
|
x2 := x1 + float64(component.Size.X)*columnWidth
|
||||||
y2 := y1 + float64(item.Size.Y)*rowHeight
|
y2 := y1 + float64(component.Size.Y)*rowHeight
|
||||||
|
|
||||||
item.Data.SetRect(int(x1), int(y1), int(x2), int(y2))
|
component.Drawable.SetRect(int(x1), int(y1), int(x2), int(y2))
|
||||||
item.Data.Draw(buf)
|
component.Drawable.Draw(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
12
main.go
12
main.go
|
@ -30,9 +30,9 @@ func main() {
|
||||||
pollers := make([]data.Poller, 0)
|
pollers := make([]data.Poller, 0)
|
||||||
lout := layout.NewLayout(ui.TerminalDimensions())
|
lout := layout.NewLayout(ui.TerminalDimensions())
|
||||||
|
|
||||||
for _, chartConfig := range cfg.LineChartConfigs {
|
for _, chartConfig := range cfg.RunCharts {
|
||||||
|
|
||||||
chart := widgets.NewTimePlot(chartConfig.Title)
|
chart := widgets.NewRunChart(chartConfig.Title)
|
||||||
lout.AddItem(chart, chartConfig.Position, chartConfig.Size)
|
lout.AddItem(chart, chartConfig.Position, chartConfig.Size)
|
||||||
|
|
||||||
for _, chartData := range chartConfig.DataConfig {
|
for _, chartData := range chartConfig.DataConfig {
|
||||||
|
@ -62,13 +62,13 @@ func main() {
|
||||||
switch e.ID {
|
switch e.ID {
|
||||||
case "<Left>":
|
case "<Left>":
|
||||||
// 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("").MoveItem(-1, 0)
|
//lout.GetItem("").Move(-1, 0)
|
||||||
case "<Right>":
|
case "<Right>":
|
||||||
//lout.GetItem(0).MoveItem(1, 0)
|
//lout.GetItem(0).Move(1, 0)
|
||||||
case "<Down>":
|
case "<Down>":
|
||||||
//lout.GetItem(0).MoveItem(0, 1)
|
//lout.GetItem(0).Move(0, 1)
|
||||||
case "<Up>":
|
case "<Up>":
|
||||||
//lout.GetItem(0).MoveItem(0, -1)
|
//lout.GetItem(0).Move(0, -1)
|
||||||
case "p":
|
case "p":
|
||||||
for _, poller := range pollers {
|
for _, poller := range pollers {
|
||||||
poller.TogglePause()
|
poller.TogglePause()
|
||||||
|
|
|
@ -11,19 +11,6 @@ import (
|
||||||
. "github.com/sqshq/termui"
|
. "github.com/sqshq/termui"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TimePlot struct { // TODO rename to linechart
|
|
||||||
Block
|
|
||||||
DataLabels []string
|
|
||||||
MaxValueTimePoint TimePoint
|
|
||||||
LineColors []Color
|
|
||||||
DotRune rune
|
|
||||||
HorizontalScale int
|
|
||||||
|
|
||||||
timePoints []TimePoint
|
|
||||||
dataMutex *sync.Mutex
|
|
||||||
grid PlotGrid
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
xAxisLabelsHeight = 1
|
xAxisLabelsHeight = 1
|
||||||
xAxisLabelsWidth = 8
|
xAxisLabelsWidth = 8
|
||||||
|
@ -32,61 +19,77 @@ const (
|
||||||
yAxisLabelsGap = 1
|
yAxisLabelsGap = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type RunChart struct {
|
||||||
|
Block
|
||||||
|
DataLabels []string
|
||||||
|
LineColors []Color
|
||||||
|
timePoints []TimePoint
|
||||||
|
dataMutex *sync.Mutex
|
||||||
|
grid ChartGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
type Item struct {
|
||||||
|
timePoints []TimePoint
|
||||||
|
color Color
|
||||||
|
label string
|
||||||
|
}
|
||||||
|
|
||||||
type TimePoint struct {
|
type TimePoint struct {
|
||||||
Value float64
|
Value float64
|
||||||
Time time.Time
|
Time time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTimePlot(title string) *TimePlot {
|
func NewRunChart(title string) *RunChart {
|
||||||
block := *NewBlock()
|
block := *NewBlock()
|
||||||
block.Title = title
|
block.Title = title
|
||||||
//self.LineColors[0] = ui.ColorYellow
|
//self.LineColors[0] = ui.ColorYellow
|
||||||
return &TimePlot{
|
return &RunChart{
|
||||||
Block: block,
|
Block: block,
|
||||||
LineColors: Theme.Plot.Lines,
|
LineColors: Theme.Plot.Lines,
|
||||||
DotRune: DOT,
|
timePoints: make([]TimePoint, 0),
|
||||||
HorizontalScale: 1,
|
dataMutex: &sync.Mutex{},
|
||||||
timePoints: make([]TimePoint, 0),
|
|
||||||
dataMutex: &sync.Mutex{},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type PlotGrid struct {
|
type ChartGrid struct {
|
||||||
count int
|
linesCount int
|
||||||
maxTimeX int
|
paddingDuration time.Duration
|
||||||
maxTime time.Time
|
paddingWidth int
|
||||||
minTime time.Time
|
maxTimeWidth int
|
||||||
maxValue float64
|
valueExtremum ValueExtremum
|
||||||
minValue float64
|
timeExtremum TimeExtremum
|
||||||
spacingDuration time.Duration
|
|
||||||
spacingWidth int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *TimePlot) newPlotGrid() PlotGrid {
|
type TimeExtremum struct {
|
||||||
|
max time.Time
|
||||||
|
min time.Time
|
||||||
|
}
|
||||||
|
|
||||||
count := (self.Inner.Max.X - self.Inner.Min.X) / (xAxisLabelsGap + xAxisLabelsWidth)
|
type ValueExtremum struct {
|
||||||
spacingDuration := time.Duration(time.Second) // TODO support others and/or adjust automatically depending on refresh rate
|
max float64
|
||||||
maxTime := time.Now()
|
min float64
|
||||||
minTime := maxTime.Add(-time.Duration(spacingDuration.Nanoseconds() * int64(count)))
|
}
|
||||||
maxPoint, minPoint := GetMaxAndMinValueTimePoints(self.timePoints)
|
|
||||||
|
|
||||||
return PlotGrid{
|
func (self *RunChart) newChartGrid() ChartGrid {
|
||||||
count: count,
|
|
||||||
spacingDuration: spacingDuration,
|
linesCount := (self.Inner.Max.X - self.Inner.Min.X) / (xAxisLabelsGap + xAxisLabelsWidth)
|
||||||
spacingWidth: xAxisLabelsGap + xAxisLabelsWidth,
|
paddingDuration := time.Duration(time.Second) // TODO support others and/or adjust automatically depending on refresh rate
|
||||||
maxTimeX: self.Inner.Max.X - xAxisLabelsWidth/2 - xAxisLabelsGap,
|
|
||||||
maxTime: maxTime,
|
return ChartGrid{
|
||||||
minTime: minTime,
|
linesCount: linesCount,
|
||||||
maxValue: maxPoint.Value,
|
paddingDuration: paddingDuration,
|
||||||
minValue: minPoint.Value,
|
paddingWidth: xAxisLabelsGap + xAxisLabelsWidth,
|
||||||
|
maxTimeWidth: self.Inner.Max.X - xAxisLabelsWidth/2 - xAxisLabelsGap,
|
||||||
|
timeExtremum: GetTimeExtremum(linesCount, paddingDuration),
|
||||||
|
valueExtremum: GetValueExtremum(self.timePoints),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *TimePlot) Draw(buf *Buffer) {
|
func (self *RunChart) Draw(buf *Buffer) {
|
||||||
|
|
||||||
self.dataMutex.Lock()
|
self.dataMutex.Lock()
|
||||||
self.Block.Draw(buf)
|
self.Block.Draw(buf)
|
||||||
self.grid = self.newPlotGrid()
|
self.grid = self.newChartGrid()
|
||||||
self.plotAxes(buf)
|
self.plotAxes(buf)
|
||||||
|
|
||||||
drawArea := image.Rect(
|
drawArea := image.Rect(
|
||||||
|
@ -98,7 +101,7 @@ func (self *TimePlot) Draw(buf *Buffer) {
|
||||||
self.dataMutex.Unlock()
|
self.dataMutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *TimePlot) ConsumeValue(value string, label string) {
|
func (self *RunChart) ConsumeValue(value string, label string) {
|
||||||
|
|
||||||
float, err := strconv.ParseFloat(value, 64)
|
float, err := strconv.ParseFloat(value, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -111,11 +114,11 @@ func (self *TimePlot) ConsumeValue(value string, label string) {
|
||||||
self.dataMutex.Unlock()
|
self.dataMutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *TimePlot) ConsumeError(err error) {
|
func (self *RunChart) ConsumeError(err error) {
|
||||||
// TODO visual notification
|
// TODO visual notification
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *TimePlot) trimOutOfRangeValues() {
|
func (self *RunChart) trimOutOfRangeValues() {
|
||||||
|
|
||||||
lastOutOfRangeValueIndex := -1
|
lastOutOfRangeValueIndex := -1
|
||||||
|
|
||||||
|
@ -130,7 +133,7 @@ func (self *TimePlot) trimOutOfRangeValues() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *TimePlot) renderBraille(buf *Buffer, drawArea image.Rectangle) {
|
func (self *RunChart) renderBraille(buf *Buffer, drawArea image.Rectangle) {
|
||||||
|
|
||||||
canvas := NewCanvas()
|
canvas := NewCanvas()
|
||||||
canvas.Rectangle = drawArea
|
canvas.Rectangle = drawArea
|
||||||
|
@ -144,12 +147,12 @@ func (self *TimePlot) renderBraille(buf *Buffer, drawArea image.Rectangle) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
timeDeltaWithGridMaxTime := self.grid.maxTime.Sub(timePoint.Time)
|
timeDeltaWithGridMaxTime := self.grid.timeExtremum.max.Sub(timePoint.Time)
|
||||||
deltaToSpacingRelation := float64(timeDeltaWithGridMaxTime.Nanoseconds()) / float64(self.grid.spacingDuration.Nanoseconds())
|
deltaToPaddingRelation := float64(timeDeltaWithGridMaxTime.Nanoseconds()) / float64(self.grid.paddingDuration.Nanoseconds())
|
||||||
x := self.grid.maxTimeX - (int(float64(self.grid.spacingWidth) * deltaToSpacingRelation))
|
x := self.grid.maxTimeWidth - (int(float64(self.grid.paddingWidth) * deltaToPaddingRelation))
|
||||||
|
|
||||||
valuePerYDot := (self.grid.maxValue - self.grid.minValue) / float64(drawArea.Dy()-1)
|
valuePerYDot := (self.grid.valueExtremum.max - self.grid.valueExtremum.min) / float64(drawArea.Dy()-1)
|
||||||
y := int(float64(timePoint.Value-self.grid.minValue) / valuePerYDot)
|
y := int(float64(timePoint.Value-self.grid.valueExtremum.min) / valuePerYDot)
|
||||||
|
|
||||||
if _, exists := pointPerX[x]; exists {
|
if _, exists := pointPerX[x]; exists {
|
||||||
continue
|
continue
|
||||||
|
@ -185,11 +188,11 @@ func (self *TimePlot) renderBraille(buf *Buffer, drawArea image.Rectangle) {
|
||||||
canvas.Draw(buf)
|
canvas.Draw(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *TimePlot) isTimePointInRange(point TimePoint) bool {
|
func (self *RunChart) isTimePointInRange(point TimePoint) bool {
|
||||||
return point.Time.After(self.grid.minTime.Add(self.grid.spacingDuration))
|
return point.Time.After(self.grid.timeExtremum.min.Add(self.grid.paddingDuration))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *TimePlot) plotAxes(buf *Buffer) {
|
func (self *RunChart) plotAxes(buf *Buffer) {
|
||||||
// draw origin cell
|
// draw origin cell
|
||||||
buf.SetCell(
|
buf.SetCell(
|
||||||
NewCell(BOTTOM_LEFT, NewStyle(ColorWhite)),
|
NewCell(BOTTOM_LEFT, NewStyle(ColorWhite)),
|
||||||
|
@ -206,10 +209,10 @@ func (self *TimePlot) plotAxes(buf *Buffer) {
|
||||||
|
|
||||||
// draw grid
|
// draw grid
|
||||||
for y := 0; y < self.Inner.Dy()-xAxisLabelsHeight-1; y = y + 2 {
|
for y := 0; y < self.Inner.Dy()-xAxisLabelsHeight-1; y = y + 2 {
|
||||||
for x := 0; x < self.grid.count; x++ {
|
for x := 0; x < self.grid.linesCount; x++ {
|
||||||
buf.SetCell(
|
buf.SetCell(
|
||||||
NewCell(VERTICAL_DASH, NewStyle(ColorDarkGrey)),
|
NewCell(VERTICAL_DASH, NewStyle(ColorDarkGrey)),
|
||||||
image.Pt(self.grid.maxTimeX-x*self.grid.spacingWidth, y+self.Inner.Min.Y+1),
|
image.Pt(self.grid.maxTimeWidth-x*self.grid.paddingWidth, y+self.Inner.Min.Y+1),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,30 +226,30 @@ func (self *TimePlot) plotAxes(buf *Buffer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw x axis time labels
|
// draw x axis time labels
|
||||||
for i := 0; i < self.grid.count; i++ {
|
for i := 0; i < self.grid.linesCount; i++ {
|
||||||
labelTime := self.grid.maxTime.Add(time.Duration(-i) * time.Second)
|
labelTime := self.grid.timeExtremum.max.Add(time.Duration(-i) * time.Second)
|
||||||
buf.SetString(
|
buf.SetString(
|
||||||
labelTime.Format("15:04:05"),
|
labelTime.Format("15:04:05"),
|
||||||
NewStyle(ColorWhite),
|
NewStyle(ColorWhite),
|
||||||
image.Pt(self.grid.maxTimeX-xAxisLabelsWidth/2-i*(self.grid.spacingWidth), self.Inner.Max.Y-1),
|
image.Pt(self.grid.maxTimeWidth-xAxisLabelsWidth/2-i*(self.grid.paddingWidth), self.Inner.Max.Y-1),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw y axis labels
|
// draw y axis labels
|
||||||
verticalScale := self.grid.maxValue - self.grid.minValue/float64(self.Inner.Dy()-xAxisLabelsHeight-1)
|
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++ {
|
for i := 1; i*(yAxisLabelsGap+1) <= self.Inner.Dy()-1; i++ {
|
||||||
buf.SetString(
|
buf.SetString(
|
||||||
fmt.Sprintf("%.3f", float64(i)*self.grid.minValue*verticalScale*(yAxisLabelsGap+1)),
|
fmt.Sprintf("%.3f", float64(i)*self.grid.valueExtremum.min*verticalScale*(yAxisLabelsGap+1)),
|
||||||
NewStyle(ColorWhite),
|
NewStyle(ColorWhite),
|
||||||
image.Pt(self.Inner.Min.X, self.Inner.Max.Y-(i*(yAxisLabelsGap+1))-2),
|
image.Pt(self.Inner.Min.X, self.Inner.Max.Y-(i*(yAxisLabelsGap+1))-2),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMaxAndMinValueTimePoints(points []TimePoint) (TimePoint, TimePoint) {
|
func GetValueExtremum(points []TimePoint) ValueExtremum {
|
||||||
|
|
||||||
if len(points) == 0 {
|
if len(points) == 0 {
|
||||||
return TimePoint{0, time.Now()}, TimePoint{0, time.Now()}
|
return ValueExtremum{0, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
var max, min = points[0], points[0]
|
var max, min = points[0], points[0]
|
||||||
|
@ -260,5 +263,13 @@ func GetMaxAndMinValueTimePoints(points []TimePoint) (TimePoint, TimePoint) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return max, min
|
return ValueExtremum{max: max.Value, min: min.Value}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTimeExtremum(linesCount int, paddingDuration time.Duration) TimeExtremum {
|
||||||
|
maxTime := time.Now()
|
||||||
|
return TimeExtremum{
|
||||||
|
max: maxTime,
|
||||||
|
min: maxTime.Add(-time.Duration(paddingDuration.Nanoseconds() * int64(linesCount))),
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue