package tui import ( "sync" "fmt" "time" "github.com/rivo/tview" tcell "github.com/gdamore/tcell/v2" ) type System struct { // struct to hold actual system state Display Connected map[uint32]*Reactor err chan error type Display struct { app *tview.Application flex *tview.Flex reactorlist *tview.List sensortable *tview.Table mu sync.Mutex } type Reactor struct { Index int // index into list Id uint32 Ip string Port int Model string Status bool Sensors map[int]*Sensor } func (r Reactor) String() string { s := "" if r.Status { s = "[green]ONLINE" } else { s = "[red]OFFLINE" } return fmt.Sprintf("Reactor %v (%v) is %v on %v:%v",r.Id,r.Model,s,r.Ip,r.Port) } type Sensor struct { Id uint32 Addr int Type string Status string } func NewDisplay(ch chan error) *Display { m := make(map[uint32]*Reactor) a := tview.NewApplication() f := tview.NewFlex() f.AddItem(tview.NewBox().SetTitle("Connected Reactors").SetBorder(true),0,1, true) f.AddItem(tview.NewBox().SetTitle("Sensor Info").SetBorder(true),0,2, false) if err := a.SetRoot(f,true).SetFocus(f).Run(); err != nil { panic(err) } a.Stop() return &Display{app:a,Connected:m,err:ch} } func NewReactor(ip,model string, status bool, id uint32, port int) *Reactor { s := map[int]*Sensor{} return &Reactor{Ip:ip,Model:model,Status:status,Id:id,Port:port,Sensors:s} } func NewSensor(addr int, t, status string) *Sensor { return &Sensor{Addr:addr,Type:t,Status:status} } func (d *Display) Start () { go d.Refresh() } func (d *Display) UpdateReactor(r *Reactor) { d.mu.Lock() defer d.mu.Unlock() if _, ok := d.Connected[r.Id]; ok { // reactor exists in memory d.Connected[r.Id] = r d.reactorlist.SetItemText(r.Index,fmt.Sprint(r),"") } else { r.Index = d.reactorlist.GetItemCount() // get before adding d.reactorlist.AddItem(fmt.Sprint(r),"",rune(r.Index), func () {d.SelectSensor(r.Id)}) } d.app.Run() d.app.Stop() } func (d *Display) UpdateSensor(s *Sensor) { d.mu.Lock() defer d.mu.Unlock() if r,ok := d.Connected[s.Id]; ok { r.Sensors[s.Addr] = s } } func (d *Display) SelectSensor(id uint32) { d.mu.Lock() defer d.mu.Unlock() if reactor, ok := d.Connected[id]; ok { d.sensortable.Clear() row := 0 ch := []string{"Type","Addr","Status"} for i,v := range ch { // create column headings c := tview.NewTableCell(v).SetTextColor(tcell.ColorGray) d.sensortable.SetCell(row,i,c) } row += 1 for a,s := range reactor.Sensors { addr := tview.NewTableCell(string(a)) typ := tview.NewTableCell(s.Type) st := "" col := tcell.ColorWhite if s.Status == "ACTIVE" { col = tcell.ColorGreen } else if s.Status == "KILLED" { col = tcell.ColorRed } else { st = "UNKNOWN" col = tcell.ColorYellow } status := tview.NewTableCell(st).SetTextColor(col) d.sensortable.SetCell(row,0,typ) d.sensortable.SetCell(row,1,addr) d.sensortable.SetCell(row,2,status) row += 1 } } } func (d *Display) Refresh() { tick := time.NewTicker(500 * time.Millisecond) for { select { case <-tick.C: d.app.Draw() } } }