github.com/cloudfoundry-attic/ltc@v0.0.0-20151123212628-098adc7919fc/app_examiner/command_factory/graphical/graphical_visualizer.go (about)

     1  package graphical
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sort"
     7  	"strconv"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/cloudfoundry-incubator/ltc/app_examiner"
    12  	"github.com/gizak/termui"
    13  	"github.com/pivotal-golang/clock"
    14  )
    15  
    16  const (
    17  	graphicalRateDelta = 100 * time.Millisecond
    18  )
    19  
    20  //go:generate counterfeiter -o fake_graphical_visualizer/fake_graphical_visualizer.go . GraphicalVisualizer
    21  type GraphicalVisualizer interface {
    22  	PrintDistributionChart(rate time.Duration) error
    23  }
    24  
    25  type graphicalVisualizer struct {
    26  	appExaminer app_examiner.AppExaminer
    27  }
    28  
    29  type cellBar struct {
    30  	index   int
    31  	label   string
    32  	running int
    33  	claimed int
    34  }
    35  
    36  type cellBarSliceSortedByLabelNumber []cellBar
    37  
    38  func (c cellBarSliceSortedByLabelNumber) Len() int { return len(c) }
    39  func (c cellBarSliceSortedByLabelNumber) Less(i, j int) bool {
    40  	return c[i].index < c[j].index
    41  }
    42  func (c cellBarSliceSortedByLabelNumber) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
    43  
    44  func NewGraphicalVisualizer(appExaminer app_examiner.AppExaminer) *graphicalVisualizer {
    45  	return &graphicalVisualizer{
    46  		appExaminer: appExaminer,
    47  	}
    48  }
    49  
    50  func (gv *graphicalVisualizer) PrintDistributionChart(rate time.Duration) error {
    51  
    52  	//Initialize termui
    53  	err := termui.Init()
    54  	if err != nil {
    55  		return errors.New("Unable to initalize terminal graphics mode.")
    56  		//panic(err)
    57  	}
    58  	defer termui.Close()
    59  	if rate <= time.Duration(0) {
    60  		rate = graphicalRateDelta
    61  	}
    62  
    63  	termui.UseTheme("helloworld")
    64  
    65  	//Initalize some widgets
    66  	p := termui.NewPar("Lattice Visualization")
    67  	if p == nil {
    68  		return errors.New("Error Initializing termui objects NewPar")
    69  	}
    70  	p.Height = 1
    71  	p.Width = 25
    72  	p.TextFgColor = termui.ColorWhite
    73  	p.HasBorder = false
    74  
    75  	r := termui.NewPar(fmt.Sprintf("rate:%v", rate))
    76  	if r == nil {
    77  		return errors.New("Error Initializing termui objects NewPar")
    78  	}
    79  	r.Height = 1
    80  	r.Width = 10
    81  	r.TextFgColor = termui.ColorWhite
    82  	r.HasBorder = false
    83  
    84  	s := termui.NewPar("hit [+=inc; -=dec; q=quit]")
    85  	if s == nil {
    86  		return errors.New("Error Initializing termui objects NewPar")
    87  	}
    88  	s.Height = 1
    89  	s.Width = 30
    90  	s.TextFgColor = termui.ColorWhite
    91  	s.HasBorder = false
    92  
    93  	bg := termui.NewMBarChart()
    94  	if bg == nil {
    95  		return errors.New("Error Initializing termui objects NewMBarChart")
    96  	}
    97  	bg.IsDisplay = false
    98  	bg.Data[0] = []int{0}
    99  	bg.DataLabels = []string{"Missing"}
   100  	bg.Width = termui.TermWidth() - 10
   101  	bg.Height = termui.TermHeight() - 5
   102  	bg.BarColor[0] = termui.ColorGreen
   103  	bg.BarColor[1] = termui.ColorYellow
   104  	bg.NumColor[0] = termui.ColorRed
   105  	bg.NumColor[1] = termui.ColorRed
   106  	bg.TextColor = termui.ColorWhite
   107  	bg.Border.LabelFgColor = termui.ColorWhite
   108  	bg.Border.Label = "[X-Axis: Cells; Y-Axis: Instances]"
   109  	bg.BarWidth = 10
   110  	bg.BarGap = 1
   111  	bg.ShowScale = true
   112  
   113  	//12 column grid system
   114  	termui.Body.AddRows(termui.NewRow(termui.NewCol(12, 5, p)))
   115  	termui.Body.AddRows(termui.NewRow(termui.NewCol(12, 0, bg)))
   116  	termui.Body.AddRows(termui.NewRow(termui.NewCol(6, 0, s), termui.NewCol(6, 5, r)))
   117  
   118  	termui.Body.Align()
   119  
   120  	termui.Render(termui.Body)
   121  
   122  	bg.IsDisplay = true
   123  	clock := clock.NewClock()
   124  	evt := termui.EventCh()
   125  	for {
   126  		select {
   127  		case e := <-evt:
   128  			if e.Type == termui.EventKey {
   129  				switch {
   130  				case (e.Ch == 'q' || e.Ch == 'Q'):
   131  					return nil
   132  				case (e.Ch == '+' || e.Ch == '='):
   133  					rate += graphicalRateDelta
   134  				case (e.Ch == '_' || e.Ch == '-'):
   135  					rate -= graphicalRateDelta
   136  					if rate <= time.Duration(0) {
   137  						rate = graphicalRateDelta
   138  					}
   139  				}
   140  				r.Text = fmt.Sprintf("rate:%v", rate)
   141  				termui.Render(termui.Body)
   142  			}
   143  			if e.Type == termui.EventResize {
   144  				termui.Body.Width = termui.TermWidth()
   145  				termui.Body.Align()
   146  				termui.Render(termui.Body)
   147  			}
   148  		case <-clock.NewTimer(rate).C():
   149  			err := gv.getProgressBars(bg)
   150  			if err != nil {
   151  				return err
   152  			}
   153  			termui.Render(termui.Body)
   154  		}
   155  	}
   156  	return nil
   157  }
   158  
   159  func (gv *graphicalVisualizer) getProgressBars(bg *termui.MBarChart) error {
   160  	cells, err := gv.appExaminer.ListCells()
   161  	if err != nil {
   162  		return err
   163  	}
   164  
   165  	cellBars := []cellBar{}
   166  	maxTotal := -1
   167  	for _, cell := range cells {
   168  		var bar cellBar
   169  		if cell.Missing {
   170  			bar = cellBar{label: "Missing"}
   171  		} else {
   172  			index := 0
   173  			if strings.HasPrefix(cell.CellID, "cell-") {
   174  				s := cell.CellID[5:len(cell.CellID)]
   175  				if cellIndexInt, err := strconv.Atoi(s); err == nil {
   176  					index = cellIndexInt
   177  				}
   178  			}
   179  
   180  			bar = cellBar{
   181  				label:   cell.CellID,
   182  				index:   index,
   183  				running: cell.RunningInstances,
   184  				claimed: cell.ClaimedInstances,
   185  			}
   186  
   187  			total := cell.RunningInstances + cell.ClaimedInstances
   188  			if total > maxTotal {
   189  				maxTotal = total
   190  			}
   191  		}
   192  		cellBars = append(cellBars, bar)
   193  	}
   194  
   195  	sort.Sort(cellBarSliceSortedByLabelNumber(cellBars))
   196  
   197  	bg.Data[0] = getRunningFromBars(cellBars)
   198  	bg.Data[1] = getClaimedFromBars(cellBars)
   199  	bg.DataLabels = getLabelFromBars(cellBars)
   200  
   201  	if maxTotal < 10 {
   202  		bg.SetMax(10)
   203  	} else {
   204  		bg.SetMax(maxTotal)
   205  	}
   206  
   207  	return nil
   208  }
   209  
   210  func getRunningFromBars(cellBars []cellBar) []int {
   211  	ints := []int{}
   212  	for _, bar := range cellBars {
   213  		ints = append(ints, bar.running)
   214  	}
   215  	return ints
   216  }
   217  
   218  func getClaimedFromBars(cellBars []cellBar) []int {
   219  	ints := []int{}
   220  	for _, bar := range cellBars {
   221  		ints = append(ints, bar.claimed)
   222  	}
   223  	return ints
   224  }
   225  
   226  func getLabelFromBars(cellBars []cellBar) []string {
   227  	labels := []string{}
   228  	for _, bar := range cellBars {
   229  		labels = append(labels, bar.label)
   230  	}
   231  	return labels
   232  }