|
|
|
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
|
|
|
|
}
|