implemented components size/position save in yaml, defaults for optional settings
This commit is contained in:
parent
94c7d5011e
commit
687e6f098e
100
config.yml
100
config.yml
|
@ -1,78 +1,48 @@
|
|||
theme: dark
|
||||
runcharts:
|
||||
- title: SEARCH ENGINE RESPONSE TIME (sec)
|
||||
items:
|
||||
- label: GOOGLE
|
||||
script: curl -o /dev/null -s -w '%{time_total}' https://www.google.com/
|
||||
- label: YAHOO
|
||||
script: curl -o /dev/null -s -w '%{time_total}' https://search.yahoo.com/
|
||||
- label: BING
|
||||
script: curl -o /dev/null -s -w '%{time_total}' https://www.bing.com/
|
||||
refresh-rate-ms: 200
|
||||
decimal-places: 3
|
||||
alert:
|
||||
value:
|
||||
more-than: 0.231
|
||||
less-than: 0.123
|
||||
equal: 0.144
|
||||
indicator:
|
||||
terminal: true
|
||||
beep: true
|
||||
- title: SEARCH ENGINE RESPONSE TIME (sec)
|
||||
precision: 3
|
||||
position:
|
||||
w: 0
|
||||
h: 0
|
||||
size:
|
||||
w: 30
|
||||
h: 20
|
||||
legend:
|
||||
enabled: true
|
||||
details: true
|
||||
position:
|
||||
x: 0
|
||||
y: 0
|
||||
size:
|
||||
x: 30
|
||||
y: 15
|
||||
- title: SEARCH ENGINE RESPONSE TIME 2 (sec)
|
||||
items:
|
||||
- label: GOOGLE
|
||||
script: curl -o /dev/null -s -w '%{time_total}' https://www.google.com/
|
||||
- label: YAHOO
|
||||
script: curl -o /dev/null -s -w '%{time_total}' https://search.yahoo.com/
|
||||
- label: BING
|
||||
script: curl -o /dev/null -s -w '%{time_total}' https://www.bing.com/
|
||||
- script: curl -o /dev/null -s -w '%{time_total}' https://www.google.com/
|
||||
label: GOOGLE
|
||||
- script: curl -o /dev/null -s -w '%{time_total}' https://search.yahoo.com/
|
||||
label: YAHOO
|
||||
- script: curl -o /dev/null -s -w '%{time_total}' https://www.bing.com/
|
||||
label: BING
|
||||
- title: SEARCH ENGINE RESPONSE TIME 2 (sec)
|
||||
refresh-rate-ms: 5000
|
||||
decimal-places: 3
|
||||
alert:
|
||||
value:
|
||||
more-than: 0.231
|
||||
less-than: 0.123
|
||||
equal: 0.144
|
||||
indicator:
|
||||
terminal: true
|
||||
beep: true
|
||||
position:
|
||||
w: 0
|
||||
h: 20
|
||||
size:
|
||||
w: 15
|
||||
h: 10
|
||||
legend:
|
||||
enabled: true
|
||||
details: false
|
||||
position:
|
||||
x: 0
|
||||
y: 15
|
||||
size:
|
||||
x: 15
|
||||
y: 15
|
||||
- title: MONGO COLLECTIONS COUNT
|
||||
items:
|
||||
- label: POSTS
|
||||
script: mongo --quiet --host=localhost blog --eval "db.getCollection('post').find({}).size()"
|
||||
- script: curl -o /dev/null -s -w '%{time_total}' https://www.google.com/
|
||||
label: GOOGLE
|
||||
- script: curl -o /dev/null -s -w '%{time_total}' https://search.yahoo.com/
|
||||
label: YAHOO
|
||||
- script: curl -o /dev/null -s -w '%{time_total}' https://www.bing.com/
|
||||
label: BING
|
||||
- title: MONGO COLLECTIONS COUNT
|
||||
position:
|
||||
x: 15
|
||||
y: 15
|
||||
w: 15
|
||||
h: 20
|
||||
size:
|
||||
x: 15
|
||||
y: 15
|
||||
barcharts:
|
||||
none
|
||||
sparklines:
|
||||
none
|
||||
gauges:
|
||||
none
|
||||
lists:
|
||||
none
|
||||
textboxes:
|
||||
none
|
||||
asciiboxes:
|
||||
none
|
||||
w: 15
|
||||
h: 10
|
||||
items:
|
||||
- script: mongo --quiet --host=localhost blog --eval "db.getCollection('post').find({}).size()"
|
||||
label: POSTS
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/sqshq/sampler/console"
|
||||
"github.com/sqshq/sampler/data"
|
||||
. "github.com/sqshq/sampler/widgets"
|
||||
"gopkg.in/yaml.v2"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
|
@ -11,18 +11,22 @@ import (
|
|||
)
|
||||
|
||||
type Config struct {
|
||||
Theme console.Theme `yaml:"theme"`
|
||||
RunCharts []RunChartConfig `yaml:"runcharts"`
|
||||
Theme *console.Theme `yaml:"theme,omitempty"`
|
||||
RunCharts []RunChartConfig `yaml:"runcharts,omitempty"`
|
||||
}
|
||||
|
||||
type ComponentConfig struct {
|
||||
Title string `yaml:"title"`
|
||||
RefreshRateMs *int `yaml:"refresh-rate-ms,omitempty"`
|
||||
Precision *int `yaml:"precision,omitempty"`
|
||||
Position Position `yaml:"position"`
|
||||
Size Size `yaml:"size"`
|
||||
}
|
||||
|
||||
type RunChartConfig struct {
|
||||
Title string `yaml:"title"`
|
||||
ComponentConfig `yaml:",inline"`
|
||||
Legend *LegendConfig `yaml:"legend,omitempty"`
|
||||
Items []data.Item `yaml:"items"`
|
||||
Position Position `yaml:"position"`
|
||||
Size Size `yaml:"size"`
|
||||
RefreshRateMs int `yaml:"refresh-rate-ms"`
|
||||
Precision int `yaml:"decimal-places"`
|
||||
Legend LegendConfig `yaml:"legend"`
|
||||
}
|
||||
|
||||
type LegendConfig struct {
|
||||
|
@ -30,6 +34,30 @@ type LegendConfig struct {
|
|||
Details bool `yaml:"details"`
|
||||
}
|
||||
|
||||
type Position struct {
|
||||
X int `yaml:"w"`
|
||||
Y int `yaml:"h"`
|
||||
}
|
||||
|
||||
type Size struct {
|
||||
X int `yaml:"w"`
|
||||
Y int `yaml:"h"`
|
||||
}
|
||||
|
||||
type ComponentType rune
|
||||
|
||||
const (
|
||||
TypeRunChart ComponentType = 0
|
||||
TypeBarChart ComponentType = 1
|
||||
)
|
||||
|
||||
type ComponentSettings struct {
|
||||
Type ComponentType
|
||||
Title string
|
||||
Size Size
|
||||
Position Position
|
||||
}
|
||||
|
||||
func Load() *Config {
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
|
@ -39,13 +67,36 @@ func Load() *Config {
|
|||
|
||||
cfg := readFile(os.Args[1])
|
||||
cfg.validate()
|
||||
cfg.setDefaultValues()
|
||||
cfg.setDefaultColors()
|
||||
cfg.setDefaultLayout()
|
||||
cfg.setDefaults()
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
func Update(settings []ComponentSettings) {
|
||||
cfg := readFile(os.Args[1])
|
||||
for _, s := range settings {
|
||||
componentConfig := cfg.findComponent(s.Type, s.Title)
|
||||
componentConfig.Size = s.Size
|
||||
componentConfig.Position = s.Position
|
||||
}
|
||||
saveFile(cfg)
|
||||
}
|
||||
|
||||
func (c *Config) findComponent(componentType ComponentType, componentTitle string) *ComponentConfig {
|
||||
|
||||
switch componentType {
|
||||
case TypeRunChart:
|
||||
for i, component := range c.RunCharts {
|
||||
if component.Title == componentTitle {
|
||||
return &c.RunCharts[i].ComponentConfig
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf(
|
||||
"Can't find component type %v with title %v", componentType, componentTitle))
|
||||
}
|
||||
|
||||
func readFile(location string) *Config {
|
||||
|
||||
yamlFile, err := ioutil.ReadFile(location)
|
||||
|
@ -62,3 +113,15 @@ func readFile(location string) *Config {
|
|||
|
||||
return cfg
|
||||
}
|
||||
|
||||
func saveFile(config *Config) {
|
||||
file, err := yaml.Marshal(config)
|
||||
if err != nil {
|
||||
log.Fatalf("Can't marshal config file: %v", err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile("config.yml", file, 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("Can't save config file: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,35 +10,48 @@ const (
|
|||
defaultTheme = console.ThemeDark
|
||||
)
|
||||
|
||||
func (self *Config) setDefaultValues() {
|
||||
func (c *Config) setDefaults() {
|
||||
c.setDefaultValues()
|
||||
c.setDefaultColors()
|
||||
c.setDefaultLayout()
|
||||
}
|
||||
|
||||
if len(self.Theme) == 0 {
|
||||
self.Theme = defaultTheme
|
||||
func (c *Config) setDefaultValues() {
|
||||
|
||||
if c.Theme == nil {
|
||||
t := defaultTheme
|
||||
c.Theme = &t
|
||||
}
|
||||
|
||||
for i, chart := range self.RunCharts {
|
||||
if chart.RefreshRateMs == 0 {
|
||||
chart.RefreshRateMs = defaultRefreshRateMs
|
||||
for i, chart := range c.RunCharts {
|
||||
if chart.RefreshRateMs == nil {
|
||||
r := defaultRefreshRateMs
|
||||
chart.RefreshRateMs = &r
|
||||
}
|
||||
if chart.Precision == 0 {
|
||||
chart.Precision = defaultPrecision
|
||||
if chart.Precision == nil {
|
||||
p := defaultPrecision
|
||||
chart.Precision = &p
|
||||
}
|
||||
self.RunCharts[i] = chart
|
||||
if chart.Legend == nil {
|
||||
chart.Legend = &LegendConfig{true, true}
|
||||
c.RunCharts[i] = chart
|
||||
}
|
||||
c.RunCharts[i] = chart
|
||||
}
|
||||
}
|
||||
|
||||
func (config *Config) setDefaultLayout() {
|
||||
func (c *Config) setDefaultLayout() {
|
||||
|
||||
}
|
||||
|
||||
func (config *Config) setDefaultColors() {
|
||||
func (c *Config) setDefaultColors() {
|
||||
|
||||
palette := console.GetPalette(config.Theme)
|
||||
palette := console.GetPalette(*c.Theme)
|
||||
|
||||
for i, chart := range config.RunCharts {
|
||||
for i, chart := range c.RunCharts {
|
||||
for j, item := range chart.Items {
|
||||
if item.Color == 0 {
|
||||
item.Color = palette.Colors[i+j] // TODO handle out of range case
|
||||
if item.Color == nil {
|
||||
item.Color = &palette.Colors[i+j] // TODO handle out of range case
|
||||
chart.Items[j] = item
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,6 @@ package config
|
|||
- title uniquness and mandatory within a single type of widget
|
||||
- label uniqueness and mandatory (if > 1 data bullets)
|
||||
*/
|
||||
func (self *Config) validate() {
|
||||
func (c *Config) validate() {
|
||||
|
||||
}
|
||||
|
|
|
@ -9,12 +9,12 @@ import (
|
|||
type Item struct {
|
||||
Script string `yaml:"script"`
|
||||
Label string `yaml:"label"`
|
||||
Color ui.Color `yaml:"color"`
|
||||
Color *ui.Color `yaml:"color,omitempty"`
|
||||
}
|
||||
|
||||
func (self *Item) nextValue() (value string, err error) {
|
||||
func (i *Item) nextValue() (value string, err error) {
|
||||
|
||||
output, err := exec.Command("sh", "-c", self.Script).Output()
|
||||
output, err := exec.Command("sh", "-c", i.Script).Output()
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
|
@ -11,10 +11,12 @@ type Sampler struct {
|
|||
|
||||
func NewSampler(consumer Consumer, item Item, rateMs int) Sampler {
|
||||
|
||||
ticker := time.NewTicker(time.Duration(rateMs * int(time.Millisecond)))
|
||||
sampler := Sampler{consumer, item}
|
||||
|
||||
go func() {
|
||||
for t := time.Tick(time.Duration(rateMs * int(time.Millisecond))); ; <-t {
|
||||
sampler.sample()
|
||||
for ; true; <-ticker.C {
|
||||
sampler.sample()
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package event
|
||||
|
||||
import (
|
||||
"github.com/sqshq/sampler/config"
|
||||
"github.com/sqshq/sampler/console"
|
||||
"github.com/sqshq/sampler/widgets"
|
||||
ui "github.com/sqshq/termui"
|
||||
|
@ -13,28 +14,38 @@ type Handler struct {
|
|||
ConsoleEvents <-chan ui.Event
|
||||
}
|
||||
|
||||
func (self *Handler) HandleEvents() {
|
||||
func (h *Handler) HandleEvents() {
|
||||
|
||||
pause := false
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-self.RenderEvents:
|
||||
case <-h.RenderEvents:
|
||||
if !pause {
|
||||
ui.Render(self.Layout)
|
||||
ui.Render(h.Layout)
|
||||
}
|
||||
case e := <-self.ConsoleEvents:
|
||||
case e := <-h.ConsoleEvents:
|
||||
switch e.ID {
|
||||
case console.KeyQuit, console.KeyExit:
|
||||
h.handleExit()
|
||||
return
|
||||
case console.KeyPause:
|
||||
pause = !pause
|
||||
case console.SignalResize:
|
||||
payload := e.Payload.(ui.Resize)
|
||||
self.Layout.ChangeDimensions(payload.Width, payload.Height)
|
||||
h.Layout.ChangeDimensions(payload.Width, payload.Height)
|
||||
default:
|
||||
self.Layout.HandleConsoleEvent(e.ID)
|
||||
h.Layout.HandleConsoleEvent(e.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) handleExit() {
|
||||
var settings []config.ComponentSettings
|
||||
for _, c := range h.Layout.Components {
|
||||
settings = append(settings,
|
||||
config.ComponentSettings{Type: c.Type, Title: c.Title, Size: c.Size, Position: c.Position})
|
||||
}
|
||||
config.Update(settings)
|
||||
}
|
||||
|
|
8
main.go
8
main.go
|
@ -24,12 +24,12 @@ func main() {
|
|||
for _, c := range cfg.RunCharts {
|
||||
|
||||
legend := runchart.Legend{Enabled: c.Legend.Enabled, Details: c.Legend.Details}
|
||||
chart := runchart.NewRunChart(c.Title, c.Precision, c.RefreshRateMs, legend)
|
||||
layout.AddComponent(chart, c.Title, c.Position, c.Size, widgets.TypeRunChart)
|
||||
chart := runchart.NewRunChart(c.Title, *c.Precision, *c.RefreshRateMs, legend)
|
||||
layout.AddComponent(chart, c.Title, c.Position, c.Size, config.TypeRunChart)
|
||||
|
||||
for _, item := range c.Items {
|
||||
chart.AddLine(item.Label, item.Color)
|
||||
data.NewSampler(chart, item, c.RefreshRateMs)
|
||||
chart.AddLine(item.Label, *item.Color)
|
||||
data.NewSampler(chart, item, *c.RefreshRateMs)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,32 +1,16 @@
|
|||
package widgets
|
||||
|
||||
import (
|
||||
. "github.com/sqshq/termui"
|
||||
"github.com/sqshq/sampler/config"
|
||||
ui "github.com/sqshq/termui"
|
||||
)
|
||||
|
||||
type Component struct {
|
||||
Drawable Drawable
|
||||
Drawable ui.Drawable
|
||||
Title string
|
||||
Position Position
|
||||
Size Size
|
||||
Type ComponentType
|
||||
}
|
||||
|
||||
type ComponentType rune
|
||||
|
||||
const (
|
||||
TypeRunChart ComponentType = 0
|
||||
TypeBarChart ComponentType = 1
|
||||
)
|
||||
|
||||
type Position struct {
|
||||
X int `yaml:"x"`
|
||||
Y int `yaml:"y"`
|
||||
}
|
||||
|
||||
type Size struct {
|
||||
X int `yaml:"x"`
|
||||
Y int `yaml:"y"`
|
||||
Position config.Position
|
||||
Size config.Size
|
||||
Type config.ComponentType
|
||||
}
|
||||
|
||||
func (c *Component) Move(x, y int) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package widgets
|
||||
|
||||
import (
|
||||
"github.com/sqshq/sampler/config"
|
||||
"github.com/sqshq/sampler/console"
|
||||
"github.com/sqshq/sampler/widgets/runchart"
|
||||
ui "github.com/sqshq/termui"
|
||||
|
@ -8,7 +9,7 @@ import (
|
|||
|
||||
type Layout struct {
|
||||
ui.Block
|
||||
components []Component
|
||||
Components []Component
|
||||
menu *Menu
|
||||
mode Mode
|
||||
selection int
|
||||
|
@ -37,22 +38,22 @@ func NewLayout(width, height int, menu *Menu) *Layout {
|
|||
|
||||
return &Layout{
|
||||
Block: block,
|
||||
components: make([]Component, 0),
|
||||
Components: make([]Component, 0),
|
||||
menu: menu,
|
||||
mode: ModeDefault,
|
||||
selection: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Layout) AddComponent(drawable ui.Drawable, title string, position Position, size Size, Type ComponentType) {
|
||||
l.components = append(l.components, Component{drawable, title, position, size, Type})
|
||||
func (l *Layout) AddComponent(drawable ui.Drawable, title string, position config.Position, size config.Size, Type config.ComponentType) {
|
||||
l.Components = append(l.Components, Component{drawable, title, position, size, Type})
|
||||
}
|
||||
|
||||
func (l *Layout) GetComponents(Type ComponentType) []ui.Drawable {
|
||||
func (l *Layout) GetComponents(Type config.ComponentType) []ui.Drawable {
|
||||
|
||||
var components []ui.Drawable
|
||||
|
||||
for _, component := range l.components {
|
||||
for _, component := range l.Components {
|
||||
if component.Type == Type {
|
||||
components = append(components, component.Drawable)
|
||||
}
|
||||
|
@ -133,7 +134,7 @@ func (l *Layout) HandleConsoleEvent(e string) {
|
|||
chart := l.getSelectedComponent().Drawable.(*runchart.RunChart)
|
||||
chart.MoveSelection(1)
|
||||
case ModeComponentSelect:
|
||||
if l.selection < len(l.components)-1 {
|
||||
if l.selection < len(l.Components)-1 {
|
||||
l.selection++
|
||||
}
|
||||
l.menu.highlight(l.getComponent(l.selection))
|
||||
|
@ -167,7 +168,7 @@ func (l *Layout) HandleConsoleEvent(e string) {
|
|||
l.selection = 0
|
||||
l.menu.highlight(l.getComponent(l.selection))
|
||||
case ModeComponentSelect:
|
||||
if l.selection < len(l.components)-1 {
|
||||
if l.selection < len(l.Components)-1 {
|
||||
l.selection++
|
||||
}
|
||||
l.menu.highlight(l.getComponent(l.selection))
|
||||
|
@ -187,11 +188,11 @@ func (l *Layout) ChangeDimensions(width, height int) {
|
|||
|
||||
// TODO func to get prev/next component navigating left/right/top/bottom
|
||||
func (l *Layout) getComponent(i int) Component {
|
||||
return l.components[i]
|
||||
return l.Components[i]
|
||||
}
|
||||
|
||||
func (l *Layout) getSelectedComponent() *Component {
|
||||
return &l.components[l.selection]
|
||||
return &l.Components[l.selection]
|
||||
}
|
||||
|
||||
func (l *Layout) Draw(buffer *ui.Buffer) {
|
||||
|
@ -199,7 +200,7 @@ func (l *Layout) Draw(buffer *ui.Buffer) {
|
|||
columnWidth := float64(l.GetRect().Dx()) / columnsCount
|
||||
rowHeight := float64(l.GetRect().Dy()) / rowsCount
|
||||
|
||||
for _, component := range l.components {
|
||||
for _, component := range l.Components {
|
||||
|
||||
x1 := float64(component.Position.X) * columnWidth
|
||||
y1 := float64(component.Position.Y) * rowHeight
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package widgets
|
||||
|
||||
import (
|
||||
"github.com/sqshq/sampler/config"
|
||||
"github.com/sqshq/sampler/console"
|
||||
ui "github.com/sqshq/termui"
|
||||
"image"
|
||||
|
@ -191,7 +192,7 @@ func (m *Menu) renderOptions(buffer *ui.Buffer) {
|
|||
style = highlightedStyle
|
||||
}
|
||||
|
||||
if option != MenuOptionPinpoint || m.component.Type == TypeRunChart {
|
||||
if option != MenuOptionPinpoint || m.component.Type == config.TypeRunChart {
|
||||
offset += 2
|
||||
point := getMiddlePoint(m.Block, string(option), offset-5)
|
||||
if point.In(m.GetRect()) {
|
||||
|
|
|
@ -328,8 +328,12 @@ func getMidRangeTime(r TimeRange) time.Time {
|
|||
}
|
||||
|
||||
func formatValue(value float64, precision int) string {
|
||||
if math.Abs(value) == math.MaxFloat64 {
|
||||
return "Inf"
|
||||
} else {
|
||||
format := "%." + strconv.Itoa(precision) + "f"
|
||||
return fmt.Sprintf(format, value)
|
||||
}
|
||||
}
|
||||
|
||||
// time duration between grid lines
|
||||
|
|
Loading…
Reference in New Issue