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 }