triggers mechanism integration
This commit is contained in:
parent
9b22083af8
commit
9b487ada89
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -27,7 +27,7 @@ func NewStatusLine(configFileName string) *StatusBar {
|
|||
"(Q) quit",
|
||||
"(P) pause",
|
||||
"(<->) selection",
|
||||
"(ESC) reset alarm",
|
||||
"(ESC) reset alerts",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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()}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
44
main.go
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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,
|
Loading…
Reference in New Issue