// Why realMain? See https://groups.google.com/forum/#!topic/golang-nuts/_Twwb5ULStM // So that defer will run propoerly func realMain() (exitStatus int) { // initialize ncurses stdscr, colors, resetScreen := initializeNcurses() // clean up the screen before we die defer func() { if err := recover(); err != nil { resetScreen() fmt.Fprintf(os.Stderr, "exiting from error: %s \n", err) ERROR.Println("exiting from error: ", err) exitStatus = 1 os.Exit(1) } }() // draw the stuff on the screen msgWin, workerCountWin, durWin, reqSecWin, barsWin, scaleWin, maxWin := drawDisplay(stdscr) // create our various channels infoMsgsCh := make(chan ncursesMsg) exitCh := make(chan int) changeNumRequestersCh := make(chan interface{}) changeNumRequestersListenerCh := make(chan interface{}) reqMadeOnSecCh := make(chan interface{}) reqMadeOnSecListenerCh := make(chan interface{}) reqMadeOnSecSlaveListenerCh := make(chan interface{}) failsOnSecCh := make(chan int) durationCh := make(chan int64) durationDisplayCh := make(chan string) reqSecDisplayCh := make(chan string) bytesPerSecCh := make(chan bytesPerSecMsg) bytesPerSecDisplayCh := make(chan string) barsToDrawCh := make(chan currentBars) // start all the worker goroutines go windowRunloop(infoMsgsCh, exitCh, changeNumRequestersCh, msgWin) go barsController(reqMadeOnSecListenerCh, failsOnSecCh, barsToDrawCh, reqSecDisplayCh) go requesterController(infoMsgsCh, changeNumRequestersListenerCh, reqMadeOnSecCh, failsOnSecCh, durationCh, bytesPerSecCh, *testUrl, *introduceRandomFails) go durationWinController(durationCh, durationDisplayCh) go bytesPerSecController(bytesPerSecCh, bytesPerSecDisplayCh) numRequestersBcaster := bcast.MakeNew(changeNumRequestersCh, INFO) numRequestersBcaster.Join(changeNumRequestersListenerCh) reqMadeOnSecBcaster := bcast.MakeNew(reqMadeOnSecCh, INFO) reqMadeOnSecBcaster.Join(reqMadeOnSecListenerCh) if *listen > 0 { port := *listen // we don't want to join until we start the listener joinUp := func() { reqMadeOnSecBcaster.Join(reqMadeOnSecSlaveListenerCh) } go slave.ListenForMaster(port, changeNumRequestersCh, reqMadeOnSecSlaveListenerCh, joinUp, INFO) } else if len(slaveList) > 0 { connectToSlaves(slaveList, numRequestersBcaster, reqMadeOnSecCh) } currentScale := int64(1) // This is the main loop controlling the ncurses display. Since ncurses // wasn't designed with concurrency in mind, only one goroutine should // write to a window, so I'm putting all the window writing in here. main: for { select { case msg := <-infoMsgsCh: updateMsgWin(msg, msgWin, workerCountWin) case msg := <-durationDisplayCh: // that %7s should really be determined from durWidth durWin.MovePrint(1, 1, fmt.Sprintf("%11s", msg)) durWin.NoutRefresh() case msg := <-reqSecDisplayCh: reqSecWin.MovePrint(1, 1, fmt.Sprintf("%14s", msg)) reqSecWin.NoutRefresh() case msg := <-barsToDrawCh: currentScale = calculateScale(msg.max) // 25 is the number of rows in the window, s/b dynamic or defined elsewhere maxWin.MovePrint(1, 1, fmt.Sprintf("%5d", msg.max)) maxWin.NoutRefresh() scaleWin.MovePrint(1, 1, fmt.Sprintf("%5d", currentScale)) scaleWin.NoutRefresh() updateBarsWin(msg, barsWin, *colors, currentScale) case msg := <-bytesPerSecDisplayCh: //msgAsFloat64, err := strconv.ParseFloat(msg, 64) //if err != nil { // panic(fmt.Sprintf("converting %s failed: %v", msg, err)) //} //if msgAsFloat64 > 0 { if msg != " 0.00" { INFO.Println("bytes/sec for each second: ", msg) } case exitStatus = <-exitCh: break main } gc.Update() } msgWin.Delete() gc.End() INFO.Println("exiting with status ", exitStatus) return exitStatus }
func drawDisplay( stdscr *gc.Window, ) ( msgWin *gc.Window, workerCountWin *gc.Window, durWin *gc.Window, reqSecWin *gc.Window, barsWin *gc.Window, scaleWin *gc.Window, maxWin *gc.Window, ) { // print startup message stdscr.Print("Press 'q' to exit") stdscr.NoutRefresh() // Create message window // and enable the use of the // keypad on it so the arrow keys are available msgHeight, msgWidth := 5, 40 msgY, msgX := 1, 0 msgWin = createWindow(msgHeight, msgWidth, msgY, msgX) msgWin.Keypad(true) msgWin.Box(0, 0) msgWin.NoutRefresh() // Create the counter window, showing how many goroutines are active ctrHeight, ctrWidth := 3, 7 ctrY := 2 ctrX := msgWidth + 1 stdscr.MovePrint(1, ctrX+1, "thrds") stdscr.NoutRefresh() workerCountWin = createWindow(ctrHeight, ctrWidth, ctrY, ctrX) workerCountWin.Box(0, 0) workerCountWin.NoutRefresh() // Create the avg duration window, showing 5 second moving average durHeight, durWidth := 4, 14 durY := 2 durX := ctrX + ctrWidth + 1 stdscr.MovePrint(1, durX+1, "duration ms") stdscr.NoutRefresh() durWin = createWindow(durHeight, durWidth, durY, durX) durWin.Box(0, 0) durWin.NoutRefresh() // Create the requests/sec window, reqSecHeight, reqSecWidth := 3, 16 reqSecY := 2 reqSecX := durX + durWidth + 1 stdscr.MovePrint(1, reqSecX+1, "req/s 1/5/60") stdscr.NoutRefresh() reqSecWin = createWindow(reqSecHeight, reqSecWidth, reqSecY, reqSecX) reqSecWin.Box(0, 0) reqSecWin.NoutRefresh() // Create the bars window, showing the moving display of bars secondsPerMinute := 60 barsWidth := secondsPerMinute + 3 // we wrap after a minute barsHeight := 25 // need to size this dynamically, TBD barsY := msgHeight + 1 barsX := 9 // leave space for scale window barsWin = createWindow(barsHeight, barsWidth, barsY, barsX) barsWin.Box(0, 0) barsWin.NoutRefresh() // Max window, showing the max seen over the last 60 seconds maxWidth := 7 maxHeight := 3 maxY := barsY + barsHeight - 8 maxX := 1 stdscr.MovePrint(maxY, 1, "max:") stdscr.NoutRefresh() maxY += 1 maxWin = createWindow(maxHeight, maxWidth, maxY, maxX) maxWin.Box(0, 0) maxWin.NoutRefresh() // Scale window, showing our current scaling factor for the bars display scaleWidth := 7 scaleHeight := 3 scaleY := barsY + barsHeight - 4 scaleX := 1 stdscr.MovePrint(scaleY, 1, "scale:") stdscr.NoutRefresh() scaleY += 1 scaleWin = createWindow(scaleHeight, scaleWidth, scaleY, scaleX) scaleWin.Box(0, 0) scaleWin.MovePrint(1, 1, fmt.Sprintf("%5s", "1")) scaleWin.NoutRefresh() // Update will flush only the characters which have changed between the // physical screen and the virtual screen, minimizing the number of // characters which must be sent gc.Update() return }
func main() { stdscr, _ := gc.Init() defer gc.End() yMAX, xMAX := stdscr.MaxYX() yMAX = yMAX - 5 xMAX = xMAX - 5 gc.StartColor() gc.Raw(true) gc.Echo(false) gc.Cursor(0) stdscr.Keypad(true) //gc.InitPair(1, gc.C_RED, gc.C_BLACK) //gc.InitPair(2, gc.C_CYAN, gc.C_BLACK) // build the menu items menu_items := []string{ " P - 152.111.192.51", " W - 152.111.192.52", " E - 152.111.192.53", " R - 152.111.192.54", " T - 152.111.192.55", "Exit"} items := make([]*gc.MenuItem, len(menu_items)) for i, val := range menu_items { items[i], _ = gc.NewItem(val, "") defer items[i].Free() } // create the menu menu, _ := gc.NewMenu(items) defer menu.Free() menuwin, _ := gc.NewWindow(yMAX, xMAX, 1, 1) menuwin.Keypad(true) menu.SetWindow(menuwin) //dwin := menuwin.Derived(6, 38, 3, 1) //menu.SubWindow(dwin) menu.Option(gc.O_SHOWDESC, true) menu.Format(5, 2) menu.Mark("*") // MovePrint centered menu title title := "My Menu" menuwin.Box(0, 0) menuwin.ColorOn(1) menuwin.MovePrint(1, (WIDTH/2)-(len(title)/2), title) menuwin.ColorOff(1) menuwin.MoveAddChar(2, 0, gc.ACS_LTEE) //menuwin.HLine(4, 1, gc.ACS_HLINE, WIDTH-2) // menuwin.HLine(12, 10, gc.ACS_HLINE, WIDTH-2) //menuwin.MoveAddChar(2, WIDTH-1, gc.ACS_RTEE) y, _ := stdscr.MaxYX() stdscr.ColorOn(2) stdscr.MovePrint(y-3, 1, "Use up/down arrows or page up/down to navigate. 'q' to exit") stdscr.ColorOff(2) stdscr.Refresh() menu.Post() defer menu.UnPost() menuwin.Refresh() for { gc.Update() ch := menuwin.GetChar() if ch == 'p' { cmd2 := exec.Command("clear") cmd2.Stdout = os.Stdout cmd2.Stdin = os.Stdin //cmd2.Stderr = os.Stderr cmd2.Run() cmd := exec.Command("ssh", "[email protected]") cmd.Stdout = os.Stdout cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr cmd.Run() return } else if ch == 'q' { return } else if ch == 27 { return } else { menu.Driver(gc.DriverActions[ch]) } } }