Initial commit
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
package ui
|
||||
|
||||
import "cli-mon/yabl"
|
||||
|
||||
var fNewCIS = func(packet *yabl.ContactorInternalState) *contactorType {
|
||||
return &contactorType{state: packet}
|
||||
}
|
||||
var fGetCIS = func(c *contactorType) *yabl.ContactorInternalState {
|
||||
return c.state
|
||||
}
|
||||
var fSetCIS = func(c *contactorType, packet *yabl.ContactorInternalState) {
|
||||
c.state = packet
|
||||
}
|
||||
|
||||
var fNewCES = func(packet *yabl.ContactorInternalErrors) *contactorType {
|
||||
return &contactorType{errors: packet}
|
||||
}
|
||||
var fGetCES = func(c *contactorType) *yabl.ContactorInternalErrors {
|
||||
return c.errors
|
||||
}
|
||||
var fSetCES = func(c *contactorType, packet *yabl.ContactorInternalErrors) {
|
||||
c.errors = packet
|
||||
}
|
||||
|
||||
var fNewPPE = func(packet *yabl.PuPresentEnergy) *connectorType {
|
||||
return &connectorType{energy: packet}
|
||||
}
|
||||
var fGetPPE = func(c *connectorType) *yabl.PuPresentEnergy {
|
||||
return c.energy
|
||||
}
|
||||
var fSetPPE = func(c *connectorType, packet *yabl.PuPresentEnergy) {
|
||||
c.energy = packet
|
||||
}
|
||||
|
||||
var fNewPP = func(packet *yabl.PuPeriphery) *connectorType {
|
||||
return &connectorType{state: packet}
|
||||
}
|
||||
var fGetPP = func(c *connectorType) *yabl.PuPeriphery {
|
||||
return c.state
|
||||
}
|
||||
var fSetPP = func(c *connectorType, packet *yabl.PuPeriphery) {
|
||||
c.state = packet
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"cli-mon/yabl"
|
||||
"github.com/gizak/termui/v3/widgets"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type connectorType struct {
|
||||
energy *yabl.PuPresentEnergy
|
||||
state *yabl.PuPeriphery
|
||||
}
|
||||
|
||||
type contactorType struct {
|
||||
state *yabl.ContactorInternalState
|
||||
errors *yabl.ContactorInternalErrors
|
||||
}
|
||||
|
||||
type modelTypes interface {
|
||||
*connectorType | *contactorType
|
||||
}
|
||||
|
||||
type dataTypes interface {
|
||||
*yabl.PuPresentEnergy | *yabl.PuPeriphery | *yabl.ContactorInternalState | *yabl.ContactorInternalErrors
|
||||
}
|
||||
|
||||
type fNewType[T modelTypes, V dataTypes] func(e V) T
|
||||
type fGetType[T modelTypes, V dataTypes] func(m T) V
|
||||
type fSetType[T modelTypes, V dataTypes] func(m T, e V)
|
||||
|
||||
var connectors = make([]*connectorType, connectorsCount+1)
|
||||
var contactors = make([]*contactorType, contactorsCount+1)
|
||||
|
||||
var connUI [connectorsCount + 1]*widgets.Paragraph
|
||||
var contUI [contactorsCount + 1]*widgets.Paragraph
|
||||
var contactorsErr *widgets.Paragraph
|
||||
|
||||
func process(message yabl.Packet) {
|
||||
if message == nil {
|
||||
return
|
||||
}
|
||||
|
||||
unitId := message.GetUnitId()
|
||||
|
||||
switch msg := message.(type) {
|
||||
case *yabl.ContactorInternalState:
|
||||
if updateOrNew(contactors, unitId, msg, fNewCIS, fGetCIS, fSetCIS) {
|
||||
updateContactorsView(unitId)
|
||||
}
|
||||
|
||||
case *yabl.ContactorInternalErrors:
|
||||
if updateOrNew(contactors, unitId, msg, fNewCES, fGetCES, fSetCES) {
|
||||
updateContactorsStateView()
|
||||
}
|
||||
|
||||
case *yabl.PuPeriphery:
|
||||
if updateOrNew(connectors, unitId, msg, fNewPP, fGetPP, fSetPP) {
|
||||
updateConnectorsView(unitId)
|
||||
}
|
||||
|
||||
case *yabl.PuPresentEnergy:
|
||||
if updateOrNew(connectors, unitId, msg, fNewPPE, fGetPPE, fSetPPE) {
|
||||
updateConnectorsView(unitId)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func updateOrNew[T modelTypes, V dataTypes](array []T, id uint, msg V, fNew fNewType[T, V], fGet fGetType[T, V], fSet fSetType[T, V]) bool {
|
||||
if model := array[id]; model != nil {
|
||||
if element := fGet(model); !reflect.DeepEqual(element, msg) {
|
||||
fSet(model, msg)
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
model = fNew(msg)
|
||||
array[id] = model
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"cli-mon/yabl"
|
||||
ui "github.com/gizak/termui/v3"
|
||||
"github.com/gizak/termui/v3/widgets"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
const headerHeight int = 4
|
||||
const contactorsCount = 18
|
||||
const connectorsCount = 6
|
||||
|
||||
func InitCliApp(events <-chan yabl.Packet) {
|
||||
if err := ui.Init(); err != nil {
|
||||
log.Fatalf("failed to initialize termui: %v", err)
|
||||
}
|
||||
defer ui.Close()
|
||||
|
||||
var connectorsTab *ui.Grid
|
||||
//var contactorsErr *widgets.Paragraph
|
||||
var contactorsStateTab *ui.Grid
|
||||
var header *widgets.Paragraph
|
||||
|
||||
tabpane := widgets.NewTabPane("Connectors", "Contactors States", "Contactors Errors")
|
||||
tabpane.SetRect(0, 1, 150, 4)
|
||||
tabpane.Border = true
|
||||
|
||||
header = newHeader()
|
||||
connectorsTab = newConnectorsView()
|
||||
contactorsStateTab = newContactorsView()
|
||||
contactorsErr = newContactorsErrView()
|
||||
|
||||
renderTab := func() {
|
||||
switch tabpane.ActiveTabIndex {
|
||||
case 0:
|
||||
termWidth, termHeight := ui.TerminalDimensions()
|
||||
connectorsTab.SetRect(0, headerHeight, termWidth, termHeight)
|
||||
ui.Render(connectorsTab)
|
||||
case 1:
|
||||
termWidth, termHeight := ui.TerminalDimensions()
|
||||
contactorsStateTab.SetRect(0, headerHeight, termWidth, termHeight)
|
||||
ui.Render(contactorsStateTab)
|
||||
case 2:
|
||||
ui.Render(contactorsErr)
|
||||
}
|
||||
}
|
||||
|
||||
ui.Render(header, tabpane, connectorsTab)
|
||||
|
||||
uiEvents := ui.PollEvents()
|
||||
ticker := time.NewTicker(2 * time.Second).C
|
||||
for {
|
||||
select {
|
||||
case e := <-uiEvents:
|
||||
switch e.ID { // event string/identifier
|
||||
case "q", "<C-c>": // press 'q' or 'C-c' to quit
|
||||
return
|
||||
case "<MouseLeft>":
|
||||
//payload := e.Payload.(ui.Mouse)
|
||||
//x, y := payload.X, payload.Y
|
||||
//p.Text = fmt.Sprintf("X = %d, Y = %d", x, y)
|
||||
case "<Left>":
|
||||
tabpane.FocusLeft()
|
||||
ui.Clear()
|
||||
ui.Render(header, tabpane)
|
||||
renderTab()
|
||||
case "<Right>":
|
||||
tabpane.FocusRight()
|
||||
ui.Clear()
|
||||
ui.Render(header, tabpane)
|
||||
renderTab()
|
||||
}
|
||||
|
||||
switch e.Type {
|
||||
case ui.KeyboardEvent: // handle all key presses
|
||||
//eventID := e.ID // keypress string
|
||||
//p.Text = eventID
|
||||
}
|
||||
// use Go's built-in tickers for updating and drawing data
|
||||
case <-ticker:
|
||||
renderTab()
|
||||
case message := <-events:
|
||||
process(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"cli-mon/yabl"
|
||||
"fmt"
|
||||
ui "github.com/gizak/termui/v3"
|
||||
"github.com/gizak/termui/v3/widgets"
|
||||
)
|
||||
|
||||
func newHeader() *widgets.Paragraph {
|
||||
widget := widgets.NewParagraph()
|
||||
widget.Text = "Press q to quit, Press <Left> or <Right> to switch tabs"
|
||||
widget.SetRect(0, 0, 50, 1)
|
||||
widget.Border = false
|
||||
widget.TextStyle.Bg = ui.ColorBlue
|
||||
return widget
|
||||
}
|
||||
|
||||
func newConnectorsView() *ui.Grid {
|
||||
grid := ui.NewGrid()
|
||||
|
||||
termWidth, termHeight := ui.TerminalDimensions()
|
||||
grid.SetRect(0, headerHeight, termWidth, termHeight)
|
||||
|
||||
var conn [connectorsCount + 1]*widgets.Paragraph //ui.GridItem
|
||||
|
||||
for i := 0; i <= connectorsCount; i++ {
|
||||
p := widgets.NewParagraph()
|
||||
p.Text = fmt.Sprintf("No data: #%2.d", i)
|
||||
p.Title = fmt.Sprintf("Connector: #%2.d", i)
|
||||
//p.SetRect(0, 0, 50, 30)
|
||||
p.TextStyle.Bg = ui.ColorBlue
|
||||
p.TextStyle.Fg = ui.ColorWhite
|
||||
p.BorderStyle.Fg = ui.ColorYellow
|
||||
p.Border = true
|
||||
|
||||
connUI[i] = p
|
||||
conn[i] = p //ui.NewCol(1.0/6, p)
|
||||
}
|
||||
|
||||
grid.Set(
|
||||
ui.NewRow(
|
||||
1.0/2,
|
||||
ui.NewCol(1.0/4, conn[1]),
|
||||
ui.NewCol(1.0/4, conn[2]),
|
||||
ui.NewCol(1.0/4, conn[3]),
|
||||
ui.NewCol(1.0/4, conn[4]),
|
||||
),
|
||||
ui.NewRow(
|
||||
1.0/2,
|
||||
ui.NewCol(1.0/4, conn[5]),
|
||||
ui.NewCol(1.0/4, conn[6]),
|
||||
ui.NewCol(1.0/4, conn[0]),
|
||||
),
|
||||
)
|
||||
|
||||
return grid
|
||||
}
|
||||
|
||||
func newContactorsView() *ui.Grid {
|
||||
grid := ui.NewGrid()
|
||||
|
||||
termWidth, termHeight := ui.TerminalDimensions()
|
||||
grid.SetRect(0, headerHeight, termWidth, termHeight)
|
||||
|
||||
var cont [contactorsCount + 1]ui.GridItem
|
||||
|
||||
//var contactors = make([]ui.GridItem, contactorsCount)
|
||||
for i := 1; i <= contactorsCount; i++ {
|
||||
p := widgets.NewParagraph()
|
||||
p.Text = fmt.Sprintf("No data: #%2.d", i)
|
||||
p.Title = fmt.Sprintf("Contactor: #%2.d", i)
|
||||
p.Border = false
|
||||
p.TextStyle.Bg = ui.ColorBlue
|
||||
p.TextStyle.Fg = ui.ColorWhite
|
||||
p.BorderStyle.Fg = ui.ColorYellow
|
||||
p.Border = true
|
||||
|
||||
contUI[i] = p
|
||||
cont[i] = ui.NewCol(1.0/6, p)
|
||||
}
|
||||
|
||||
grid.Set(
|
||||
ui.NewRow(
|
||||
1.0/3,
|
||||
cont[1],
|
||||
cont[2],
|
||||
cont[3],
|
||||
cont[4],
|
||||
cont[5],
|
||||
cont[6],
|
||||
),
|
||||
ui.NewRow(
|
||||
1.0/3,
|
||||
cont[7],
|
||||
cont[8],
|
||||
cont[9],
|
||||
cont[10],
|
||||
cont[11],
|
||||
cont[12],
|
||||
),
|
||||
ui.NewRow(
|
||||
1.0/3,
|
||||
cont[13],
|
||||
cont[14],
|
||||
cont[15],
|
||||
cont[16],
|
||||
cont[17],
|
||||
cont[18],
|
||||
))
|
||||
|
||||
//ui.NewRow(1, ui.InterfaceSlice(contactors)...)
|
||||
grid.Set()
|
||||
|
||||
return grid
|
||||
}
|
||||
|
||||
func newContactorsErrView() *widgets.Paragraph {
|
||||
p := widgets.NewParagraph()
|
||||
p.Text = "No data\n"
|
||||
p.Title = "Contactors Errors"
|
||||
p.SetRect(5, 5, 55, 15)
|
||||
p.BorderStyle.Fg = ui.ColorYellow
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func updateContactorsView(unitId uint) {
|
||||
p := contUI[unitId]
|
||||
s := contactors[unitId].state
|
||||
|
||||
if s == nil || p == nil {
|
||||
return
|
||||
}
|
||||
|
||||
p.Text = fmt.Sprintf(
|
||||
"BoardReady: %s\nContactorOn: %s\nUnexpectedState: %s\nIsolated: %s\nDebug: %s",
|
||||
s.ContactorReady,
|
||||
s.ContactorOn,
|
||||
s.UnexpectedState,
|
||||
s.Isolated,
|
||||
s.DebugEnabled)
|
||||
|
||||
if s.ContactorReady == yabl.BOARD_READY_OK {
|
||||
p.TextStyle.Bg = ui.ColorClear
|
||||
} else {
|
||||
p.TextStyle.Bg = ui.ColorRed
|
||||
}
|
||||
|
||||
if s.ContactorOn == yabl.ON {
|
||||
p.TitleStyle.Bg = ui.ColorBlue
|
||||
} else {
|
||||
p.TitleStyle.Bg = ui.ColorClear
|
||||
}
|
||||
}
|
||||
|
||||
func updateConnectorsView(unitId uint) {
|
||||
p := connUI[unitId]
|
||||
s := connectors[unitId].state
|
||||
e := connectors[unitId].energy
|
||||
|
||||
if s == nil || e == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fstring := "ConnectorInsert %s\n" +
|
||||
"ContactorOn %s\n" +
|
||||
"ConnectorLocked %s\n" +
|
||||
"LineLevel %s\n" +
|
||||
"IsolationState %s\n" +
|
||||
"ChargingAllowed %s\n" +
|
||||
"PwmEnabled %s\n" +
|
||||
"CpLineVoltage %s\n" +
|
||||
"V2GMode %s\n" +
|
||||
"VoltageBefore %s\n" +
|
||||
"VoltageAfter %s\n" +
|
||||
"PresentCurrent %s"
|
||||
|
||||
p.Text = fmt.Sprintf(
|
||||
fstring,
|
||||
s.ConnectorInsert,
|
||||
s.ContactorOn,
|
||||
s.ConnectorLocked,
|
||||
s.CpLineLevel,
|
||||
s.IsolationState,
|
||||
s.ChargingAllowed,
|
||||
s.PwmEnabled,
|
||||
s.CpLineVoltage,
|
||||
e.V2GMode,
|
||||
e.VoltageBefore,
|
||||
e.VoltageAfter,
|
||||
e.PresentCurrent)
|
||||
}
|
||||
|
||||
func updateContactorsStateView() {
|
||||
p := contactorsErr
|
||||
|
||||
if contactors[1] == nil {
|
||||
return
|
||||
}
|
||||
|
||||
e := contactors[1].errors
|
||||
|
||||
if e == nil {
|
||||
return
|
||||
}
|
||||
|
||||
p.Text = fmt.Sprintf(
|
||||
"BoardReady: %s\nOtherError: %s\nGroupChanged: %s\nUnexpectedFormation: %s\nCpuNotReady: %s\nPuNotReady: %s\nDebug: %s",
|
||||
e.BoardReady,
|
||||
e.OtherError,
|
||||
e.ContactorGroupChanged,
|
||||
e.UnexpectedFormation,
|
||||
e.CpuNotReady,
|
||||
e.PuNotReady,
|
||||
e.Debug,
|
||||
)
|
||||
|
||||
if e.BoardReady == yabl.BOARD_READY_OK {
|
||||
p.TextStyle.Bg = ui.ColorClear
|
||||
} else {
|
||||
p.TextStyle.Bg = ui.ColorRed
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user