Moved sparklines data (stacks) to UI data structure

This commit is contained in:
Ivan Daniluk 2016-11-10 22:34:31 +01:00
parent 93da8b7853
commit 555bff1b9d
6 changed files with 102 additions and 86 deletions

12
data.go
View File

@ -7,11 +7,21 @@ type UIData struct {
Services []*Service
Vars []VarName
LastTimestamp time.Time
Stacks map[VarName]*Stack
Stats map[VarName]*Stat
}
// NewUIData inits and return new data object.
func NewUIData(vars []VarName) *UIData {
stacks := make(map[VarName]*Stack)
stats := make(map[VarName]*Stat)
for _, v := range vars {
stacks[v] = NewStack()
stats[v] = NewStat()
}
return &UIData{
Vars: vars,
Vars: vars,
Stacks: stacks,
Stats: stats,
}
}

View File

@ -8,9 +8,8 @@ const DefaultSize = 1200
// Stack is a limited FIFO for holding sparkline values.
type Stack struct {
Values []Var
Values []int
Len int
Max Var
}
// NewStack inits new Stack with default size limit.
@ -21,88 +20,26 @@ func NewStack() *Stack {
// NewStackWithSize inits new Stack with size limit.
func NewStackWithSize(size int) *Stack {
return &Stack{
Values: make([]Var, size),
Values: make([]int, size),
Len: size,
}
}
/*
TODO: FIXME: review or remove this code
// Push inserts data to stack, preserving constant length.
func (s *Stack) Push(val Var) {
func (s *Stack) Push(v IntVar) {
val := v.Value()
s.Values = append(s.Values, val)
if len(s.Values) > s.Len {
// TODO: check if underlying array is growing constantly
s.Values = s.Values[1:]
}
if s.Max == nil {
s.Max = val
return
}
switch val.(type) {
case int64:
switch s.Max.(type) {
case int64:
if val.(int64) > s.Max.(int64) {
s.Max = val
}
case float64:
if float64(val.(int64)) > s.Max.(float64) {
s.Max = val
}
}
case float64:
switch s.Max.(type) {
case int64:
if val.(float64) > float64(s.Max.(int64)) {
s.Max = val
}
case float64:
if val.(float64) > s.Max.(float64) {
s.Max = val
}
}
}
}
// Front returns front value.
func (s *Stack) Front() Var {
if len(s.Values) == 0 {
return nil
}
return s.Values[len(s.Values)-1]
}
// IntValues returns stack values explicitly casted to int.
//
// Main case is to use with termui.Sparklines.
func (s *Stack) IntValues() []int {
ret := make([]int, s.Len)
for i, v := range s.Values {
n, ok := v.(int64)
if ok {
ret[i] = int(n)
continue
}
f, ok := v.(float64)
if ok {
// 12.34 (float) -> 1234 (int)
ret[i] = int(f * 100)
continue
}
b, ok := v.(bool)
if ok {
// false => 0, true = 1
if b {
ret[i] = 1
} else {
ret[i] = 0
}
}
}
return ret
return s.Values
}
*/
// TODO: implement trim and resize

27
stat.go Normal file
View File

@ -0,0 +1,27 @@
package main
// Stat holds basic statistics data for
// integer data used for sparklines.
type Stat struct {
max IntVar
// TODO: implement running median
}
// NewStat inits new Stat object.
func NewStat() *Stat {
return &Stat{
max: &Number{},
}
}
// Update updates stats on each push.
func (s *Stat) Update(v IntVar) {
if v.Value() > s.max.Value() {
s.max = v
}
}
// Max returns maximum recorded value.
func (s *Stat) Max() IntVar {
return s.max
}

View File

@ -68,7 +68,7 @@ func (t *TermUI) Init(data UIData) error {
var sparklines []termui.Sparkline
for _, service := range data.Services {
spl := termui.NewSparkline()
spl.Height = 1
spl.Height = 1 // TODO: set height to th/len(services)
spl.LineColor = termui.ColorGreen
spl.Title = service.Name
sparklines = append(sparklines, spl)
@ -113,14 +113,28 @@ func (t *TermUI) Update(data UIData) {
// Sparklines
for i, service := range data.Services {
max := "MAX" // TODO: FIXME: formatMax(service.Max(data.Vars[0]))
t.Sparkline1.Lines[i].Title = fmt.Sprintf("%s%s", service.Name, max)
t.Sparkline1.Lines[i].Data = []int{} // TODO: service.Values(data.Vars[0])
name := data.Vars[0]
v, ok := service.Vars[name].(IntVar)
if ok {
data.Stacks[name].Push(v)
data.Stats[name].Update(v)
max := data.Stats[name].Max().String()
t.Sparkline1.Lines[i].Title = fmt.Sprintf("%s (max: %s)", service.Name, max)
t.Sparkline1.Lines[i].Data = data.Stacks[name].IntValues()
}
if len(data.Vars) > 1 {
max = "MAX" // TODO: FIXME: formatMax(service.Max(data.Vars[1]))
t.Sparkline2.Lines[i].Title = fmt.Sprintf("%s%s", service.Name, max)
t.Sparkline2.Lines[i].Data = []int{} // TODO: service.Values(data.Vars[1])
if len(data.Vars) == 1 {
continue
}
name = data.Vars[1]
v, ok = service.Vars[name].(IntVar)
if ok {
data.Stacks[name].Push(v)
data.Stats[name].Update(v)
max := data.Stats[name].Max().String()
t.Sparkline2.Lines[i].Title = fmt.Sprintf("%s (max: %s)", service.Name, max)
t.Sparkline2.Lines[i].Data = data.Stacks[name].IntValues()
}
}

View File

@ -92,17 +92,21 @@ func (t *TermUISingle) Update(data UIData) {
// Sparklines
for i, name := range data.Vars {
v, ok := service.Vars[name].(IntVar)
if !ok {
continue
}
data.Stacks[name].Push(v)
data.Stats[name].Update(v)
spl := &t.Sparkline.Lines[i]
max := "MAX" // FIXME: formatMax(service.Max(name))
spl.Title = fmt.Sprintf("%s: %v%s", name.Long(), service.Value(name), max)
max := data.Stats[name].Max().String()
spl.Title = fmt.Sprintf("%s: %v (max: %v)", name.Long(), service.Value(name), max)
spl.TitleColor = colorByKind(name.Kind())
spl.LineColor = colorByKind(name.Kind())
if name.Kind() == KindString {
continue
}
spl.Data = []int{} // FIXME: service.Values(name)
spl.Data = data.Stacks[name].IntValues()
}
t.Relayout()

24
var.go
View File

@ -27,6 +27,13 @@ type Var interface {
Set(*jason.Value)
}
// IntVar represents variable which value can be represented as integer,
// and suitable for displaying with sparklines.
type IntVar interface {
Var
Value() int
}
type Number struct {
// TODO: add mutex here or level above, in service?
val float64
@ -46,6 +53,11 @@ func (v *Number) Set(j *jason.Value) {
}
}
// Value implements IntVar for Number type.
func (v *Number) Value() int {
return int(v.val)
}
type Memory struct {
bytes int64
}
@ -62,6 +74,12 @@ func (v *Memory) Set(j *jason.Value) {
}
}
// Value implements IntVar for Memory type.
func (v *Memory) Value() int {
// TODO: check for possible overflows
return int(v.bytes)
}
type Duration struct {
dur time.Duration
}
@ -81,6 +99,12 @@ func (v *Duration) Set(j *jason.Value) {
}
}
// Value implements IntVar for Duration type.
func (v *Duration) Value() int {
// TODO: check for possible overflows
return int(v.dur)
}
type String struct {
str string
}