package tui import ( "fmt" "log" "strconv" "time" "github.com/rivo/tview" _ "github.com/gdamore/tcell/v2" ) // gonna start from scratch :/ 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 c := make(chan uint32) t.Display = NewDisplay(c) t.SelectedReactor = c 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 t.Display.Start() t.Connect() go t.Monitor() go t.Listen() //go t.Refresh() } func (t *TUI) Refresh() { for { // } } func (t *TUI) Listen() { for { select { case <-t.SelectedReactor: //blah } } } 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 go 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) } for id, r := range reactors { go t.LocalView.UpdateReactors(id, r) } t.DisplayReactors(reactors) } // 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.SelectedReactor = ch d.ReactorList.SetSelectedFunc(d.SelectReactor) return d } func (d *Display) Start() { d.Flex.AddItem(d.ReactorList.SetBorder(true).SetTitle("Reactors"),0,1,true) d.Flex.AddItem(d.DeviceList.SetBorder(true).SetTitle("Devices"),0,3,false) if err := d.App.SetRoot(d.Flex, true).Run(); err != nil { log.Fatal(err) } } func (d *Display) DisplayReactors(r map[uint32]*Reactor) { // function to display reactor list to table for id, reactor := range r { var status string if reactor.Status { status = "[green]ONLINE" } else { status = "[red]OFFLINE" } txt := fmt.Sprintf("Reactor %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) } }