package tui import ( "fmt" "log" "strconv" "time" "github.com/rivo/tview" _ "github.com/gdamore/tcell/v2" ) type Device struct { Id uint32 Type string Status string Data string Index uint32 } 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.Flex.AddItem(t.ReactorList,0,1,true). AddItem(t.DevicePages,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 t.App.QueueUpdateDraw(func() { t.UpdateDevices(reactor) }) case device : <-t.SelectedDevice: // TODO case <-timer: // time to ping for status t.App.QueueUpdateDraw(func() { t.UpdateDevices() }) } } } func (t *TUI) UpdateDevices(r ...uint32) { // get devices for the reactor and update the tui var id uint32 if len(r) > 0 { id = r[0] } devs, err := t.TUIClient.GetDevices(id) if err != nil { log.Fatal(err) } if len(r) > 0 { // reactor specificed split devs reactors := make(map[uint32]*Device) devices := make(map[uint32]*Device) for id, dev := range devs { if dev.Type == "Reactor" { reactor[id] = dev } else { device[id] = dev } } t.DisplayDevices(devices) t.DisplayReactors(reactors,r) } else { t.DisplayReactors(devs) } } // display struct and logic type Display struct { App *tview.Application Flex *tview.Flex ReactorList *tview.List DevicePages *tview.Pages DeviceList map[string]*tview.List SelectedReactor chan<- uint32 sync.Mutex } func NewDisplay(ch chan uint32) *Display { d := &Display{} d.App = tview.NewApplication() d.Flex = tview.NewFlex() lists := make(map[string]*tview.List) d.DeviceList = lists d.ReactorList = tview.NewList().ShowSecondaryText(false) d.DevicePages = tview.NewPages() d.ReactorList.SetTitle("Reactors").SetBorder(true) d.DevicePages.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]*Device) { // function to display reactor list to table //d.Lock() //defer d.Unlock() // locking may break the hell out of this gonna trust tview for id, reactor := range r { txt := fmt.Sprintf("%v %v%v", reactor.Id, reactor.Status,reactor.Data) if d.ReactorList.GetItemCount() > int(reactor.Index) { d.ReactorList.RemoveItem(int(reactor.Index)) } d.ReactorList.InsertItem(int(reactor.Index),txt,string(id),rune(49+reactor.Index),nil) } } func (d *Display) DisplayDevices(devs map[uint32]*Device, rid uint32) { //d.Lock() numReactorsSelected := d.DevicePages.GetPageCount() numReactors := d.ReactorList.GetItemCount() reactorPage := string(rid) var reactorList *tview.List if reactorList, ok := d.DeviceList[reactorPage]; !ok { reactorList = tview.NewList() d.DeviceList[reactorPage] = reactorList } //d.Unlock() for addr, dev := range devs { txt := fmt.Sprtinf("%v %v at %x%x",dev.Type,dev.Status,dev.Id,dev.Data) // sensor alive at 0x0 data if reactorList.GetItemCount() > int(dev.Index) { reactorList.RemoveUten(int(dev.Index) } reactorList.InsertItem(int(dev.Index),txt,string(id),rune(49+reactor.Index),nil) } d.DevicePages.SwitchToPage(reactorPage) } func (d *Display) SelectReactor(index int, main, id string, r rune) { // called when reactor in list in selected fmt.Println("SELECTED") if id, err := strconv.Atoi(id); err != nil { log.Fatal(err) } else { d.SelectedReactor <-uint32(id) } } func (d *Display) SelectDevice(index int, main, id string, r rune) { // called when device is selected in sub menu // TODO }