implemented components size/position save in yaml, defaults for optional settings

This commit is contained in:
sqshq 2019-02-16 20:54:48 -05:00
parent 94c7d5011e
commit 687e6f098e
12 changed files with 206 additions and 157 deletions

View File

@ -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
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/
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
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()"
position:
x: 15
y: 15
size:
x: 15
y: 15
barcharts:
none
sparklines:
none
gauges:
none
lists:
none
textboxes:
none
asciiboxes:
none
- title: SEARCH ENGINE RESPONSE TIME (sec)
precision: 3
position:
w: 0
h: 0
size:
w: 30
h: 20
legend:
enabled: true
details: true
items:
- 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
position:
w: 0
h: 20
size:
w: 15
h: 10
legend:
enabled: true
details: false
items:
- 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:
w: 15
h: 20
size:
w: 15
h: 10
items:
- script: mongo --quiet --host=localhost blog --eval "db.getCollection('post').find({}).size()"
label: POSTS

View File

@ -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"`
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"`
ComponentConfig `yaml:",inline"`
Legend *LegendConfig `yaml:"legend,omitempty"`
Items []data.Item `yaml:"items"`
}
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)
}
}

View File

@ -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
}
}

View File

@ -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() {
}

View File

@ -7,14 +7,14 @@ import (
)
type Item struct {
Script string `yaml:"script"`
Label string `yaml:"label"`
Color ui.Color `yaml:"color"`
Script string `yaml:"script"`
Label string `yaml:"label"`
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

View File

@ -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()
}
}()

View File

@ -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)
}

View File

@ -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)
}
}

View File

@ -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) {

View File

@ -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

View File

@ -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()) {

View File

@ -328,8 +328,12 @@ func getMidRangeTime(r TimeRange) time.Time {
}
func formatValue(value float64, precision int) string {
format := "%." + strconv.Itoa(precision) + "f"
return fmt.Sprintf(format, value)
if math.Abs(value) == math.MaxFloat64 {
return "Inf"
} else {
format := "%." + strconv.Itoa(precision) + "f"
return fmt.Sprintf(format, value)
}
}
// time duration between grid lines