Skip to content

Commit

Permalink
Merge pull request #60 from ewilliams0305/ip-table
Browse files Browse the repository at this point in the history
Created IP Table View
  • Loading branch information
ewilliams0305 committed Jan 6, 2024
2 parents 17b7763 + c6b68d3 commit 21e8dfa
Show file tree
Hide file tree
Showing 8 changed files with 277 additions and 7 deletions.
186 changes: 186 additions & 0 deletions pkg/tui/iptable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package tui

import (
"fmt"
"os"
"strconv"

"github.com/charmbracelet/bubbles/table"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"golang.org/x/term"

vc "github.com/ewilliams0305/VC4-CLI/pkg/vc"
)

var iptable *IpTableModel

type IpTableModel struct {
roomId string
table table.Model
entries []vc.IpTableEntry
selected vc.IpTableEntry
err error
help HelpModel
width, height int
banner *BannerModel
}

func InitialIpTableModel(width, height int, roomid string) *IpTableModel {
entries := make([]vc.IpTableEntry, 0)
iptable = &IpTableModel{
table: newIpTableDeviceTable(entries, 0, width),
roomId: roomid,
entries: entries,
selected: vc.IpTableEntry{},
help: NewHelpModel(),
width: width,
height: height,
banner: NewBanner(fmt.Sprintf("VIEWING %s PROGRAM IP TABLES", roomid), BannerNormalState, width),
}
return iptable
}

func (m IpTableModel) Init() tea.Cmd {
return IpTableQuery(m.roomId)
}

func (m IpTableModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd

switch msg := msg.(type) {

case tickMsg:
w, h, _ := term.GetSize(int(os.Stdout.Fd()))
if w != m.width || h != m.height {
iptable.width = w
iptable.height = h
}
return iptable, tea.Batch(tick, IpTableQuery(iptable.roomId))

case tea.WindowSizeMsg:
iptable.width = msg.Width
iptable.height = msg.Height
iptable.table, cmd = iptable.table.Update(msg)
return iptable, cmd

case int:
iptable.selected = iptable.entries[msg]
return iptable, nil

case []vc.IpTableEntry:
iptable.err = nil
iptable.entries = msg
iptable.banner = NewBanner(fmt.Sprintf("VIEWING %s PROGRAM IP TABLES", iptable.roomId), BannerNormalState, iptable.width)
iptable.table.SetRows(getIpTableRows(iptable.width, iptable.table.Cursor(), msg))

if len(msg) != 0 && iptable.table.Cursor() >= 0 {
iptable.selected = msg[iptable.table.Cursor()]
}
return iptable, nil

case error:
iptable.err = msg
iptable.banner = NewBanner(msg.Error(), BannerErrorState, iptable.width)
return iptable, nil

case tea.KeyMsg:
switch msg.String() {

case "ctrl+q", "q", "ctrl+c", "esc":
return ReturnRoomsModel(), tea.Batch(tick, RoomsQuery)
}
}

iptable.table, cmd = m.table.Update(msg)
return iptable, cmd
}

func (m IpTableModel) View() string {
s := m.banner.View() + "\n"
s += BaseStyle.Render(m.table.View()) + "\n\n"
s += RenderMessageBox(m.width).Render(fmt.Sprintf("IPID: %d, %s %s", m.selected.ProgramIPID, m.selected.Model, m.selected.Description))
s += m.help.renderHelpInfo()
return s
}

func newIpTableDeviceTable(entries []vc.IpTableEntry, cursor int, width int) table.Model {

columns := getIpTableColumns(width)
rows := getIpTableRows(width, cursor, entries)

t := table.New(
table.WithColumns(columns),
table.WithRows(rows),
table.WithFocused(false),
table.WithHeight(16),
table.WithWidth(width),
)
t.Focus()

s := table.DefaultStyles()
s.Header = s.Header.
BorderStyle(lipgloss.NormalBorder()).
BorderForeground(lipgloss.Color("240")).
BorderBottom(true).
Foreground(lipgloss.Color(AccentColor)).
Bold(true)
s.Selected = s.Selected.
Foreground(lipgloss.Color(PrimaryLight)).
Background(lipgloss.Color(PrimaryDark)).
Bold(false)
t.SetStyles(s)
t.SetCursor(cursor)
return t
}

func getIpTableColumns(width int) []table.Column {

if width < 120 {

return []table.Column{
{Title: "", Width: 1},
{Title: "IPID", Width: 8},
{Title: "MODEL", Width: 28},
{Title: "DESCRIPTION", Width: width - 57},
{Title: "STATUS", Width: 8},
}
}
return []table.Column{
{Title: "", Width: 1},
{Title: "ID", Width: 20},
{Title: "MODEL", Width: 35},
{Title: "DESCRIPTION", Width: width - 113},
{Title: "IP ADDRESS", Width: 35},
{Title: "STATUS", Width: 8},
}
}

func getIpTableRows(width int, cursor int, entries []vc.IpTableEntry) []table.Row {
rows := []table.Row{}
small := width < 120

for i, ipt := range entries {
marker := ""
if cursor == i {
marker = "\u2192"
}
if small {
rows = append(rows, table.Row{marker, strconv.FormatInt(int64(ipt.ProgramIPID), 16), ipt.Model, ipt.Description, GetOnlineIcon(ipt.Status)})
} else {
rows = append(rows, table.Row{marker, strconv.FormatInt(int64(ipt.ProgramIPID), 16), ipt.Model, ipt.Description, ipt.RemoteIP, GetOnlineIcon(ipt.Status)})
}
}
return rows
}

func IpTableQuery(id string) tea.Cmd {

return func() tea.Msg {
ipTable, err := server.GetIpTable(id)
if err != nil {
return err
}
return ipTable
}
}
5 changes: 5 additions & 0 deletions pkg/tui/rooms.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ func (m RoomsTableModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return roomsModel, cmdCursor(roomsModel.table.Cursor())
}

case "t", "ctrl+t":
if roomsModel.err == nil {
ipt := InitialIpTableModel(roomsModel.width, roomsModel.height, roomsModel.selectedRoom.ID)
return ipt, ipt.Init()
}
case "ctrl+s":
if roomsModel.err == nil {
if m.selectedRoom.Status == string(vc.Running) {
Expand Down
15 changes: 10 additions & 5 deletions pkg/tui/rooms_help.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ type roomsKeyMap struct {
Create key.Binding
Delete key.Binding
Edit key.Binding
Table key.Binding
}

// ShortHelp returns keybindings to be shown in the mini help view. It's part
// of the key.Map interface.
func (k roomsKeyMap) ShortHelp() []key.Binding {
return []key.Binding{k.Quit, k.Up, k.Down, k.Start, k.Restart, k.Debug, k.Delete}
return []key.Binding{k.Quit, k.Up, k.Down, k.Start, k.Restart, k.Debug, k.Delete, k.Table}
}

// FullHelp returns keybindings for the expanded help view. It's part of the
Expand All @@ -43,11 +44,11 @@ func (k roomsKeyMap) FullHelp() [][]key.Binding {
var roomKeys = roomsKeyMap{
Up: key.NewBinding(
key.WithKeys("up", "k"),
key.WithHelp("↑/k", "move up"),
key.WithHelp("↑/k", "up"),
),
Down: key.NewBinding(
key.WithKeys("down", "j"),
key.WithHelp("↓/j", "move down"),
key.WithHelp("↓/j", "down"),
),
Help: key.NewBinding(
key.WithKeys("?", "h"),
Expand All @@ -67,15 +68,19 @@ var roomKeys = roomsKeyMap{
),
Debug: key.NewBinding(
key.WithKeys("ctrl+d"),
key.WithHelp("ctrl+d", "enable/disable debug"),
key.WithHelp("ctrl+d", "debug room"),
),
Restart: key.NewBinding(
key.WithKeys("ctrl+r"),
key.WithHelp("ctrl+r", "restart room"),
),
Delete: key.NewBinding(
key.WithKeys("delete"),
key.WithHelp("delete", "delete selected room"),
key.WithHelp("delete", "delete room"),
),
Table: key.NewBinding(
key.WithKeys("ctrl+t"),
key.WithHelp("ctrl+t", "view ip table"),
),
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/tui/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (m MainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case "enter", " ":
return arrowSelected(&m)

case "i":
case "i", "ctrl+i":
m.state = info
return NewDeviceInfo(m.width, m.height), DeviceInfoCommand

Expand Down
10 changes: 10 additions & 0 deletions pkg/tui/styles.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,16 @@ func GetStatus(status string) string {
return "🤚"
}

func GetOnlineIcon(status string) string {
switch status {
case "ONLINE":
return "✅"
case "OFFLINE":
return "❌"
}
return "❌"
}

func CheckMark(status bool) string {
if status {
return " \u2713"
Expand Down
2 changes: 1 addition & 1 deletion pkg/vc/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func (vc *VC) getBody(url string, result any) (err VirtualControlError) {
defer resp.Body.Close()

if resp.StatusCode != 200 {
return NewServerError(resp.StatusCode, errors.New("FAILED TO GET DEVICE INFO"))
return NewServerError(resp.StatusCode, errors.New("FFAILED GET REQUEST FROM SERVER"))
}

body, err := io.ReadAll(resp.Body)
Expand Down
63 changes: 63 additions & 0 deletions pkg/vc/iptable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package vc

import (
"cmp"
"slices"
)

const (
IPTABLEBYID = "IpTableByPID"
)

type VcIpTableApi interface {
GetIpTable(roomId string) ([]IpTableEntry, VirtualControlError)
}

func (v *VC) GetIpTable(roomId string) ([]IpTableEntry, VirtualControlError) {
return getIpTable(v, roomId)
}

func getIpTable(server *VC, roomId string) ([]IpTableEntry, VirtualControlError) {

var results IpTableResponse
err := server.getBody(IPTABLEBYID+"/"+roomId, &results)

if err != nil {
return make([]IpTableEntry, 0), NewServerError(500, err)
}

comparById := func(a, b IpTableEntry) int {
return cmp.Compare(a.ProgramIPID, b.ProgramIPID)
}

ipt := results.IpTableDevice.IpTablePrograms.IPTableByPID
slices.SortFunc(ipt, comparById)
return ipt, nil
}

type IpTableResponse struct {
IpTableDevice `json:"Device"`
}

type IpTableDevice struct {
IpTablePrograms `json:"Programs"`
}

type IpTablePrograms struct {
IPTableByPID []IpTableEntry `json:"IpTableByPID"`
}

type IpTableEntry struct {
UniqueID int `json:"UniqueId"`
ProgramInstanceID string `json:"ProgramInstanceId"`
ProgramIPID int `json:"ProgramIpId"`
Model string `json:"Model"`
Description string `json:"Description"`
RemoteIP string `json:"remote_ip"`
Status string `json:"Status"`
DeviceType int `json:"device_type"`
MacAddress string `json:"MacAddress"`
DeviceID int `json:"DeviceId"`
Hostname string `json:"Hostname"`
SupportAssociation bool `json:"SupportAssociation"`
}
1 change: 1 addition & 0 deletions pkg/vc/vc.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type VirtualControl interface {
VcProgramApi
VcInfoApi
VcRoomApi
VcIpTableApi
}

type VC struct {
Expand Down

0 comments on commit 21e8dfa

Please sign in to comment.