You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

146 lines
3.5 KiB
Go

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