package tui import ( "fmt" "log" "strconv" "time" "github.com/rivo/tview" _ "github.com/gdamore/tcell/v2" ) type TUI struct { *Display //*LocalView *TUIClient SelectedReactor <-chan uint32 Err chan error } func NewTUI(ip string, port int, ch chan error) *TUI { //r := make(map[uint32]*Reactor) t := &TUI{} //l := new(LocalView) //l.Reactors = r //t.LocalView = l t.Err = ch client := NewTUIClient(ip, port) t.TUIClient = client return t } func (t *TUI) Start() { // setup tview app and wait for user connection in standin modal if err := t.TUIClient.Start(); err != nil { t.Err <- err } go t.Monitor() t.CreateDisplay() t.Display.Start() //go t.Refresh() } func (t *TUI) CreateDisplay() { c := make(chan uint32) t.Display = NewDisplay(c) t.SelectedReactor = c t.ReactorList.AddItem("REACTOR IS ONLINE"," ",0,nil) t.Flex.AddItem(t.ReactorList,0,1,true). AddItem(t.DeviceList,0,2,false) } func (t *TUI) Monitor() { // orchestrates updates and grpc requests timer := make(chan struct{}) go func(signal chan struct{}){ for { signal <- struct{}{} time.Sleep(1 * time.Second) } }(timer) for { select { case reactor := <-t.SelectedReactor: // reactor has been selected in tui, grabbing devs go t.UpdateDevices(reactor) //devs := t.GetReactorDevices(reactor) //t.DisplayReactorDevices(devs) case <-timer: // time to ping for status t.App.QueueUpdateDraw(func() { t.UpdateReactors() }) } } } func (t *TUI) UpdateDevices(r uint32) { // get devices for the reactor and update the tui devs, err := t.TUIClient.GetReactorDevices(r) if err != nil { log.Fatal(err) } //for a, d := range devs { // go t.LocalView.UpdateReactorDevices(r,a,d) //} t.DisplayReactorDevices(devs) } func (t *TUI) UpdateReactors() { // get all reactors and update the tui reactors, err := t.TUIClient.GetReactors() if err != nil { log.Fatal(err) } t.DisplayReactors(reactors) //for id, r := range reactors { // go t.LocalView.UpdateReactors(id, r) //} } // display struct and logic type Display struct { App *tview.Application Flex *tview.Flex ReactorList *tview.List DeviceList *tview.List SelectedReactor chan<- uint32 } func NewDisplay(ch chan uint32) *Display { d := &Display{} d.App = tview.NewApplication() d.Flex = tview.NewFlex() d.ReactorList = tview.NewList().ShowSecondaryText(false) d.DeviceList = tview.NewList().ShowSecondaryText(false) d.ReactorList.SetTitle("Reactors").SetBorder(true) d.DeviceList.SetTitle("Devices").SetBorder(true) d.SelectedReactor = ch return d } func (d *Display) Start() { if err := d.App.SetRoot(d.Flex, true).Run(); err != nil { d.App.Stop() log.Fatal(err) } } func (d *Display) DisplayReactors(r map[uint32]*Reactor) { // function to display reactor list to table d.ReactorList.Clear() for id, reactor := range r { var status string if reactor.Status { status = "[green]ONLINE" } else { status = "[red]OFFLINE" } txt := fmt.Sprintf("%v is %v", id, status) d.ReactorList.AddItem(txt,string(id),0,nil) } } func (d *Display) SelectReactor(index int, main, id string, r rune) { // called when reactor in list in selected if id, err := strconv.Atoi(id); err != nil { log.Fatal(err) } else { d.SelectedReactor <-uint32(id) } } func (d *Display) DisplayReactorDevices(devs map[int]*Device) { //Function that displays devices to the table //d.DeviceList.Clear() for addr, dev := range devs { var status string if dev.Status == "ACTIVE" { status = "[green]ACTIVE[white]" } else if dev.Status == "KILLED" { status = "[red]KILLED[white]" } txt := fmt.Sprintf("%v is %v at %x",dev.Type,status,addr) d.DeviceList.AddItem(txt,"",0,nil) } d.App.Draw() }