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

View File

@ -17,6 +17,7 @@ const (
type BarChart struct {
ui.Block
data.Consumer
bars []Bar
scale int
maxValue float64
@ -33,19 +34,31 @@ type Bar struct {
func NewBarChart(title string, scale int) *BarChart {
block := *ui.NewBlock()
block.Title = title
return &BarChart{
chart := BarChart{
Block: block,
Consumer: data.NewConsumer(),
bars: []Bar{},
scale: scale,
maxValue: -math.MaxFloat64,
}
go chart.consume()
return &chart
}
func (b *BarChart) AddBar(label string, color ui.Color) {
b.bars = append(b.bars, Bar{label: label, color: color, value: 0})
func (b *BarChart) consume() {
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++
@ -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() {
maxValue := -math.MaxFloat64
for _, bar := range b.bars {

View File

@ -18,6 +18,7 @@ const (
type Gauge struct {
ui.Block
data.Consumer
minValue float64
maxValue float64
curValue float64
@ -26,12 +27,30 @@ type Gauge struct {
}
func NewGauge(title string, scale int, color ui.Color) *Gauge {
block := *ui.NewBlock()
block.Title = title
return &Gauge{
Block: block,
scale: scale,
color: color,
gauge := Gauge{
Block: block,
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"
)
func getRectLeftAgeCenter(rect image.Rectangle) image.Point {
func GetRectLeftAgeCenter(rect image.Rectangle) image.Point {
return image.Point{
X: rect.Min.X,
Y: rect.Min.Y + rect.Dy()/2,
}
}
func getRectRightAgeCenter(rect image.Rectangle) image.Point {
func GetRectRightAgeCenter(rect image.Rectangle) image.Point {
return image.Point{
X: rect.Max.X,
Y: rect.Min.Y + rect.Dy()/2,
}
}
func getRectTopAgeCenter(rect image.Rectangle) image.Point {
func GetRectTopAgeCenter(rect image.Rectangle) image.Point {
return image.Point{
X: rect.Min.X + rect.Dx()/2,
Y: rect.Min.Y,
}
}
func getRectBottomAgeCenter(rect image.Rectangle) image.Point {
func GetRectBottomAgeCenter(rect image.Rectangle) image.Point {
return image.Point{
X: rect.Min.X + rect.Dx()/2,
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))
y := math.Abs(float64(p1.Y - p2.Y))
return math.Sqrt(x*x + y*y)

View File

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

View File

@ -35,13 +35,13 @@ func (c *RunChart) newChartGrid() ChartGrid {
func (c *RunChart) renderAxes(buffer *ui.Buffer) {
// draw origin cell
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))
// draw x axis line
for i := c.grid.minTimeWidth + 1; i < c.Inner.Dx(); i++ {
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))
}
@ -57,7 +57,7 @@ func (c *RunChart) renderAxes(buffer *ui.Buffer) {
// draw y axis line
for i := 0; i < c.Inner.Dy()-xAxisLabelsHeight-1; i++ {
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))
}
@ -66,7 +66,7 @@ func (c *RunChart) renderAxes(buffer *ui.Buffer) {
labelTime := c.grid.timeRange.max.Add(time.Duration(-i) * c.timescale)
buffer.SetString(
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))
}
@ -78,13 +78,13 @@ func (c *RunChart) renderAxes(buffer *ui.Buffer) {
value := c.grid.valueExtrema.max - (valuePerY * float64(i) * (yAxisLabelsIndent + yAxisLabelsHeight))
buffer.SetString(
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)))
}
} else {
buffer.SetString(
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))
}
}

View File

@ -2,6 +2,7 @@ package runchart
import (
"fmt"
"github.com/sqshq/sampler/console"
ui "github.com/sqshq/termui"
"image"
"math"
@ -54,7 +55,7 @@ func (c *RunChart) renderLegend(buffer *ui.Buffer, rectangle image.Rectangle) {
y := c.Inner.Min.Y + yAxisLegendIndent + row*height
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(line.label, titleStyle, image.Pt(x, y))

View File

@ -2,9 +2,10 @@ package runchart
import (
"fmt"
"github.com/sqshq/sampler/component/trigger"
"github.com/sqshq/sampler/config"
"github.com/sqshq/sampler/console"
"github.com/sqshq/sampler/data"
"github.com/sqshq/sampler/trigger"
"image"
"math"
"strconv"
@ -37,6 +38,7 @@ const (
type RunChart struct {
ui.Block
data.Consumer
triggers []trigger.Trigger
lines []TimeLine
grid ChartGrid
@ -73,17 +75,35 @@ type ValueExtrema struct {
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.Title = title
return &RunChart{
block.Title = c.Title
chart := RunChart{
Block: block,
Consumer: data.NewConsumer(),
lines: []TimeLine{},
timescale: calculateTimescale(refreshRateMs),
timescale: calculateTimescale(*c.RefreshRateMs),
mutex: &sync.Mutex{},
scale: scale,
scale: *c.Scale,
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)
}
func (c *RunChart) ConsumeSample(sample data.Sample) {
func (c *RunChart) consumeSample(sample data.Sample) {
float, err := strconv.ParseFloat(sample.Value, 64)
if err != nil {
// TODO visual notification + check sample.Error
// TODO visual notification
}
c.mutex.Lock()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,11 +1,22 @@
package data
type Consumer interface {
ConsumeSample(sample Sample)
type Consumer struct {
SampleChannel chan Sample
AlertChannel chan Alert
}
type Sample struct {
Label 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 {
Label *string `yaml:"label,omitempty"`
Script string `yaml:"value"`
Color *ui.Color `yaml:"color,omitempty"`
Label string
Script string
Color ui.Color
}
func (i *Item) nextValue() (value string, err error) {

View File

@ -1,18 +1,20 @@
package data
import (
"github.com/sqshq/sampler/trigger"
"time"
)
type Sampler struct {
consumer Consumer
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)))
sampler := Sampler{consumer, item}
sampler := Sampler{consumer, item, triggers}
go func() {
sampler.sample()
@ -26,6 +28,10 @@ func NewSampler(consumer Consumer, item Item, rateMs int) Sampler {
func (s *Sampler) sample() {
value, err := s.item.nextValue()
sample := Sample{Value: value, Error: err, Label: *s.item.Label}
s.consumer.ConsumeSample(sample)
if err == nil {
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
import (
"github.com/sqshq/sampler/component"
"github.com/sqshq/sampler/component/layout"
"github.com/sqshq/sampler/config"
"github.com/sqshq/sampler/console"
ui "github.com/sqshq/termui"
@ -13,13 +13,13 @@ const (
)
type Handler struct {
layout *component.Layout
layout *layout.Layout
renderTicker *time.Ticker
consoleEvents <-chan ui.Event
renderRate time.Duration
}
func NewHandler(layout *component.Layout) Handler {
func NewHandler(layout *layout.Layout) Handler {
renderRate := calcMinRenderRate(layout)
return Handler{
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
ui.Render(h.layout)
h.renderTicker.Stop()
switch m {
case component.ModeDefault:
case layout.ModeDefault:
h.renderTicker = time.NewTicker(h.renderRate)
case component.ModePause:
case layout.ModePause:
// proceed with stopped timer
default:
h.renderTicker = time.NewTicker(console.MinRenderInterval)
@ -80,7 +80,7 @@ func (h *Handler) handleExit() {
config.Update(settings)
}
func calcMinRenderRate(layout *component.Layout) time.Duration {
func calcMinRenderRate(layout *layout.Layout) time.Duration {
minRefreshRateMs := layout.Components[0].RefreshRateMs
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/barchart"
"github.com/sqshq/sampler/component/gauge"
"github.com/sqshq/sampler/component/layout"
"github.com/sqshq/sampler/component/runchart"
"github.com/sqshq/sampler/config"
"github.com/sqshq/sampler/console"
"github.com/sqshq/sampler/data"
"github.com/sqshq/sampler/event"
"github.com/sqshq/sampler/trigger"
ui "github.com/sqshq/termui"
)
@ -21,47 +23,55 @@ func main() {
defer csl.Close()
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 {
legend := runchart.Legend{Enabled: c.Legend.Enabled, Details: c.Legend.Details}
chart := runchart.NewRunChart(c.Title, *c.Scale, *c.RefreshRateMs, legend)
layout.AddComponent(config.TypeRunChart, chart, c.Title, c.Position, c.Size, *c.RefreshRateMs)
chart := runchart.NewRunChart(c, legend)
lout.AddComponent(config.TypeRunChart, chart, c.Title, c.Position, c.Size, *c.RefreshRateMs)
triggers := trigger.NewTriggers(c.Triggers)
for _, item := range c.Items {
chart.AddLine(*item.Label, *item.Color)
data.NewSampler(chart, item, *c.RefreshRateMs)
for _, i := range c.Items {
item := data.Item{Label: *i.Label, Script: i.Script, Color: *i.Color}
chart.AddLine(item.Label, item.Color)
data.NewSampler(chart.Consumer, item, triggers, *c.RefreshRateMs)
}
}
for _, a := range cfg.AsciiBoxes {
box := asciibox.NewAsciiBox(a.Title, *a.Font, *a.Item.Color)
layout.AddComponent(config.TypeAsciiBox, box, a.Title, a.Position, a.Size, *a.RefreshRateMs)
data.NewSampler(box, a.Item, *a.RefreshRateMs)
box := asciibox.NewAsciiBox(a)
item := data.Item{Label: *a.Label, Script: a.Script, Color: *a.Color}
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 {
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 {
chart.AddBar(*item.Label, *item.Color)
data.NewSampler(chart, item, *b.RefreshRateMs)
for _, i := range b.Items {
item := data.Item{Label: *i.Label, Script: i.Script, Color: *i.Color}
chart.AddBar(*i.Label, *i.Color)
data.NewSampler(chart.Consumer, item, triggers, *b.RefreshRateMs)
}
}
for _, gc := range cfg.Gauges {
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 {
data.NewSampler(g, item, *gc.RefreshRateMs)
for _, i := range gc.Items {
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()
}

View File

@ -32,6 +32,17 @@ type Data struct {
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 {
return Trigger{
title: config.Title,