component functionality rearrangement
This commit is contained in:
parent
fec7eefd9f
commit
78f0c0ed83
|
@ -9,48 +9,26 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Alerter struct {
|
func RenderAlert(alert *data.Alert, area image.Rectangle, buffer *ui.Buffer) {
|
||||||
channel <-chan data.Alert
|
|
||||||
alert *data.Alert
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAlerter(channel <-chan data.Alert) *Alerter {
|
if alert == nil {
|
||||||
alerter := Alerter{channel: channel}
|
|
||||||
alerter.consume()
|
|
||||||
return &alerter
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Alerter) consume() {
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case alert := <-a.channel:
|
|
||||||
a.alert = &alert
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Alerter) RenderAlert(buffer *ui.Buffer, area image.Rectangle) {
|
|
||||||
|
|
||||||
if a.alert == nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
color := console.ColorWhite
|
color := console.ColorWhite
|
||||||
|
|
||||||
if a.alert.Color != nil {
|
if alert.Color != nil {
|
||||||
color = *a.alert.Color
|
color = *alert.Color
|
||||||
}
|
}
|
||||||
|
|
||||||
width := max(len(a.alert.Title), len(a.alert.Text)) + 10
|
width := max(len(alert.Title), len(alert.Text)) + 10
|
||||||
|
|
||||||
if width > area.Dx() {
|
if width > area.Dx() {
|
||||||
width = area.Dx()
|
width = area.Dx()
|
||||||
}
|
}
|
||||||
|
|
||||||
cells := ui.WrapCells(ui.ParseStyles(fmt.Sprintf("%s\n%s\n",
|
cells := ui.WrapCells(ui.ParseStyles(fmt.Sprintf("%s\n%s\n",
|
||||||
strings.ToUpper(a.alert.Title), a.alert.Text), ui.NewStyle(console.ColorWhite)), uint(width))
|
strings.ToUpper(alert.Title), alert.Text), ui.NewStyle(console.ColorWhite)), uint(width))
|
||||||
|
|
||||||
var lines []string
|
var lines []string
|
||||||
line := ""
|
line := ""
|
|
@ -6,11 +6,14 @@ import (
|
||||||
"github.com/sqshq/sampler/asset"
|
"github.com/sqshq/sampler/asset"
|
||||||
"github.com/sqshq/sampler/component"
|
"github.com/sqshq/sampler/component"
|
||||||
"github.com/sqshq/sampler/config"
|
"github.com/sqshq/sampler/config"
|
||||||
|
"github.com/sqshq/sampler/data"
|
||||||
"image"
|
"image"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AsciiBox struct {
|
type AsciiBox struct {
|
||||||
*component.Component
|
*ui.Block
|
||||||
|
*data.Consumer
|
||||||
|
alert *data.Alert
|
||||||
text string
|
text string
|
||||||
ascii string
|
ascii string
|
||||||
style ui.Style
|
style ui.Style
|
||||||
|
@ -34,7 +37,8 @@ func NewAsciiBox(c config.AsciiBoxConfig) *AsciiBox {
|
||||||
_ = render.LoadBindataFont(fontStr, options.FontName)
|
_ = render.LoadBindataFont(fontStr, options.FontName)
|
||||||
|
|
||||||
box := AsciiBox{
|
box := AsciiBox{
|
||||||
Component: component.NewComponent(c.ComponentConfig, config.TypeAsciiBox),
|
Block: component.NewBlock(c.Title, true),
|
||||||
|
Consumer: data.NewConsumer(),
|
||||||
style: ui.NewStyle(*c.Color),
|
style: ui.NewStyle(*c.Color),
|
||||||
render: render,
|
render: render,
|
||||||
options: options,
|
options: options,
|
||||||
|
@ -46,6 +50,8 @@ func NewAsciiBox(c config.AsciiBoxConfig) *AsciiBox {
|
||||||
case sample := <-box.SampleChannel:
|
case sample := <-box.SampleChannel:
|
||||||
box.text = sample.Value
|
box.text = sample.Value
|
||||||
box.ascii, _ = box.render.RenderOpts(sample.Value, box.options)
|
box.ascii, _ = box.render.RenderOpts(sample.Value, box.options)
|
||||||
|
case alert := <-box.AlertChannel:
|
||||||
|
box.alert = alert
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -69,4 +75,6 @@ func (a *AsciiBox) Draw(buffer *ui.Buffer) {
|
||||||
point = point.Add(image.Pt(1, 0))
|
point = point.Add(image.Pt(1, 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
component.RenderAlert(a.alert, a.Rectangle, buffer)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,9 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type BarChart struct {
|
type BarChart struct {
|
||||||
*component.Component
|
*ui.Block
|
||||||
|
*data.Consumer
|
||||||
|
alert *data.Alert
|
||||||
bars []Bar
|
bars []Bar
|
||||||
scale int
|
scale int
|
||||||
maxValue float64
|
maxValue float64
|
||||||
|
@ -35,7 +37,8 @@ type Bar struct {
|
||||||
func NewBarChart(c config.BarChartConfig) *BarChart {
|
func NewBarChart(c config.BarChartConfig) *BarChart {
|
||||||
|
|
||||||
chart := BarChart{
|
chart := BarChart{
|
||||||
Component: component.NewComponent(c.ComponentConfig, config.TypeBarChart),
|
Block: component.NewBlock(c.Title, true),
|
||||||
|
Consumer: data.NewConsumer(),
|
||||||
bars: []Bar{},
|
bars: []Bar{},
|
||||||
scale: *c.Scale,
|
scale: *c.Scale,
|
||||||
maxValue: -math.MaxFloat64,
|
maxValue: -math.MaxFloat64,
|
||||||
|
@ -50,6 +53,8 @@ func NewBarChart(c config.BarChartConfig) *BarChart {
|
||||||
select {
|
select {
|
||||||
case sample := <-chart.SampleChannel:
|
case sample := <-chart.SampleChannel:
|
||||||
chart.consumeSample(sample)
|
chart.consumeSample(sample)
|
||||||
|
case alert := <-chart.AlertChannel:
|
||||||
|
chart.alert = alert
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -57,13 +62,19 @@ func NewBarChart(c config.BarChartConfig) *BarChart {
|
||||||
return &chart
|
return &chart
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BarChart) consumeSample(sample data.Sample) {
|
func (b *BarChart) consumeSample(sample *data.Sample) {
|
||||||
|
|
||||||
b.count++
|
b.count++
|
||||||
|
|
||||||
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
|
b.AlertChannel <- &data.Alert{
|
||||||
|
Title: "FAILED TO PARSE NUMBER",
|
||||||
|
Text: err.Error(),
|
||||||
|
Color: sample.Color,
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
index := -1
|
index := -1
|
||||||
|
@ -102,8 +113,8 @@ func (b *BarChart) reselectMaxValue() {
|
||||||
b.maxValue = maxValue
|
b.maxValue = maxValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BarChart) Draw(buf *ui.Buffer) {
|
func (b *BarChart) Draw(buffer *ui.Buffer) {
|
||||||
b.Block.Draw(buf)
|
b.Block.Draw(buffer)
|
||||||
|
|
||||||
barWidth := int(math.Ceil(float64(b.Inner.Dx()-2*barIndent-len(b.bars)*barIndent) / float64(len(b.bars))))
|
barWidth := int(math.Ceil(float64(b.Inner.Dx()-2*barIndent-len(b.bars)*barIndent) / float64(len(b.bars))))
|
||||||
barXCoordinate := b.Inner.Min.X + barIndent
|
barXCoordinate := b.Inner.Min.X + barIndent
|
||||||
|
@ -122,7 +133,7 @@ func (b *BarChart) Draw(buf *ui.Buffer) {
|
||||||
for x := barXCoordinate; x < ui.MinInt(barXCoordinate+barWidth, b.Inner.Max.X-barIndent); x++ {
|
for x := barXCoordinate; x < ui.MinInt(barXCoordinate+barWidth, b.Inner.Max.X-barIndent); x++ {
|
||||||
for y := b.Inner.Max.Y - 2; y >= maxYCoordinate; y-- {
|
for y := b.Inner.Max.Y - 2; y >= maxYCoordinate; y-- {
|
||||||
c := ui.NewCell(console.SymbolShade, ui.NewStyle(bar.color))
|
c := ui.NewCell(console.SymbolShade, ui.NewStyle(bar.color))
|
||||||
buf.SetCell(c, image.Pt(x, y))
|
buffer.SetCell(c, image.Pt(x, y))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +141,7 @@ func (b *BarChart) Draw(buf *ui.Buffer) {
|
||||||
labelXCoordinate := barXCoordinate +
|
labelXCoordinate := barXCoordinate +
|
||||||
int(float64(barWidth)/2) -
|
int(float64(barWidth)/2) -
|
||||||
int(float64(rw.StringWidth(bar.label))/2)
|
int(float64(rw.StringWidth(bar.label))/2)
|
||||||
buf.SetString(
|
buffer.SetString(
|
||||||
bar.label,
|
bar.label,
|
||||||
labelStyle,
|
labelStyle,
|
||||||
image.Pt(labelXCoordinate, b.Inner.Max.Y-1))
|
image.Pt(labelXCoordinate, b.Inner.Max.Y-1))
|
||||||
|
@ -143,13 +154,15 @@ func (b *BarChart) Draw(buf *ui.Buffer) {
|
||||||
valueXCoordinate := barXCoordinate +
|
valueXCoordinate := barXCoordinate +
|
||||||
int(float64(barWidth)/2) -
|
int(float64(barWidth)/2) -
|
||||||
int(float64(rw.StringWidth(value))/2)
|
int(float64(rw.StringWidth(value))/2)
|
||||||
buf.SetString(
|
buffer.SetString(
|
||||||
value,
|
value,
|
||||||
labelStyle,
|
labelStyle,
|
||||||
image.Pt(valueXCoordinate, maxYCoordinate-1))
|
image.Pt(valueXCoordinate, maxYCoordinate-1))
|
||||||
|
|
||||||
barXCoordinate += barWidth + barIndent
|
barXCoordinate += barWidth + barIndent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
component.RenderAlert(b.alert, b.Rectangle, buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO extract to utils
|
// TODO extract to utils
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
package component
|
||||||
|
|
||||||
|
import (
|
||||||
|
ui "github.com/gizak/termui/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewBlock(title string, border bool) *ui.Block {
|
||||||
|
block := ui.NewBlock()
|
||||||
|
block.Title = title
|
||||||
|
block.Border = border
|
||||||
|
return block
|
||||||
|
}
|
|
@ -7,9 +7,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Component struct {
|
type Component struct {
|
||||||
ui.Block
|
ui.Drawable
|
||||||
data.Consumer
|
*data.Consumer
|
||||||
*Alerter
|
|
||||||
Type config.ComponentType
|
Type config.ComponentType
|
||||||
Title string
|
Title string
|
||||||
Position config.Position
|
Position config.Position
|
||||||
|
@ -17,21 +16,15 @@ type Component struct {
|
||||||
RefreshRateMs int
|
RefreshRateMs int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewComponent(c config.ComponentConfig, t config.ComponentType) *Component {
|
func NewComponent(dbl ui.Drawable, cmr *data.Consumer, cfg config.ComponentConfig, ct config.ComponentType) *Component {
|
||||||
|
|
||||||
consumer := data.NewConsumer()
|
|
||||||
block := *ui.NewBlock()
|
|
||||||
block.Title = c.Title
|
|
||||||
|
|
||||||
return &Component{
|
return &Component{
|
||||||
Block: block,
|
Drawable: dbl,
|
||||||
Consumer: consumer,
|
Consumer: cmr,
|
||||||
Alerter: NewAlerter(consumer.AlertChannel),
|
Type: ct,
|
||||||
Type: t,
|
Title: cfg.Title,
|
||||||
Title: c.Title,
|
Position: cfg.Position,
|
||||||
Position: c.Position,
|
Size: cfg.Size,
|
||||||
Size: c.Size,
|
RefreshRateMs: *cfg.RefreshRateMs,
|
||||||
RefreshRateMs: *c.RefreshRateMs,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,9 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Gauge struct {
|
type Gauge struct {
|
||||||
*component.Component
|
*ui.Block
|
||||||
|
*data.Consumer
|
||||||
|
alert *data.Alert
|
||||||
minValue float64
|
minValue float64
|
||||||
maxValue float64
|
maxValue float64
|
||||||
curValue float64
|
curValue float64
|
||||||
|
@ -30,7 +32,8 @@ type Gauge struct {
|
||||||
func NewGauge(c config.GaugeConfig) *Gauge {
|
func NewGauge(c config.GaugeConfig) *Gauge {
|
||||||
|
|
||||||
gauge := Gauge{
|
gauge := Gauge{
|
||||||
Component: component.NewComponent(c.ComponentConfig, config.TypeGauge),
|
Block: component.NewBlock(c.Title, true),
|
||||||
|
Consumer: data.NewConsumer(),
|
||||||
scale: *c.Scale,
|
scale: *c.Scale,
|
||||||
color: *c.Color,
|
color: *c.Color,
|
||||||
}
|
}
|
||||||
|
@ -40,6 +43,8 @@ func NewGauge(c config.GaugeConfig) *Gauge {
|
||||||
select {
|
select {
|
||||||
case sample := <-gauge.SampleChannel:
|
case sample := <-gauge.SampleChannel:
|
||||||
gauge.ConsumeSample(sample)
|
gauge.ConsumeSample(sample)
|
||||||
|
case alert := <-gauge.AlertChannel:
|
||||||
|
gauge.alert = alert
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -47,11 +52,16 @@ func NewGauge(c config.GaugeConfig) *Gauge {
|
||||||
return &gauge
|
return &gauge
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Gauge) ConsumeSample(sample data.Sample) {
|
func (g *Gauge) 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 handle in Component
|
g.AlertChannel <- &data.Alert{
|
||||||
|
Title: "FAILED TO PARSE NUMBER",
|
||||||
|
Text: err.Error(),
|
||||||
|
Color: sample.Color,
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch sample.Label {
|
switch sample.Label {
|
||||||
|
@ -67,9 +77,9 @@ func (g *Gauge) ConsumeSample(sample data.Sample) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Gauge) Draw(buf *ui.Buffer) {
|
func (g *Gauge) Draw(buffer *ui.Buffer) {
|
||||||
|
|
||||||
g.Block.Draw(buf)
|
g.Block.Draw(buffer)
|
||||||
|
|
||||||
percent := 0.0
|
percent := 0.0
|
||||||
if g.curValue != 0 && g.maxValue != g.minValue {
|
if g.curValue != 0 && g.maxValue != g.minValue {
|
||||||
|
@ -85,7 +95,7 @@ func (g *Gauge) Draw(buf *ui.Buffer) {
|
||||||
} else if barWidth > g.Dx()-2 {
|
} else if barWidth > g.Dx()-2 {
|
||||||
barWidth = g.Dx() - 2
|
barWidth = g.Dx() - 2
|
||||||
}
|
}
|
||||||
buf.Fill(
|
buffer.Fill(
|
||||||
ui.NewCell(console.SymbolVerticalBar, ui.NewStyle(g.color)),
|
ui.NewCell(console.SymbolVerticalBar, ui.NewStyle(g.color)),
|
||||||
image.Rect(g.Inner.Min.X+1, g.Inner.Min.Y, g.Inner.Min.X+barWidth, g.Inner.Max.Y),
|
image.Rect(g.Inner.Min.X+1, g.Inner.Min.Y, g.Inner.Min.X+barWidth, g.Inner.Max.Y),
|
||||||
)
|
)
|
||||||
|
@ -99,9 +109,11 @@ func (g *Gauge) Draw(buf *ui.Buffer) {
|
||||||
if labelXCoordinate+i+1 <= g.Inner.Min.X+barWidth {
|
if labelXCoordinate+i+1 <= g.Inner.Min.X+barWidth {
|
||||||
style = ui.NewStyle(console.ColorWhite, ui.ColorClear)
|
style = ui.NewStyle(console.ColorWhite, ui.ColorClear)
|
||||||
}
|
}
|
||||||
buf.SetCell(ui.NewCell(char, style), image.Pt(labelXCoordinate+i, labelYCoordinate))
|
buffer.SetCell(ui.NewCell(char, style), image.Pt(labelXCoordinate+i, labelYCoordinate))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
component.RenderAlert(g.alert, g.Rectangle, buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO extract to utils
|
// TODO extract to utils
|
||||||
|
|
|
@ -57,7 +57,7 @@ func NewLayout(width, height int, statusline *component.StatusBar, menu *compone
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Layout) AddComponent(cpt *component.Component, Type config.ComponentType) {
|
func (l *Layout) AddComponent(cpt *component.Component) {
|
||||||
l.Components = append(l.Components, cpt)
|
l.Components = append(l.Components, cpt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ func (l *Layout) HandleConsoleEvent(e string) {
|
||||||
l.changeMode(ModeDefault)
|
l.changeMode(ModeDefault)
|
||||||
} else {
|
} else {
|
||||||
if selected.Type == config.TypeRunChart {
|
if selected.Type == config.TypeRunChart {
|
||||||
selected.CommandChannel <- data.Command{Type: runchart.CommandDisableSelection}
|
selected.CommandChannel <- &data.Command{Type: runchart.CommandDisableSelection}
|
||||||
}
|
}
|
||||||
l.menu.Idle()
|
l.menu.Idle()
|
||||||
l.changeMode(ModePause)
|
l.changeMode(ModePause)
|
||||||
|
@ -98,7 +98,7 @@ func (l *Layout) HandleConsoleEvent(e string) {
|
||||||
case component.MenuOptionPinpoint:
|
case component.MenuOptionPinpoint:
|
||||||
l.changeMode(ModeChartPinpoint)
|
l.changeMode(ModeChartPinpoint)
|
||||||
l.menu.Idle()
|
l.menu.Idle()
|
||||||
selected.CommandChannel <- data.Command{Type: runchart.CommandMoveSelection, Value: 0}
|
selected.CommandChannel <- &data.Command{Type: runchart.CommandMoveSelection, Value: 0}
|
||||||
case component.MenuOptionResume:
|
case component.MenuOptionResume:
|
||||||
l.changeMode(ModeDefault)
|
l.changeMode(ModeDefault)
|
||||||
l.menu.Idle()
|
l.menu.Idle()
|
||||||
|
@ -112,7 +112,7 @@ func (l *Layout) HandleConsoleEvent(e string) {
|
||||||
case console.KeyEsc:
|
case console.KeyEsc:
|
||||||
switch l.mode {
|
switch l.mode {
|
||||||
case ModeChartPinpoint:
|
case ModeChartPinpoint:
|
||||||
selected.CommandChannel <- data.Command{Type: runchart.CommandDisableSelection}
|
selected.CommandChannel <- &data.Command{Type: runchart.CommandDisableSelection}
|
||||||
fallthrough
|
fallthrough
|
||||||
case ModeComponentSelect:
|
case ModeComponentSelect:
|
||||||
fallthrough
|
fallthrough
|
||||||
|
@ -126,7 +126,7 @@ func (l *Layout) HandleConsoleEvent(e string) {
|
||||||
l.changeMode(ModeComponentSelect)
|
l.changeMode(ModeComponentSelect)
|
||||||
l.menu.Highlight(l.getComponent(l.selection))
|
l.menu.Highlight(l.getComponent(l.selection))
|
||||||
case ModeChartPinpoint:
|
case ModeChartPinpoint:
|
||||||
selected.CommandChannel <- data.Command{Type: runchart.CommandMoveSelection, Value: -1}
|
selected.CommandChannel <- &data.Command{Type: runchart.CommandMoveSelection, Value: -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))
|
||||||
|
@ -141,7 +141,7 @@ func (l *Layout) HandleConsoleEvent(e string) {
|
||||||
l.changeMode(ModeComponentSelect)
|
l.changeMode(ModeComponentSelect)
|
||||||
l.menu.Highlight(l.getComponent(l.selection))
|
l.menu.Highlight(l.getComponent(l.selection))
|
||||||
case ModeChartPinpoint:
|
case ModeChartPinpoint:
|
||||||
selected.CommandChannel <- data.Command{Type: runchart.CommandMoveSelection, Value: 1}
|
selected.CommandChannel <- &data.Command{Type: runchart.CommandMoveSelection, Value: 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))
|
||||||
|
|
|
@ -219,7 +219,7 @@ func (m *Menu) renderOptions(buffer *ui.Buffer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Menu) updateDimensions() {
|
func (m *Menu) updateDimensions() {
|
||||||
r := m.component.Block.GetRect()
|
r := m.component.GetRect()
|
||||||
m.SetRect(r.Min.X, r.Min.Y, r.Max.X, r.Max.Y)
|
m.SetRect(r.Min.X, r.Min.Y, r.Max.X, r.Max.Y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,9 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type RunChart struct {
|
type RunChart struct {
|
||||||
*component.Component
|
*ui.Block
|
||||||
|
*data.Consumer
|
||||||
|
alert *data.Alert
|
||||||
lines []TimeLine
|
lines []TimeLine
|
||||||
grid ChartGrid
|
grid ChartGrid
|
||||||
timescale time.Duration
|
timescale time.Duration
|
||||||
|
@ -79,7 +81,8 @@ type ValueExtrema struct {
|
||||||
func NewRunChart(c config.RunChartConfig) *RunChart {
|
func NewRunChart(c config.RunChartConfig) *RunChart {
|
||||||
|
|
||||||
chart := RunChart{
|
chart := RunChart{
|
||||||
Component: component.NewComponent(c.ComponentConfig, config.TypeRunChart),
|
Block: component.NewBlock(c.Title, true),
|
||||||
|
Consumer: data.NewConsumer(),
|
||||||
lines: []TimeLine{},
|
lines: []TimeLine{},
|
||||||
timescale: calculateTimescale(*c.RefreshRateMs),
|
timescale: calculateTimescale(*c.RefreshRateMs),
|
||||||
mutex: &sync.Mutex{},
|
mutex: &sync.Mutex{},
|
||||||
|
@ -97,6 +100,8 @@ func NewRunChart(c config.RunChartConfig) *RunChart {
|
||||||
select {
|
select {
|
||||||
case sample := <-chart.SampleChannel:
|
case sample := <-chart.SampleChannel:
|
||||||
chart.consumeSample(sample)
|
chart.consumeSample(sample)
|
||||||
|
case alert := <-chart.AlertChannel:
|
||||||
|
chart.alert = alert
|
||||||
case command := <-chart.CommandChannel:
|
case command := <-chart.CommandChannel:
|
||||||
switch command.Type {
|
switch command.Type {
|
||||||
case CommandDisableSelection:
|
case CommandDisableSelection:
|
||||||
|
@ -134,7 +139,7 @@ func (c *RunChart) Draw(buffer *ui.Buffer) {
|
||||||
c.renderAxes(buffer)
|
c.renderAxes(buffer)
|
||||||
c.renderLines(buffer, drawArea)
|
c.renderLines(buffer, drawArea)
|
||||||
c.renderLegend(buffer, drawArea)
|
c.renderLegend(buffer, drawArea)
|
||||||
c.RenderAlert(buffer, c.Rectangle)
|
component.RenderAlert(c.alert, c.Rectangle, buffer)
|
||||||
c.mutex.Unlock()
|
c.mutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,16 +153,17 @@ 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 {
|
||||||
c.AlertChannel <- data.Alert{
|
c.AlertChannel <- &data.Alert{
|
||||||
Title: "SAMPLING FAILURE",
|
Title: "FAILED TO PARSE NUMBER",
|
||||||
Text: err.Error(),
|
Text: err.Error(),
|
||||||
Color: sample.Color,
|
Color: sample.Color,
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.mutex.Lock()
|
c.mutex.Lock()
|
||||||
|
|
|
@ -2,11 +2,10 @@ package data
|
||||||
|
|
||||||
import ui "github.com/gizak/termui/v3"
|
import ui "github.com/gizak/termui/v3"
|
||||||
|
|
||||||
// TODO interface here, move fields declaration in the Component
|
|
||||||
type Consumer struct {
|
type Consumer struct {
|
||||||
SampleChannel chan Sample
|
SampleChannel chan *Sample
|
||||||
AlertChannel chan Alert
|
AlertChannel chan *Alert
|
||||||
CommandChannel chan Command
|
CommandChannel chan *Command
|
||||||
}
|
}
|
||||||
|
|
||||||
type Sample struct {
|
type Sample struct {
|
||||||
|
@ -26,10 +25,10 @@ type Command struct {
|
||||||
Value interface{}
|
Value interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConsumer() Consumer {
|
func NewConsumer() *Consumer {
|
||||||
return Consumer{
|
return &Consumer{
|
||||||
SampleChannel: make(chan Sample),
|
SampleChannel: make(chan *Sample),
|
||||||
AlertChannel: make(chan Alert),
|
AlertChannel: make(chan *Alert),
|
||||||
CommandChannel: make(chan Command),
|
CommandChannel: make(chan *Command),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Sampler struct {
|
type Sampler struct {
|
||||||
consumer Consumer
|
consumer *Consumer
|
||||||
items []Item
|
items []Item
|
||||||
triggers []Trigger
|
triggers []Trigger
|
||||||
triggersChannel chan Sample
|
triggersChannel chan *Sample
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSampler(consumer Consumer, items []Item, triggers []Trigger, rateMs int) Sampler {
|
func NewSampler(consumer *Consumer, items []Item, triggers []Trigger, rateMs int) Sampler {
|
||||||
|
|
||||||
ticker := time.NewTicker(
|
ticker := time.NewTicker(
|
||||||
time.Duration(rateMs * int(time.Millisecond)),
|
time.Duration(rateMs * int(time.Millisecond)),
|
||||||
|
@ -21,7 +21,7 @@ func NewSampler(consumer Consumer, items []Item, triggers []Trigger, rateMs int)
|
||||||
consumer,
|
consumer,
|
||||||
items,
|
items,
|
||||||
triggers,
|
triggers,
|
||||||
make(chan Sample),
|
make(chan *Sample),
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -51,11 +51,11 @@ func (s *Sampler) sample(item Item) {
|
||||||
val, err := item.nextValue()
|
val, err := item.nextValue()
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
sample := Sample{Label: item.Label, Value: val, Color: item.Color}
|
sample := &Sample{Label: item.Label, Value: val, Color: item.Color}
|
||||||
s.consumer.SampleChannel <- sample
|
s.consumer.SampleChannel <- sample
|
||||||
s.triggersChannel <- sample
|
s.triggersChannel <- sample
|
||||||
} else {
|
} else {
|
||||||
s.consumer.AlertChannel <- Alert{
|
s.consumer.AlertChannel <- &Alert{
|
||||||
Title: "SAMPLING FAILURE",
|
Title: "SAMPLING FAILURE",
|
||||||
Text: err.Error(),
|
Text: err.Error(),
|
||||||
Color: item.Color,
|
Color: item.Color,
|
||||||
|
|
|
@ -18,8 +18,8 @@ const (
|
||||||
type Trigger struct {
|
type Trigger struct {
|
||||||
title string
|
title string
|
||||||
condition string
|
condition string
|
||||||
actions Actions
|
actions *Actions
|
||||||
consumer Consumer
|
consumer *Consumer
|
||||||
valuesByLabel map[string]Values
|
valuesByLabel map[string]Values
|
||||||
player *asset.AudioPlayer
|
player *asset.AudioPlayer
|
||||||
digitsRegexp *regexp.Regexp
|
digitsRegexp *regexp.Regexp
|
||||||
|
@ -37,7 +37,7 @@ type Values struct {
|
||||||
previous string
|
previous string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTriggers(cfgs []config.TriggerConfig, consumer Consumer, player *asset.AudioPlayer) []Trigger {
|
func NewTriggers(cfgs []config.TriggerConfig, consumer *Consumer, player *asset.AudioPlayer) []Trigger {
|
||||||
|
|
||||||
triggers := make([]Trigger, 0)
|
triggers := make([]Trigger, 0)
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ func NewTriggers(cfgs []config.TriggerConfig, consumer Consumer, player *asset.A
|
||||||
return triggers
|
return triggers
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTrigger(config config.TriggerConfig, consumer Consumer, player *asset.AudioPlayer) Trigger {
|
func NewTrigger(config config.TriggerConfig, consumer *Consumer, player *asset.AudioPlayer) Trigger {
|
||||||
return Trigger{
|
return Trigger{
|
||||||
title: config.Title,
|
title: config.Title,
|
||||||
condition: config.Condition,
|
condition: config.Condition,
|
||||||
|
@ -56,7 +56,7 @@ func NewTrigger(config config.TriggerConfig, consumer Consumer, player *asset.Au
|
||||||
valuesByLabel: make(map[string]Values),
|
valuesByLabel: make(map[string]Values),
|
||||||
player: player,
|
player: player,
|
||||||
digitsRegexp: regexp.MustCompile("[^0-9]+"),
|
digitsRegexp: regexp.MustCompile("[^0-9]+"),
|
||||||
actions: Actions{
|
actions: &Actions{
|
||||||
terminalBell: *config.Actions.TerminalBell,
|
terminalBell: *config.Actions.TerminalBell,
|
||||||
sound: *config.Actions.Sound,
|
sound: *config.Actions.Sound,
|
||||||
visual: *config.Actions.Visual,
|
visual: *config.Actions.Visual,
|
||||||
|
@ -65,7 +65,7 @@ func NewTrigger(config config.TriggerConfig, consumer Consumer, player *asset.Au
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Trigger) Execute(sample Sample) {
|
func (t *Trigger) Execute(sample *Sample) {
|
||||||
if t.evaluate(sample) {
|
if t.evaluate(sample) {
|
||||||
|
|
||||||
if t.actions.terminalBell {
|
if t.actions.terminalBell {
|
||||||
|
@ -77,7 +77,7 @@ func (t *Trigger) Execute(sample Sample) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.actions.visual {
|
if t.actions.visual {
|
||||||
t.consumer.AlertChannel <- Alert{
|
t.consumer.AlertChannel <- &Alert{
|
||||||
Title: t.title,
|
Title: t.title,
|
||||||
Text: fmt.Sprintf("%s: %v", sample.Label, sample.Value),
|
Text: fmt.Sprintf("%s: %v", sample.Label, sample.Value),
|
||||||
Color: sample.Color,
|
Color: sample.Color,
|
||||||
|
@ -90,7 +90,7 @@ func (t *Trigger) Execute(sample Sample) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Trigger) evaluate(sample Sample) bool {
|
func (t *Trigger) evaluate(sample *Sample) bool {
|
||||||
|
|
||||||
if values, ok := t.valuesByLabel[sample.Label]; ok {
|
if values, ok := t.valuesByLabel[sample.Label]; ok {
|
||||||
values.previous = values.current
|
values.previous = values.current
|
||||||
|
@ -103,7 +103,7 @@ func (t *Trigger) evaluate(sample Sample) bool {
|
||||||
output, err := runScript(t.condition, sample.Label, t.valuesByLabel[sample.Label])
|
output, err := runScript(t.condition, sample.Label, t.valuesByLabel[sample.Label])
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.consumer.AlertChannel <- Alert{Title: "TRIGGER CONDITION FAILURE", Text: err.Error()}
|
t.consumer.AlertChannel <- &Alert{Title: "TRIGGER CONDITION FAILURE", Text: err.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
return t.digitsRegexp.ReplaceAllString(string(output), "") == TrueIndicator
|
return t.digitsRegexp.ReplaceAllString(string(output), "") == TrueIndicator
|
||||||
|
|
12
main.go
12
main.go
|
@ -30,30 +30,34 @@ func main() {
|
||||||
|
|
||||||
for _, c := range cfg.RunCharts {
|
for _, c := range cfg.RunCharts {
|
||||||
chart := runchart.NewRunChart(c)
|
chart := runchart.NewRunChart(c)
|
||||||
|
cpt := component.NewComponent(chart, chart.Consumer, c.ComponentConfig, config.TypeRunChart)
|
||||||
triggers := data.NewTriggers(c.Triggers, chart.Consumer, player)
|
triggers := data.NewTriggers(c.Triggers, chart.Consumer, player)
|
||||||
data.NewSampler(chart.Consumer, data.NewItems(c.Items), triggers, *c.RefreshRateMs)
|
data.NewSampler(chart.Consumer, data.NewItems(c.Items), triggers, *c.RefreshRateMs)
|
||||||
lout.AddComponent(chart.Component, config.TypeRunChart)
|
lout.AddComponent(cpt)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, a := range cfg.AsciiBoxes {
|
for _, a := range cfg.AsciiBoxes {
|
||||||
box := asciibox.NewAsciiBox(a)
|
box := asciibox.NewAsciiBox(a)
|
||||||
|
cpt := component.NewComponent(box, box.Consumer, a.ComponentConfig, config.TypeRunChart)
|
||||||
triggers := data.NewTriggers(a.Triggers, box.Consumer, player)
|
triggers := data.NewTriggers(a.Triggers, box.Consumer, player)
|
||||||
data.NewSampler(box.Consumer, data.NewItems([]config.Item{a.Item}), triggers, *a.RefreshRateMs)
|
data.NewSampler(box.Consumer, data.NewItems([]config.Item{a.Item}), triggers, *a.RefreshRateMs)
|
||||||
lout.AddComponent(box.Component, config.TypeAsciiBox)
|
lout.AddComponent(cpt)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, b := range cfg.BarCharts {
|
for _, b := range cfg.BarCharts {
|
||||||
chart := barchart.NewBarChart(b)
|
chart := barchart.NewBarChart(b)
|
||||||
|
cpt := component.NewComponent(chart, chart.Consumer, b.ComponentConfig, config.TypeRunChart)
|
||||||
triggers := data.NewTriggers(b.Triggers, chart.Consumer, player)
|
triggers := data.NewTriggers(b.Triggers, chart.Consumer, player)
|
||||||
data.NewSampler(chart.Consumer, data.NewItems(b.Items), triggers, *b.RefreshRateMs)
|
data.NewSampler(chart.Consumer, data.NewItems(b.Items), triggers, *b.RefreshRateMs)
|
||||||
lout.AddComponent(chart.Component, config.TypeBarChart)
|
lout.AddComponent(cpt)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, gc := range cfg.Gauges {
|
for _, gc := range cfg.Gauges {
|
||||||
g := gauge.NewGauge(gc)
|
g := gauge.NewGauge(gc)
|
||||||
|
cpt := component.NewComponent(g, g.Consumer, gc.ComponentConfig, config.TypeRunChart)
|
||||||
triggers := data.NewTriggers(gc.Triggers, g.Consumer, player)
|
triggers := data.NewTriggers(gc.Triggers, g.Consumer, player)
|
||||||
data.NewSampler(g.Consumer, data.NewItems(gc.Items), triggers, *gc.RefreshRateMs)
|
data.NewSampler(g.Consumer, data.NewItems(gc.Items), triggers, *gc.RefreshRateMs)
|
||||||
lout.AddComponent(g.Component, config.TypeGauge)
|
lout.AddComponent(cpt)
|
||||||
}
|
}
|
||||||
|
|
||||||
handler := event.NewHandler(lout)
|
handler := event.NewHandler(lout)
|
||||||
|
|
Loading…
Reference in New Issue