triggers mechanism integration

This commit is contained in:
sqshq 2019-03-07 23:04:46 -05:00
parent 9b22083af8
commit 9b487ada89
20 changed files with 295 additions and 174 deletions

View File

@ -3,6 +3,7 @@ package asciibox
import ( import (
fl "github.com/mbndr/figlet4go" fl "github.com/mbndr/figlet4go"
"github.com/sqshq/sampler/asset" "github.com/sqshq/sampler/asset"
"github.com/sqshq/sampler/config"
"github.com/sqshq/sampler/data" "github.com/sqshq/sampler/data"
ui "github.com/sqshq/termui" ui "github.com/sqshq/termui"
"image" "image"
@ -10,6 +11,7 @@ import (
type AsciiBox struct { type AsciiBox struct {
ui.Block ui.Block
data.Consumer
text string text string
ascii string ascii string
style ui.Style style ui.Style
@ -17,22 +19,15 @@ type AsciiBox struct {
options *fl.RenderOptions options *fl.RenderOptions
} }
type AsciiFont string
const (
AsciiFontFlat AsciiFont = "flat"
AsciiFont3D AsciiFont = "3d"
)
const asciiFontExtension = ".flf" const asciiFontExtension = ".flf"
func NewAsciiBox(title string, font AsciiFont, color ui.Color) *AsciiBox { func NewAsciiBox(c config.AsciiBoxConfig) *AsciiBox {
block := *ui.NewBlock() block := *ui.NewBlock()
block.Title = title block.Title = c.Title
options := fl.NewRenderOptions() options := fl.NewRenderOptions()
options.FontName = string(font) options.FontName = string(*c.Font)
fontStr, err := asset.Asset(options.FontName + asciiFontExtension) fontStr, err := asset.Asset(options.FontName + asciiFontExtension)
if err != nil { if err != nil {
@ -41,17 +36,29 @@ func NewAsciiBox(title string, font AsciiFont, color ui.Color) *AsciiBox {
render := fl.NewAsciiRender() render := fl.NewAsciiRender()
_ = render.LoadBindataFont(fontStr, options.FontName) _ = render.LoadBindataFont(fontStr, options.FontName)
return &AsciiBox{ box := AsciiBox{
Block: block, Block: block,
style: ui.NewStyle(color), Consumer: data.NewConsumer(),
render: render, style: ui.NewStyle(*c.Color),
options: options, render: render,
options: options,
} }
go box.consume()
return &box
} }
func (a *AsciiBox) ConsumeSample(sample data.Sample) { func (a *AsciiBox) consume() {
a.text = sample.Value for {
a.ascii, _ = a.render.RenderOpts(sample.Value, a.options) select {
case sample := <-a.SampleChannel:
a.text = sample.Value
a.ascii, _ = a.render.RenderOpts(sample.Value, a.options)
//case alert := <-a.alertChannel:
// TODO base alerting mechanism
}
}
} }
func (a *AsciiBox) Draw(buffer *ui.Buffer) { func (a *AsciiBox) Draw(buffer *ui.Buffer) {

View File

@ -17,6 +17,7 @@ const (
type BarChart struct { type BarChart struct {
ui.Block ui.Block
data.Consumer
bars []Bar bars []Bar
scale int scale int
maxValue float64 maxValue float64
@ -33,19 +34,31 @@ type Bar struct {
func NewBarChart(title string, scale int) *BarChart { func NewBarChart(title string, scale int) *BarChart {
block := *ui.NewBlock() block := *ui.NewBlock()
block.Title = title block.Title = title
return &BarChart{ chart := BarChart{
Block: block, Block: block,
Consumer: data.NewConsumer(),
bars: []Bar{}, bars: []Bar{},
scale: scale, scale: scale,
maxValue: -math.MaxFloat64, maxValue: -math.MaxFloat64,
} }
go chart.consume()
return &chart
} }
func (b *BarChart) AddBar(label string, color ui.Color) { func (b *BarChart) consume() {
b.bars = append(b.bars, Bar{label: label, color: color, value: 0}) for {
select {
case sample := <-b.SampleChannel:
b.consumeSample(sample)
//case alert := <-b.alertChannel:
// TODO base alerting mechanism
}
}
} }
func (b *BarChart) ConsumeSample(sample data.Sample) { func (b *BarChart) consumeSample(sample data.Sample) {
b.count++ b.count++
@ -76,6 +89,10 @@ func (b *BarChart) ConsumeSample(sample data.Sample) {
} }
} }
func (b *BarChart) AddBar(label string, color ui.Color) {
b.bars = append(b.bars, Bar{label: label, color: color, value: 0})
}
func (b *BarChart) reselectMaxValue() { func (b *BarChart) reselectMaxValue() {
maxValue := -math.MaxFloat64 maxValue := -math.MaxFloat64
for _, bar := range b.bars { for _, bar := range b.bars {

View File

@ -18,6 +18,7 @@ const (
type Gauge struct { type Gauge struct {
ui.Block ui.Block
data.Consumer
minValue float64 minValue float64
maxValue float64 maxValue float64
curValue float64 curValue float64
@ -26,12 +27,30 @@ type Gauge struct {
} }
func NewGauge(title string, scale int, color ui.Color) *Gauge { func NewGauge(title string, scale int, color ui.Color) *Gauge {
block := *ui.NewBlock() block := *ui.NewBlock()
block.Title = title block.Title = title
return &Gauge{
Block: block, gauge := Gauge{
scale: scale, Block: block,
color: color, Consumer: data.NewConsumer(),
scale: scale,
color: color,
}
go gauge.consume()
return &gauge
}
func (g *Gauge) consume() {
for {
select {
case sample := <-g.SampleChannel:
g.ConsumeSample(sample)
//case alert := <-g.alertChannel:
// TODO base alerting mechanism
}
} }
} }

View File

@ -5,35 +5,35 @@ import (
"math" "math"
) )
func getRectLeftAgeCenter(rect image.Rectangle) image.Point { func GetRectLeftAgeCenter(rect image.Rectangle) image.Point {
return image.Point{ return image.Point{
X: rect.Min.X, X: rect.Min.X,
Y: rect.Min.Y + rect.Dy()/2, Y: rect.Min.Y + rect.Dy()/2,
} }
} }
func getRectRightAgeCenter(rect image.Rectangle) image.Point { func GetRectRightAgeCenter(rect image.Rectangle) image.Point {
return image.Point{ return image.Point{
X: rect.Max.X, X: rect.Max.X,
Y: rect.Min.Y + rect.Dy()/2, Y: rect.Min.Y + rect.Dy()/2,
} }
} }
func getRectTopAgeCenter(rect image.Rectangle) image.Point { func GetRectTopAgeCenter(rect image.Rectangle) image.Point {
return image.Point{ return image.Point{
X: rect.Min.X + rect.Dx()/2, X: rect.Min.X + rect.Dx()/2,
Y: rect.Min.Y, Y: rect.Min.Y,
} }
} }
func getRectBottomAgeCenter(rect image.Rectangle) image.Point { func GetRectBottomAgeCenter(rect image.Rectangle) image.Point {
return image.Point{ return image.Point{
X: rect.Min.X + rect.Dx()/2, X: rect.Min.X + rect.Dx()/2,
Y: rect.Max.Y, Y: rect.Max.Y,
} }
} }
func getDistance(p1 image.Point, p2 image.Point) float64 { func GetDistance(p1 image.Point, p2 image.Point) float64 {
x := math.Abs(float64(p1.X - p2.X)) x := math.Abs(float64(p1.X - p2.X))
y := math.Abs(float64(p1.Y - p2.Y)) y := math.Abs(float64(p1.Y - p2.Y))
return math.Sqrt(x*x + y*y) return math.Sqrt(x*x + y*y)

View File

@ -1,6 +1,7 @@
package component package layout
import ( import (
"github.com/sqshq/sampler/component"
"github.com/sqshq/sampler/component/runchart" "github.com/sqshq/sampler/component/runchart"
"github.com/sqshq/sampler/config" "github.com/sqshq/sampler/config"
"github.com/sqshq/sampler/console" "github.com/sqshq/sampler/console"
@ -11,10 +12,10 @@ import (
type Layout struct { type Layout struct {
ui.Block ui.Block
Components []Component Components []component.Component
ChangeModeEvents chan Mode ChangeModeEvents chan Mode
statusbar *StatusBar statusbar *component.StatusBar
menu *Menu menu *component.Menu
mode Mode mode Mode
selection int selection int
} }
@ -38,7 +39,7 @@ const (
statusbarHeight = 1 statusbarHeight = 1
) )
func NewLayout(width, height int, statusline *StatusBar, menu *Menu) *Layout { func NewLayout(width, height int, statusline *component.StatusBar, menu *component.Menu) *Layout {
block := *ui.NewBlock() block := *ui.NewBlock()
block.SetRect(0, 0, width, height) block.SetRect(0, 0, width, height)
@ -46,7 +47,7 @@ func NewLayout(width, height int, statusline *StatusBar, menu *Menu) *Layout {
return &Layout{ return &Layout{
Block: block, Block: block,
Components: make([]Component, 0), Components: make([]component.Component, 0),
statusbar: statusline, statusbar: statusline,
menu: menu, menu: menu,
mode: ModeDefault, mode: ModeDefault,
@ -56,16 +57,23 @@ func NewLayout(width, height int, statusline *StatusBar, menu *Menu) *Layout {
} }
func (l *Layout) AddComponent(Type config.ComponentType, drawable ui.Drawable, title string, position config.Position, size config.Size, refreshRateMs int) { func (l *Layout) AddComponent(Type config.ComponentType, drawable ui.Drawable, title string, position config.Position, size config.Size, refreshRateMs int) {
l.Components = append(l.Components, Component{Type, drawable, title, position, size, refreshRateMs}) l.Components = append(l.Components, component.Component{
Type: Type,
Drawable: drawable,
Title: title,
Position: position,
Size: size,
RefreshRateMs: refreshRateMs,
})
} }
func (l *Layout) GetComponents(Type config.ComponentType) []ui.Drawable { func (l *Layout) GetComponents(Type config.ComponentType) []component.Component {
var components []ui.Drawable var components []component.Component
for _, component := range l.Components { for _, c := range l.Components {
if component.Type == Type { if c.Type == Type {
components = append(components, component.Drawable) components = append(components, c)
} }
} }
@ -87,36 +95,36 @@ func (l *Layout) HandleConsoleEvent(e string) {
chart := l.getSelectedComponent().Drawable.(*runchart.RunChart) chart := l.getSelectedComponent().Drawable.(*runchart.RunChart)
chart.DisableSelection() chart.DisableSelection()
} }
l.menu.idle() l.menu.Idle()
l.changeMode(ModePause) l.changeMode(ModePause)
} }
case console.KeyEnter: case console.KeyEnter:
switch l.mode { switch l.mode {
case ModeComponentSelect: case ModeComponentSelect:
l.menu.choose() l.menu.Choose()
l.changeMode(ModeMenuOptionSelect) l.changeMode(ModeMenuOptionSelect)
case ModeMenuOptionSelect: case ModeMenuOptionSelect:
option := l.menu.getSelectedOption() option := l.menu.GetSelectedOption()
switch option { switch option {
case MenuOptionMove: case component.MenuOptionMove:
l.changeMode(ModeComponentMove) l.changeMode(ModeComponentMove)
l.menu.moveOrResize() l.menu.MoveOrResize()
case MenuOptionResize: case component.MenuOptionResize:
l.changeMode(ModeComponentResize) l.changeMode(ModeComponentResize)
l.menu.moveOrResize() l.menu.MoveOrResize()
case MenuOptionPinpoint: case component.MenuOptionPinpoint:
l.changeMode(ModeChartPinpoint) l.changeMode(ModeChartPinpoint)
l.menu.idle() l.menu.Idle()
chart := l.getSelectedComponent().Drawable.(*runchart.RunChart) chart := l.getSelectedComponent().Drawable.(*runchart.RunChart)
chart.MoveSelection(0) chart.MoveSelection(0)
case MenuOptionResume: case component.MenuOptionResume:
l.changeMode(ModeDefault) l.changeMode(ModeDefault)
l.menu.idle() l.menu.Idle()
} }
case ModeComponentMove: case ModeComponentMove:
fallthrough fallthrough
case ModeComponentResize: case ModeComponentResize:
l.menu.idle() l.menu.Idle()
l.changeMode(ModeDefault) l.changeMode(ModeDefault)
} }
case console.KeyEsc: case console.KeyEsc:
@ -128,20 +136,20 @@ func (l *Layout) HandleConsoleEvent(e string) {
case ModeComponentSelect: case ModeComponentSelect:
fallthrough fallthrough
case ModeMenuOptionSelect: case ModeMenuOptionSelect:
l.menu.idle() l.menu.Idle()
l.changeMode(ModeDefault) l.changeMode(ModeDefault)
} }
case console.KeyLeft: case console.KeyLeft:
switch l.mode { switch l.mode {
case ModeDefault: case ModeDefault:
l.changeMode(ModeComponentSelect) l.changeMode(ModeComponentSelect)
l.menu.highlight(l.getComponent(l.selection)) l.menu.Highlight(l.getComponent(l.selection))
case ModeChartPinpoint: case ModeChartPinpoint:
chart := l.getSelectedComponent().Drawable.(*runchart.RunChart) chart := l.getSelectedComponent().Drawable.(*runchart.RunChart)
chart.MoveSelection(-1) chart.MoveSelection(-1)
case ModeComponentSelect: case ModeComponentSelect:
l.moveSelection(e) l.moveSelection(e)
l.menu.highlight(l.getComponent(l.selection)) l.menu.Highlight(l.getComponent(l.selection))
case ModeComponentMove: case ModeComponentMove:
l.getSelectedComponent().Move(-1, 0) l.getSelectedComponent().Move(-1, 0)
case ModeComponentResize: case ModeComponentResize:
@ -151,13 +159,13 @@ func (l *Layout) HandleConsoleEvent(e string) {
switch l.mode { switch l.mode {
case ModeDefault: case ModeDefault:
l.changeMode(ModeComponentSelect) l.changeMode(ModeComponentSelect)
l.menu.highlight(l.getComponent(l.selection)) l.menu.Highlight(l.getComponent(l.selection))
case ModeChartPinpoint: case ModeChartPinpoint:
chart := l.getSelectedComponent().Drawable.(*runchart.RunChart) chart := l.getSelectedComponent().Drawable.(*runchart.RunChart)
chart.MoveSelection(1) chart.MoveSelection(1)
case ModeComponentSelect: case ModeComponentSelect:
l.moveSelection(e) l.moveSelection(e)
l.menu.highlight(l.getComponent(l.selection)) l.menu.Highlight(l.getComponent(l.selection))
case ModeComponentMove: case ModeComponentMove:
l.getSelectedComponent().Move(1, 0) l.getSelectedComponent().Move(1, 0)
case ModeComponentResize: case ModeComponentResize:
@ -167,12 +175,12 @@ func (l *Layout) HandleConsoleEvent(e string) {
switch l.mode { switch l.mode {
case ModeDefault: case ModeDefault:
l.changeMode(ModeComponentSelect) l.changeMode(ModeComponentSelect)
l.menu.highlight(l.getComponent(l.selection)) l.menu.Highlight(l.getComponent(l.selection))
case ModeComponentSelect: case ModeComponentSelect:
l.moveSelection(e) l.moveSelection(e)
l.menu.highlight(l.getComponent(l.selection)) l.menu.Highlight(l.getComponent(l.selection))
case ModeMenuOptionSelect: case ModeMenuOptionSelect:
l.menu.up() l.menu.Up()
case ModeComponentMove: case ModeComponentMove:
l.getSelectedComponent().Move(0, -1) l.getSelectedComponent().Move(0, -1)
case ModeComponentResize: case ModeComponentResize:
@ -182,12 +190,12 @@ func (l *Layout) HandleConsoleEvent(e string) {
switch l.mode { switch l.mode {
case ModeDefault: case ModeDefault:
l.changeMode(ModeComponentSelect) l.changeMode(ModeComponentSelect)
l.menu.highlight(l.getComponent(l.selection)) l.menu.Highlight(l.getComponent(l.selection))
case ModeComponentSelect: case ModeComponentSelect:
l.moveSelection(e) l.moveSelection(e)
l.menu.highlight(l.getComponent(l.selection)) l.menu.Highlight(l.getComponent(l.selection))
case ModeMenuOptionSelect: case ModeMenuOptionSelect:
l.menu.down() l.menu.Down()
case ModeComponentMove: case ModeComponentMove:
l.getSelectedComponent().Move(0, 1) l.getSelectedComponent().Move(0, 1)
case ModeComponentResize: case ModeComponentResize:
@ -200,11 +208,11 @@ func (l *Layout) ChangeDimensions(width, height int) {
l.SetRect(0, 0, width, height) l.SetRect(0, 0, width, height)
} }
func (l *Layout) getComponent(i int) Component { func (l *Layout) getComponent(i int) *component.Component {
return l.Components[i] return &l.Components[i]
} }
func (l *Layout) getSelectedComponent() *Component { func (l *Layout) getSelectedComponent() *component.Component {
return &l.Components[l.selection] return &l.Components[l.selection]
} }
@ -229,24 +237,25 @@ func (l *Layout) moveSelection(direction string) {
switch direction { switch direction {
case console.KeyLeft: case console.KeyLeft:
previouslySelectedCornerPoint = getRectLeftAgeCenter(previouslySelected.Drawable.GetRect()) previouslySelectedCornerPoint = component.GetRectLeftAgeCenter(previouslySelected.Drawable.GetRect())
newlySelectedCornerPoint = getRectRightAgeCenter(l.getComponent(newlySelectedIndex).Drawable.GetRect()) newlySelectedCornerPoint = component.GetRectRightAgeCenter(l.getComponent(newlySelectedIndex).Drawable.GetRect())
currentCornerPoint = getRectRightAgeCenter(current.Drawable.GetRect()) currentCornerPoint = component.GetRectRightAgeCenter(current.Drawable.GetRect())
case console.KeyRight: case console.KeyRight:
previouslySelectedCornerPoint = getRectRightAgeCenter(previouslySelected.Drawable.GetRect()) previouslySelectedCornerPoint = component.GetRectRightAgeCenter(previouslySelected.Drawable.GetRect())
newlySelectedCornerPoint = getRectLeftAgeCenter(l.getComponent(newlySelectedIndex).Drawable.GetRect()) newlySelectedCornerPoint = component.GetRectLeftAgeCenter(l.getComponent(newlySelectedIndex).Drawable.GetRect())
currentCornerPoint = getRectLeftAgeCenter(current.Drawable.GetRect()) currentCornerPoint = component.GetRectLeftAgeCenter(current.Drawable.GetRect())
case console.KeyUp: case console.KeyUp:
previouslySelectedCornerPoint = getRectTopAgeCenter(previouslySelected.Drawable.GetRect()) previouslySelectedCornerPoint = component.GetRectTopAgeCenter(previouslySelected.Drawable.GetRect())
newlySelectedCornerPoint = getRectBottomAgeCenter(l.getComponent(newlySelectedIndex).Drawable.GetRect()) newlySelectedCornerPoint = component.GetRectBottomAgeCenter(l.getComponent(newlySelectedIndex).Drawable.GetRect())
currentCornerPoint = getRectBottomAgeCenter(current.Drawable.GetRect()) currentCornerPoint = component.GetRectBottomAgeCenter(current.Drawable.GetRect())
case console.KeyDown: case console.KeyDown:
previouslySelectedCornerPoint = getRectBottomAgeCenter(previouslySelected.Drawable.GetRect()) previouslySelectedCornerPoint = component.GetRectBottomAgeCenter(previouslySelected.Drawable.GetRect())
newlySelectedCornerPoint = getRectTopAgeCenter(l.getComponent(newlySelectedIndex).Drawable.GetRect()) newlySelectedCornerPoint = component.GetRectTopAgeCenter(l.getComponent(newlySelectedIndex).Drawable.GetRect())
currentCornerPoint = getRectTopAgeCenter(current.Drawable.GetRect()) currentCornerPoint = component.GetRectTopAgeCenter(current.Drawable.GetRect())
} }
if getDistance(previouslySelectedCornerPoint, currentCornerPoint) < getDistance(previouslySelectedCornerPoint, newlySelectedCornerPoint) { if component.GetDistance(previouslySelectedCornerPoint, currentCornerPoint) <
component.GetDistance(previouslySelectedCornerPoint, newlySelectedCornerPoint) {
newlySelectedIndex = i newlySelectedIndex = i
} }
} }
@ -259,12 +268,12 @@ func (l *Layout) Draw(buffer *ui.Buffer) {
columnWidth := float64(l.GetRect().Dx()) / float64(columnsCount) columnWidth := float64(l.GetRect().Dx()) / float64(columnsCount)
rowHeight := float64(l.GetRect().Dy()-statusbarHeight) / float64(rowsCount) rowHeight := float64(l.GetRect().Dy()-statusbarHeight) / float64(rowsCount)
for _, component := range l.Components { for _, c := range l.Components {
x1 := math.Floor(float64(component.Position.X) * columnWidth) x1 := math.Floor(float64(c.Position.X) * columnWidth)
y1 := math.Floor(float64(component.Position.Y) * rowHeight) y1 := math.Floor(float64(c.Position.Y) * rowHeight)
x2 := x1 + math.Floor(float64(component.Size.X))*columnWidth x2 := x1 + math.Floor(float64(c.Size.X))*columnWidth
y2 := y1 + math.Floor(float64(component.Size.Y))*rowHeight y2 := y1 + math.Floor(float64(c.Size.Y))*rowHeight
if x2-x1 < minDimension { if x2-x1 < minDimension {
x2 = x1 + minDimension x2 = x1 + minDimension
@ -274,8 +283,8 @@ func (l *Layout) Draw(buffer *ui.Buffer) {
y2 = y1 + minDimension y2 = y1 + minDimension
} }
component.Drawable.SetRect(int(x1), int(y1), int(x2), int(y2)) c.Drawable.SetRect(int(x1), int(y1), int(x2), int(y2))
component.Drawable.Draw(buffer) c.Drawable.Draw(buffer)
} }
l.statusbar.SetRect( l.statusbar.SetRect(

View File

@ -48,26 +48,26 @@ func NewMenu() *Menu {
} }
} }
func (m *Menu) getSelectedOption() MenuOption { func (m *Menu) GetSelectedOption() MenuOption {
return m.option return m.option
} }
func (m *Menu) highlight(component Component) { func (m *Menu) Highlight(component *Component) {
m.component = component m.component = *component
m.updateDimensions() m.updateDimensions()
m.mode = MenuModeHighlight m.mode = MenuModeHighlight
m.Title = component.Title m.Title = component.Title
} }
func (m *Menu) choose() { func (m *Menu) Choose() {
m.mode = MenuModeOptionSelect m.mode = MenuModeOptionSelect
} }
func (m *Menu) idle() { func (m *Menu) Idle() {
m.mode = MenuModeIdle m.mode = MenuModeIdle
} }
func (m *Menu) up() { func (m *Menu) Up() {
for i := 1; i < len(m.options); i++ { for i := 1; i < len(m.options); i++ {
if m.options[i] == m.option { if m.options[i] == m.option {
m.option = m.options[i-1] m.option = m.options[i-1]
@ -75,11 +75,11 @@ func (m *Menu) up() {
} }
} }
if m.option == MenuOptionPinpoint && m.component.Type != config.TypeRunChart { if m.option == MenuOptionPinpoint && m.component.Type != config.TypeRunChart {
m.up() m.Up()
} }
} }
func (m *Menu) down() { func (m *Menu) Down() {
for i := 0; i < len(m.options)-1; i++ { for i := 0; i < len(m.options)-1; i++ {
if m.options[i] == m.option { if m.options[i] == m.option {
m.option = m.options[i+1] m.option = m.options[i+1]
@ -87,11 +87,11 @@ func (m *Menu) down() {
} }
} }
if m.option == MenuOptionPinpoint && m.component.Type != config.TypeRunChart { if m.option == MenuOptionPinpoint && m.component.Type != config.TypeRunChart {
m.down() m.Down()
} }
} }
func (m *Menu) moveOrResize() { func (m *Menu) MoveOrResize() {
m.mode = MenuModeMoveAndResize m.mode = MenuModeMoveAndResize
} }

View File

@ -35,13 +35,13 @@ func (c *RunChart) newChartGrid() ChartGrid {
func (c *RunChart) renderAxes(buffer *ui.Buffer) { func (c *RunChart) renderAxes(buffer *ui.Buffer) {
// draw origin cell // draw origin cell
buffer.SetCell( buffer.SetCell(
ui.NewCell(ui.BOTTOM_LEFT, ui.NewStyle(ui.ColorWhite)), ui.NewCell(ui.BOTTOM_LEFT, ui.NewStyle(console.ColorWhite)),
image.Pt(c.Inner.Min.X+c.grid.minTimeWidth, c.Inner.Max.Y-xAxisLabelsHeight-1)) image.Pt(c.Inner.Min.X+c.grid.minTimeWidth, c.Inner.Max.Y-xAxisLabelsHeight-1))
// draw x axis line // draw x axis line
for i := c.grid.minTimeWidth + 1; i < c.Inner.Dx(); i++ { for i := c.grid.minTimeWidth + 1; i < c.Inner.Dx(); i++ {
buffer.SetCell( buffer.SetCell(
ui.NewCell(ui.HORIZONTAL_DASH, ui.NewStyle(ui.ColorWhite)), ui.NewCell(ui.HORIZONTAL_DASH, ui.NewStyle(console.ColorWhite)),
image.Pt(i+c.Inner.Min.X, c.Inner.Max.Y-xAxisLabelsHeight-1)) image.Pt(i+c.Inner.Min.X, c.Inner.Max.Y-xAxisLabelsHeight-1))
} }
@ -57,7 +57,7 @@ func (c *RunChart) renderAxes(buffer *ui.Buffer) {
// draw y axis line // draw y axis line
for i := 0; i < c.Inner.Dy()-xAxisLabelsHeight-1; i++ { for i := 0; i < c.Inner.Dy()-xAxisLabelsHeight-1; i++ {
buffer.SetCell( buffer.SetCell(
ui.NewCell(ui.VERTICAL_DASH, ui.NewStyle(ui.ColorWhite)), ui.NewCell(ui.VERTICAL_DASH, ui.NewStyle(console.ColorWhite)),
image.Pt(c.Inner.Min.X+c.grid.minTimeWidth, i+c.Inner.Min.Y)) image.Pt(c.Inner.Min.X+c.grid.minTimeWidth, i+c.Inner.Min.Y))
} }
@ -66,7 +66,7 @@ func (c *RunChart) renderAxes(buffer *ui.Buffer) {
labelTime := c.grid.timeRange.max.Add(time.Duration(-i) * c.timescale) labelTime := c.grid.timeRange.max.Add(time.Duration(-i) * c.timescale)
buffer.SetString( buffer.SetString(
labelTime.Format("15:04:05"), labelTime.Format("15:04:05"),
ui.NewStyle(ui.ColorWhite), ui.NewStyle(console.ColorWhite),
image.Pt(c.grid.maxTimeWidth-xAxisLabelsWidth/2-i*(xAxisGridWidth), c.Inner.Max.Y-1)) image.Pt(c.grid.maxTimeWidth-xAxisLabelsWidth/2-i*(xAxisGridWidth), c.Inner.Max.Y-1))
} }
@ -78,13 +78,13 @@ func (c *RunChart) renderAxes(buffer *ui.Buffer) {
value := c.grid.valueExtrema.max - (valuePerY * float64(i) * (yAxisLabelsIndent + yAxisLabelsHeight)) value := c.grid.valueExtrema.max - (valuePerY * float64(i) * (yAxisLabelsIndent + yAxisLabelsHeight))
buffer.SetString( buffer.SetString(
formatValue(value, c.scale), formatValue(value, c.scale),
ui.NewStyle(ui.ColorWhite), ui.NewStyle(console.ColorWhite),
image.Pt(c.Inner.Min.X, 1+c.Inner.Min.Y+i*(yAxisLabelsIndent+yAxisLabelsHeight))) image.Pt(c.Inner.Min.X, 1+c.Inner.Min.Y+i*(yAxisLabelsIndent+yAxisLabelsHeight)))
} }
} else { } else {
buffer.SetString( buffer.SetString(
formatValue(c.grid.valueExtrema.max, c.scale), formatValue(c.grid.valueExtrema.max, c.scale),
ui.NewStyle(ui.ColorWhite), ui.NewStyle(console.ColorWhite),
image.Pt(c.Inner.Min.X, c.Inner.Min.Y+c.Inner.Dy()/2)) image.Pt(c.Inner.Min.X, c.Inner.Min.Y+c.Inner.Dy()/2))
} }
} }

View File

@ -2,6 +2,7 @@ package runchart
import ( import (
"fmt" "fmt"
"github.com/sqshq/sampler/console"
ui "github.com/sqshq/termui" ui "github.com/sqshq/termui"
"image" "image"
"math" "math"
@ -54,7 +55,7 @@ func (c *RunChart) renderLegend(buffer *ui.Buffer, rectangle image.Rectangle) {
y := c.Inner.Min.Y + yAxisLegendIndent + row*height y := c.Inner.Min.Y + yAxisLegendIndent + row*height
titleStyle := ui.NewStyle(line.color) titleStyle := ui.NewStyle(line.color)
detailsStyle := ui.NewStyle(ui.ColorWhite) detailsStyle := ui.NewStyle(console.ColorWhite)
buffer.SetString(string(ui.DOT), titleStyle, image.Pt(x-2, y)) buffer.SetString(string(ui.DOT), titleStyle, image.Pt(x-2, y))
buffer.SetString(line.label, titleStyle, image.Pt(x, y)) buffer.SetString(line.label, titleStyle, image.Pt(x, y))

View File

@ -2,9 +2,10 @@ package runchart
import ( import (
"fmt" "fmt"
"github.com/sqshq/sampler/component/trigger" "github.com/sqshq/sampler/config"
"github.com/sqshq/sampler/console" "github.com/sqshq/sampler/console"
"github.com/sqshq/sampler/data" "github.com/sqshq/sampler/data"
"github.com/sqshq/sampler/trigger"
"image" "image"
"math" "math"
"strconv" "strconv"
@ -37,6 +38,7 @@ const (
type RunChart struct { type RunChart struct {
ui.Block ui.Block
data.Consumer
triggers []trigger.Trigger triggers []trigger.Trigger
lines []TimeLine lines []TimeLine
grid ChartGrid grid ChartGrid
@ -73,17 +75,35 @@ type ValueExtrema struct {
min float64 min float64
} }
func NewRunChart(title string, scale int, refreshRateMs int, legend Legend) *RunChart { func NewRunChart(c config.RunChartConfig, l Legend) *RunChart {
block := *ui.NewBlock() block := *ui.NewBlock()
block.Title = title block.Title = c.Title
return &RunChart{
chart := RunChart{
Block: block, Block: block,
Consumer: data.NewConsumer(),
lines: []TimeLine{}, lines: []TimeLine{},
timescale: calculateTimescale(refreshRateMs), timescale: calculateTimescale(*c.RefreshRateMs),
mutex: &sync.Mutex{}, mutex: &sync.Mutex{},
scale: scale, scale: *c.Scale,
mode: ModeDefault, mode: ModeDefault,
legend: legend, legend: l,
}
go chart.consume()
return &chart
}
func (c *RunChart) consume() {
for {
select {
case sample := <-c.SampleChannel:
c.consumeSample(sample)
//case alert := <-c.alertChannel:
// TODO base alerting mechanism
}
} }
} }
@ -123,12 +143,12 @@ func (c *RunChart) AddLine(Label string, color ui.Color) {
c.lines = append(c.lines, line) c.lines = append(c.lines, line)
} }
func (c *RunChart) ConsumeSample(sample data.Sample) { func (c *RunChart) consumeSample(sample data.Sample) {
float, err := strconv.ParseFloat(sample.Value, 64) float, err := strconv.ParseFloat(sample.Value, 64)
if err != nil { if err != nil {
// TODO visual notification + check sample.Error // TODO visual notification
} }
c.mutex.Lock() c.mutex.Lock()

View File

@ -27,7 +27,7 @@ func NewStatusLine(configFileName string) *StatusBar {
"(Q) quit", "(Q) quit",
"(P) pause", "(P) pause",
"(<->) selection", "(<->) selection",
"(ESC) reset alarm", "(ESC) reset alerts",
}, },
} }
} }

View File

@ -1,11 +1,20 @@
package config package config
import ( import (
"github.com/sqshq/sampler/component/asciibox" "github.com/sqshq/sampler/console"
"github.com/sqshq/sampler/data"
ui "github.com/sqshq/termui" ui "github.com/sqshq/termui"
) )
type ComponentType rune
const (
TypeRunChart ComponentType = 0
TypeBarChart ComponentType = 1
TypeTextBox ComponentType = 2
TypeAsciiBox ComponentType = 3
TypeGauge ComponentType = 4
)
type ComponentConfig struct { type ComponentConfig struct {
Title string `yaml:"title"` Title string `yaml:"title"`
RefreshRateMs *int `yaml:"refresh-rate-ms,omitempty"` RefreshRateMs *int `yaml:"refresh-rate-ms,omitempty"`
@ -32,26 +41,26 @@ type GaugeConfig struct {
Scale *int `yaml:"scale,omitempty"` Scale *int `yaml:"scale,omitempty"`
Color *ui.Color `yaml:"color,omitempty"` Color *ui.Color `yaml:"color,omitempty"`
Values map[string]string `yaml:"values"` Values map[string]string `yaml:"values"`
Items []data.Item `yaml:",omitempty"` Items []Item `yaml:",omitempty"`
} }
type BarChartConfig struct { type BarChartConfig struct {
ComponentConfig `yaml:",inline"` ComponentConfig `yaml:",inline"`
Scale *int `yaml:"scale,omitempty"` Scale *int `yaml:"scale,omitempty"`
Items []data.Item `yaml:"items"` Items []Item `yaml:"items"`
} }
type AsciiBoxConfig struct { type AsciiBoxConfig struct {
ComponentConfig `yaml:",inline"` ComponentConfig `yaml:",inline"`
data.Item `yaml:",inline"` Item `yaml:",inline"`
Font *asciibox.AsciiFont `yaml:"font,omitempty"` Font *console.AsciiFont `yaml:"font,omitempty"`
} }
type RunChartConfig struct { type RunChartConfig struct {
ComponentConfig `yaml:",inline"` ComponentConfig `yaml:",inline"`
Legend *LegendConfig `yaml:"legend,omitempty"` Legend *LegendConfig `yaml:"legend,omitempty"`
Scale *int `yaml:"scale,omitempty"` Scale *int `yaml:"scale,omitempty"`
Items []data.Item `yaml:"items"` Items []Item `yaml:"items"`
} }
type LegendConfig struct { type LegendConfig struct {
@ -69,15 +78,11 @@ type Size struct {
Y int `yaml:"h"` Y int `yaml:"h"`
} }
type ComponentType rune type Item struct {
Label *string `yaml:"label,omitempty"`
const ( Script string `yaml:"value"`
TypeRunChart ComponentType = 0 Color *ui.Color `yaml:"color,omitempty"`
TypeBarChart ComponentType = 1 }
TypeTextBox ComponentType = 2
TypeAsciiBox ComponentType = 3
TypeGauge ComponentType = 4
)
type ComponentSettings struct { type ComponentSettings struct {
Type ComponentType Type ComponentType

View File

@ -1,9 +1,7 @@
package config package config
import ( import (
"github.com/sqshq/sampler/component/asciibox"
"github.com/sqshq/sampler/console" "github.com/sqshq/sampler/console"
"github.com/sqshq/sampler/data"
) )
const ( const (
@ -71,10 +69,10 @@ func (c *Config) setDefaultValues() {
p := defaultScale p := defaultScale
g.Scale = &p g.Scale = &p
} }
var items []data.Item var items []Item
for label, script := range g.Values { for label, script := range g.Values {
l := label l := label
items = append(items, data.Item{Label: &l, Script: script}) items = append(items, Item{Label: &l, Script: script})
} }
g.Items = items g.Items = items
c.Gauges[i] = g c.Gauges[i] = g
@ -93,7 +91,7 @@ func (c *Config) setDefaultValues() {
box.Label = &label box.Label = &label
} }
if box.Font == nil { if box.Font == nil {
font := asciibox.AsciiFontFlat font := console.AsciiFontFlat
box.Font = &font box.Font = &font
} }
if box.Color == nil { if box.Color == nil {

View File

@ -14,6 +14,13 @@ const (
AppVersion = "0.1.0" AppVersion = "0.1.0"
) )
type AsciiFont string
const (
AsciiFontFlat AsciiFont = "flat"
AsciiFont3D AsciiFont = "3d"
)
type Console struct{} type Console struct{}
func (self *Console) Init() { func (self *Console) Init() {

View File

@ -20,7 +20,7 @@ const (
ColorOrange ui.Color = 166 ColorOrange ui.Color = 166
ColorPurple ui.Color = 129 ColorPurple ui.Color = 129
ColorGreen ui.Color = 64 ColorGreen ui.Color = 64
ColorDarkGrey ui.Color = 240 ColorDarkGrey ui.Color = 235
ColorGrey ui.Color = 242 ColorGrey ui.Color = 242
ColorWhite ui.Color = 15 ColorWhite ui.Color = 15
ColorBlack ui.Color = 0 ColorBlack ui.Color = 0
@ -28,7 +28,7 @@ const (
) )
const ( const (
MenuColorBackground ui.Color = 234 MenuColorBackground ui.Color = 235
MenuColorText ui.Color = 255 MenuColorText ui.Color = 255
) )

View File

@ -1,11 +1,22 @@
package data package data
type Consumer interface { type Consumer struct {
ConsumeSample(sample Sample) SampleChannel chan Sample
AlertChannel chan Alert
} }
type Sample struct { type Sample struct {
Label string Label string
Value string Value string
Error error }
type Alert struct {
Text string
}
func NewConsumer() Consumer {
return Consumer{
SampleChannel: make(chan Sample),
AlertChannel: make(chan Alert),
}
} }

View File

@ -7,9 +7,9 @@ import (
) )
type Item struct { type Item struct {
Label *string `yaml:"label,omitempty"` Label string
Script string `yaml:"value"` Script string
Color *ui.Color `yaml:"color,omitempty"` Color ui.Color
} }
func (i *Item) nextValue() (value string, err error) { func (i *Item) nextValue() (value string, err error) {

View File

@ -1,18 +1,20 @@
package data package data
import ( import (
"github.com/sqshq/sampler/trigger"
"time" "time"
) )
type Sampler struct { type Sampler struct {
consumer Consumer consumer Consumer
item Item item Item
triggers []trigger.Trigger
} }
func NewSampler(consumer Consumer, item Item, rateMs int) Sampler { func NewSampler(consumer Consumer, item Item, triggers []trigger.Trigger, rateMs int) Sampler {
ticker := time.NewTicker(time.Duration(rateMs * int(time.Millisecond))) ticker := time.NewTicker(time.Duration(rateMs * int(time.Millisecond)))
sampler := Sampler{consumer, item} sampler := Sampler{consumer, item, triggers}
go func() { go func() {
sampler.sample() sampler.sample()
@ -26,6 +28,10 @@ func NewSampler(consumer Consumer, item Item, rateMs int) Sampler {
func (s *Sampler) sample() { func (s *Sampler) sample() {
value, err := s.item.nextValue() value, err := s.item.nextValue()
sample := Sample{Value: value, Error: err, Label: *s.item.Label} if err == nil {
s.consumer.ConsumeSample(sample) sample := Sample{Value: value, Label: s.item.Label}
s.consumer.SampleChannel <- sample
} else {
s.consumer.AlertChannel <- Alert{Text: err.Error()}
}
} }

View File

@ -1,7 +1,7 @@
package event package event
import ( import (
"github.com/sqshq/sampler/component" "github.com/sqshq/sampler/component/layout"
"github.com/sqshq/sampler/config" "github.com/sqshq/sampler/config"
"github.com/sqshq/sampler/console" "github.com/sqshq/sampler/console"
ui "github.com/sqshq/termui" ui "github.com/sqshq/termui"
@ -13,13 +13,13 @@ const (
) )
type Handler struct { type Handler struct {
layout *component.Layout layout *layout.Layout
renderTicker *time.Ticker renderTicker *time.Ticker
consoleEvents <-chan ui.Event consoleEvents <-chan ui.Event
renderRate time.Duration renderRate time.Duration
} }
func NewHandler(layout *component.Layout) Handler { func NewHandler(layout *layout.Layout) Handler {
renderRate := calcMinRenderRate(layout) renderRate := calcMinRenderRate(layout)
return Handler{ return Handler{
layout: layout, layout: layout,
@ -55,16 +55,16 @@ func (h *Handler) HandleEvents() {
} }
} }
func (h *Handler) handleModeChange(m component.Mode) { func (h *Handler) handleModeChange(m layout.Mode) {
// render the change before switching the tickers // render the change before switching the tickers
ui.Render(h.layout) ui.Render(h.layout)
h.renderTicker.Stop() h.renderTicker.Stop()
switch m { switch m {
case component.ModeDefault: case layout.ModeDefault:
h.renderTicker = time.NewTicker(h.renderRate) h.renderTicker = time.NewTicker(h.renderRate)
case component.ModePause: case layout.ModePause:
// proceed with stopped timer // proceed with stopped timer
default: default:
h.renderTicker = time.NewTicker(console.MinRenderInterval) h.renderTicker = time.NewTicker(console.MinRenderInterval)
@ -80,7 +80,7 @@ func (h *Handler) handleExit() {
config.Update(settings) config.Update(settings)
} }
func calcMinRenderRate(layout *component.Layout) time.Duration { func calcMinRenderRate(layout *layout.Layout) time.Duration {
minRefreshRateMs := layout.Components[0].RefreshRateMs minRefreshRateMs := layout.Components[0].RefreshRateMs
for _, c := range layout.Components { for _, c := range layout.Components {

44
main.go
View File

@ -5,11 +5,13 @@ import (
"github.com/sqshq/sampler/component/asciibox" "github.com/sqshq/sampler/component/asciibox"
"github.com/sqshq/sampler/component/barchart" "github.com/sqshq/sampler/component/barchart"
"github.com/sqshq/sampler/component/gauge" "github.com/sqshq/sampler/component/gauge"
"github.com/sqshq/sampler/component/layout"
"github.com/sqshq/sampler/component/runchart" "github.com/sqshq/sampler/component/runchart"
"github.com/sqshq/sampler/config" "github.com/sqshq/sampler/config"
"github.com/sqshq/sampler/console" "github.com/sqshq/sampler/console"
"github.com/sqshq/sampler/data" "github.com/sqshq/sampler/data"
"github.com/sqshq/sampler/event" "github.com/sqshq/sampler/event"
"github.com/sqshq/sampler/trigger"
ui "github.com/sqshq/termui" ui "github.com/sqshq/termui"
) )
@ -21,47 +23,55 @@ func main() {
defer csl.Close() defer csl.Close()
width, height := ui.TerminalDimensions() width, height := ui.TerminalDimensions()
layout := component.NewLayout(width, height, component.NewStatusLine(flg.ConfigFileName), component.NewMenu()) lout := layout.NewLayout(width, height, component.NewStatusLine(flg.ConfigFileName), component.NewMenu())
for _, c := range cfg.RunCharts { for _, c := range cfg.RunCharts {
legend := runchart.Legend{Enabled: c.Legend.Enabled, Details: c.Legend.Details} legend := runchart.Legend{Enabled: c.Legend.Enabled, Details: c.Legend.Details}
chart := runchart.NewRunChart(c.Title, *c.Scale, *c.RefreshRateMs, legend) chart := runchart.NewRunChart(c, legend)
layout.AddComponent(config.TypeRunChart, chart, c.Title, c.Position, c.Size, *c.RefreshRateMs) lout.AddComponent(config.TypeRunChart, chart, c.Title, c.Position, c.Size, *c.RefreshRateMs)
triggers := trigger.NewTriggers(c.Triggers)
for _, item := range c.Items { for _, i := range c.Items {
chart.AddLine(*item.Label, *item.Color) item := data.Item{Label: *i.Label, Script: i.Script, Color: *i.Color}
data.NewSampler(chart, item, *c.RefreshRateMs) chart.AddLine(item.Label, item.Color)
data.NewSampler(chart.Consumer, item, triggers, *c.RefreshRateMs)
} }
} }
for _, a := range cfg.AsciiBoxes { for _, a := range cfg.AsciiBoxes {
box := asciibox.NewAsciiBox(a.Title, *a.Font, *a.Item.Color) box := asciibox.NewAsciiBox(a)
layout.AddComponent(config.TypeAsciiBox, box, a.Title, a.Position, a.Size, *a.RefreshRateMs) item := data.Item{Label: *a.Label, Script: a.Script, Color: *a.Color}
data.NewSampler(box, a.Item, *a.RefreshRateMs) triggers := trigger.NewTriggers(a.Triggers)
lout.AddComponent(config.TypeAsciiBox, box, a.Title, a.Position, a.Size, *a.RefreshRateMs)
data.NewSampler(box.Consumer, item, triggers, *a.RefreshRateMs)
} }
for _, b := range cfg.BarCharts { for _, b := range cfg.BarCharts {
chart := barchart.NewBarChart(b.Title, *b.Scale) chart := barchart.NewBarChart(b.Title, *b.Scale)
layout.AddComponent(config.TypeBarChart, chart, b.Title, b.Position, b.Size, *b.RefreshRateMs) triggers := trigger.NewTriggers(b.Triggers)
lout.AddComponent(config.TypeBarChart, chart, b.Title, b.Position, b.Size, *b.RefreshRateMs)
for _, item := range b.Items { for _, i := range b.Items {
chart.AddBar(*item.Label, *item.Color) item := data.Item{Label: *i.Label, Script: i.Script, Color: *i.Color}
data.NewSampler(chart, item, *b.RefreshRateMs) chart.AddBar(*i.Label, *i.Color)
data.NewSampler(chart.Consumer, item, triggers, *b.RefreshRateMs)
} }
} }
for _, gc := range cfg.Gauges { for _, gc := range cfg.Gauges {
g := gauge.NewGauge(gc.Title, *gc.Scale, *gc.Color) g := gauge.NewGauge(gc.Title, *gc.Scale, *gc.Color)
layout.AddComponent(config.TypeGauge, g, gc.Title, gc.Position, gc.Size, *gc.RefreshRateMs) triggers := trigger.NewTriggers(gc.Triggers)
lout.AddComponent(config.TypeGauge, g, gc.Title, gc.Position, gc.Size, *gc.RefreshRateMs)
for _, item := range gc.Items { for _, i := range gc.Items {
data.NewSampler(g, item, *gc.RefreshRateMs) item := data.Item{Label: *i.Label, Script: i.Script}
data.NewSampler(g.Consumer, item, triggers, *gc.RefreshRateMs)
} }
} }
handler := event.NewHandler(layout) handler := event.NewHandler(lout)
handler.HandleEvents() handler.HandleEvents()
} }

View File

@ -32,6 +32,17 @@ type Data struct {
currentValue interface{} currentValue interface{}
} }
func NewTriggers(configs []config.TriggerConfig) []Trigger {
triggers := make([]Trigger, len(configs))
for _, c := range configs {
triggers = append(triggers, NewTrigger(c))
}
return triggers
}
func NewTrigger(config config.TriggerConfig) Trigger { func NewTrigger(config config.TriggerConfig) Trigger {
return Trigger{ return Trigger{
title: config.Title, title: config.Title,