diff --git a/cdashdisplay/connect.go b/cdashdisplay/connect.go new file mode 100644 index 0000000..b9586cc --- /dev/null +++ b/cdashdisplay/connect.go @@ -0,0 +1,76 @@ +package cdashdisplay + +import ( + "esdi/peripheral/communication" + "esdi/peripheral/communication/packets" + "fmt" + "path/filepath" + "time" + + "github.com/tarm/serial" +) + +func listPorts() ([]string, error) { + ttyUSBs, err := filepath.Glob("/dev/ttyUSB*") + if err != nil { + return []string{}, err + } + + return ttyUSBs, nil +} + +func probe(WT *communication.WalkieTalkie) error { + err := WT.TurnOn() + if err != nil { + return err + } + + // Send the identification command + cmd := communication.CmdRequestID + var response packets.IdentificationPacket + err = WT.SendCommand(cmd, []byte{0x06, 0x07, 0x08, 0x09}, &response) + if err != nil { + return err + } + + if response.DeviceID != 0x01 { + return fmt.Errorf("wrong ID") + } + + return nil +} + +func findDisplayPort() (*communication.WalkieTalkie, error) { + ports, err := listPorts() + if err != nil { + return nil, err + } + + pLogger.Info(fmt.Sprintf("Looking into %v", ports)) + + var wt *communication.WalkieTalkie + for _, port := range ports { + wt = &communication.WalkieTalkie{ + Cfg: &serial.Config{ + Name: port, + Baud: 115200, + ReadTimeout: 500 * time.Millisecond, + }, + } + + err = probe(wt) + if err == nil { + break + } + + pLogger.Info(fmt.Sprintf("wasn't port %s", port)) + wt = nil + } + + if wt == nil { + return nil, fmt.Errorf("couldn't find cdashdisplay") + } + + pLogger.Info(fmt.Sprintf("found cdashdisplay on port: %s", wt.Cfg.Name)) + return wt, nil +} diff --git a/cdashdisplay/display.go b/cdashdisplay/display.go new file mode 100644 index 0000000..37124db --- /dev/null +++ b/cdashdisplay/display.go @@ -0,0 +1,150 @@ +// Package cdashdisplay will handle the USB connections +package cdashdisplay + +import ( + helper "esdi/helpers" + "esdi/peripheral/communication" + "esdi/peripheral/communication/packets" + "esdi/peripheral/types" + "fmt" + "log/slog" +) + +var pLogger *slog.Logger + +func SetLogger(l *slog.Logger) { + pLogger = l +} + +const ( + newWindowCMDID types.Command = 3 + destroyWindowCMDID types.Command = 4 + moveWindowCMDID types.Command = 5 + newLayoutCMDID types.Command = 6 +) + +var ( + DefaultDecorations = UIDecorations{ + HasBorder: 1, + BGColour: 0x1041, // Look in the eslabsCurses library for these colours + FGColour: 0xffff, + TitleColour: 0xffff, + BorderColour: 0xf800, + TitleSize: 2, + TextSize: 4, + Padding: 0x00, + } +) + +type UIDimensions struct { + X0 uint16 + Y0 uint16 + Width uint16 + Height uint16 +} + +type UIDecorations struct { + BGColour uint16 + FGColour uint16 + TitleColour uint16 + BorderColour uint16 + TitleSize uint8 + TextSize uint8 + HasBorder uint8 + Padding uint8 +} + +type UIWindow struct { + Dims UIDimensions + Decor UIDecorations + Title [32]byte +} + +type LayoutTree struct { + Windows map[int16]UIWindow +} + +func NewLayoutTree() *LayoutTree { + return &LayoutTree{ + Windows: make(map[int16]UIWindow), + } +} + +func (l *LayoutTree) AddWindow(idx int16, w UIWindow) { + l.Windows[idx] = w +} + +type CDashState struct { + Layout *LayoutTree +} + +func NewCDashState() *CDashState { + return &CDashState{ + Layout: NewLayoutTree(), + } +} + +type CDashDisplay struct { + WT *communication.WalkieTalkie + State *CDashState +} + +func NewCDashDisplay() (*CDashDisplay, error) { + // Look for the port + p, err := findDisplayPort() + if err != nil { + pLogger.Info("failed to find cdashdisplay port: %s", err.Error()) + return nil, err + } + + return &CDashDisplay{ + WT: p, + State: NewCDashState(), + }, nil +} + +func (d *CDashDisplay) CreateWindow(win UIWindow) (int16, error) { + bytes, err := helper.StructToBytes(win) + if err != nil { + return -1, err + } + + // Send the command + var wID packets.NewWindowID + err = d.WT.SendCommand(newWindowCMDID, bytes, &wID) + if err != nil { + return -1, err + } + + pLogger.Info(fmt.Sprintf("Recived ID message: %v", wID)) + + d.State.Layout.AddWindow(wID.ID, win) + + return wID.ID, nil +} + +func (d *CDashDisplay) DestroyWindow(wID int16) error { + type UIWindowDestructPacket struct { + WinID int16 + } + + packet := UIWindowDestructPacket{ + WinID: wID, + } + + bytes, err := helper.StructToBytes(packet) + if err != nil { + return err + } + + var ack packets.AckPacket + err = d.WT.SendCommand(destroyWindowCMDID, bytes, &ack) + if err != nil { + return err + } + + // NODE: add this + // d.State.Layout.RemoveWindow(wID) + + return nil +} diff --git a/cmd/cmd.go b/cmd/cmd.go index 54d2adc..4dc6a76 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -2,6 +2,8 @@ package cmd import ( + "context" + "log/slog" "os" "github.com/spf13/cobra" @@ -14,9 +16,14 @@ var rootCmd = &cobra.Command{ Long: `Allows the configuration and communication with the dashDisplay`, } +type loggerKey struct{} + // Execute adds all child commands to the root command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. -func Execute() { +func Execute(log *slog.Logger) { + ctx := context.WithValue(context.Background(), loggerKey{}, log) + rootCmd.SetContext(ctx) + err := rootCmd.Execute() if err != nil { os.Exit(1) diff --git a/cmd/tui.go b/cmd/tui.go index 316115a..4beb120 100644 --- a/cmd/tui.go +++ b/cmd/tui.go @@ -3,14 +3,22 @@ package cmd import ( "esdi/tui" "fmt" + "log/slog" "github.com/spf13/cobra" ) func tuiCmdAction(cmd *cobra.Command, args []string) { - err := tui.Run() + logger, ok := cmd.Context().Value(loggerKey{}).(*slog.Logger) + if !ok { + fmt.Printf("Error loading logger from context: %s\n", logger) + return + } + + err := tui.Run(logger) if err != nil { fmt.Printf("Error running TUI: %s\n", err.Error()) + return } } diff --git a/go.mod b/go.mod index 312613f..5ce39cc 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ require ( github.com/ESilva15/ESgoRepl v0.1.0 github.com/ESilva15/gobngsdk v0.0.2 github.com/ESilva15/goirsdk v0.2.0 - github.com/charmbracelet/bubbletea v1.3.10 github.com/gdamore/tcell/v2 v2.8.1 github.com/rivo/tview v0.42.0 github.com/spf13/cobra v1.8.1 @@ -15,25 +14,12 @@ require ( ) require ( - github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect - github.com/charmbracelet/lipgloss v1.1.0 // indirect - github.com/charmbracelet/x/ansi v0.10.1 // indirect - github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect - github.com/charmbracelet/x/term v0.2.1 // indirect - github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/gdamore/encoding v1.0.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect - github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/termenv v0.16.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect golang.org/x/sys v0.39.0 // indirect golang.org/x/text v0.21.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 901fd60..5d8cb47 100644 --- a/go.sum +++ b/go.sum @@ -4,23 +4,7 @@ github.com/ESilva15/gobngsdk v0.0.2 h1:N6stNOE14Wg80BAZMFu/Qd9Qa/J0DmExCfdPoDf7l github.com/ESilva15/gobngsdk v0.0.2/go.mod h1:cKLaZRgM0tGGXDvosaSjHnxeyC5Y6rg7zCLqEUJnOzw= github.com/ESilva15/goirsdk v0.2.0 h1:zmtYyH3ayGRcDfzzLrJmEpX6zdmCHTUqbs4tnV2hXmI= github.com/ESilva15/goirsdk v0.2.0/go.mod h1:5borQbw+L4fe9b58JFHRHZwrzz0sMVAteuYbRnFOc0Q= -github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= -github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= -github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= -github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= -github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= -github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= -github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= -github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= -github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ= -github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE= -github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8= -github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= -github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= -github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= -github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw= github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo= github.com/gdamore/tcell/v2 v2.8.1 h1:KPNxyqclpWpWQlPLx6Xui1pMk8S+7+R37h3g07997NU= @@ -31,18 +15,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= -github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= -github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= -github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= -github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= -github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= github.com/rivo/tview v0.42.0 h1:b/ftp+RxtDsHSaynXTbJb+/n/BxDEi+W3UfF5jILK6c= github.com/rivo/tview v0.42.0/go.mod h1:cSfIYfhpSGCjp3r/ECJb+GKS7cGJnqV8vfjQPwoXyfY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -56,16 +30,12 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 h1:UyzmZLoiDWMRywV4DUYb9Fbt8uiOSooupjTq10vpvnU= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= -github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= -golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= @@ -89,11 +59,9 @@ golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/main.go b/main.go index 2fc3a32..3e1185f 100644 --- a/main.go +++ b/main.go @@ -1,8 +1,23 @@ package main -import "esdi/cmd" +import ( + "esdi/cmd" + "log/slog" + "os" +) func main() { + output, err := os.OpenFile("./output.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0755) + if err != nil { + panic("failed to open logging file: " + err.Error()) + } + + logger := slog.New( + slog.NewTextHandler(output, &slog.HandlerOptions{ + Level: slog.LevelDebug, + }), + ) + // Launches the cobra package stuff - cmd.Execute() + cmd.Execute(logger) } diff --git a/peripheral/communication/packets/newWindowIDPakcket.go b/peripheral/communication/packets/newWindowIDPakcket.go new file mode 100644 index 0000000..baef6c1 --- /dev/null +++ b/peripheral/communication/packets/newWindowIDPakcket.go @@ -0,0 +1,20 @@ +package packets + +import ( + "esdi/peripheral/communication/constvar" +) + +type NewWindowID struct { + StartMarker byte + ID int16 + EndMarker byte +} + +func (pkt *NewWindowID) Validate() bool { + if pkt.StartMarker != constvar.StartOfText || + pkt.EndMarker != constvar.EndOfText { + return false + } + + return true +} diff --git a/peripheral/devices/cdash_display.go b/peripheral/devices/cdash_display.go index bd38573..2eca8f2 100644 --- a/peripheral/devices/cdash_display.go +++ b/peripheral/devices/cdash_display.go @@ -16,10 +16,11 @@ const ( newWindowCMDID types.Command = 3 destroyWindowCMDID types.Command = 4 moveWindowCMDID types.Command = 5 + newLayoutCMDID types.Command = 6 ) var ( - defaultDecorations = UIDecorations{ + DefaultDecorations = UIDecorations{ HasBorder: 1, BGColour: 0x1041, // Look in the eslabsCurses library for these colours FGColour: 0xffff, @@ -55,6 +56,29 @@ type UIWindow struct { Title [32]byte } +type LayoutTree struct { + Windows map[int]UIWindow +} + +func (l *LayoutTree) AddWindow(idx int, w UIWindow) { + l.Windows[idx] = w +} + +func NewLayoutTree() *LayoutTree { + return &LayoutTree{ + Windows: make(map[int]UIWindow), + } +} + +type CDashState struct { + Layout *LayoutTree +} + +// Well fuck it really, this will make do for now really lmao +var ( + state *CDashState +) + var CDashDisplay = Device{ ID: CDashDisplayDevID, Name: helper.B32("ESLabs CDashDisplay"), @@ -80,6 +104,13 @@ var CDashDisplay = Device{ ArgCheck: moveWindowArgCheck, Fn: moveWindow, }, + "new-layout": { + Identifier: newLayoutCMDID, + Name: "new-layout", + Desc: "creates a new layout", + ArgCheck: nil, + Fn: nil, + }, }, } @@ -121,7 +152,7 @@ func createWindow(dCMD *DeviceCMD, args []string) (types.Command, []byte, error) Width: uint16(width), Height: uint16(height), }, - Decor: defaultDecorations, + Decor: DefaultDecorations, Title: helper.B32(args[4]), } @@ -130,6 +161,13 @@ func createWindow(dCMD *DeviceCMD, args []string) (types.Command, []byte, error) return 0, []byte{}, err } + // All went well so far so we can update the state + if state == nil { + state = &CDashState{Layout: NewLayoutTree()} + } + + state.Layout.AddWindow(0, data) + return dCMD.GetIdentifier(), bytes, nil } diff --git a/peripheral/devices/devices.go b/peripheral/devices/devices.go index b3fd134..34d2684 100644 --- a/peripheral/devices/devices.go +++ b/peripheral/devices/devices.go @@ -18,9 +18,10 @@ var DeviceMap = map[int]Device{ } type Device struct { - ID uint8 - Name [32]byte - API map[string]DeviceCMD + ID uint8 + Name [32]byte + API map[string]DeviceCMD + State any } func (dev *Device) HasFunction(f string) *DeviceCMD { @@ -52,8 +53,6 @@ type DeviceCMD struct { Identifier types.Command Name string Desc string - Header DeviceCMDHeader - Data DeviceCMDPayload ArgCheck ArgCheckFn Fn DeviceCMDFn } @@ -80,3 +79,9 @@ func (dCMD *DeviceCMD) GetName() string { func (dCMD *DeviceCMD) GetDesc() string { return dCMD.Desc } + +type DeviceInstance[T any] struct { + Def *Device + ID uint8 + State T +} diff --git a/tui/internal/controllers/layout.go b/tui/internal/controllers/layout.go index 867079c..7d83e9a 100644 --- a/tui/internal/controllers/layout.go +++ b/tui/internal/controllers/layout.go @@ -2,11 +2,7 @@ package controllers import ( - "fmt" - "esdi/tui/internal/events" - "esdi/tui/internal/models" - "esdi/tui/internal/ui" ) type LayoutController struct { @@ -18,22 +14,17 @@ func NewLayoutController(bus *events.Bus) *LayoutController { EvBus: bus, } - bus.On(ui.CreateWindowEv{}, func(e any) { - ev := e.(ui.CreateWindowEv) - lc.createWindow(ev.Window) - }) - return lc } -func (lc *LayoutController) createWindow(win models.Window) { - lc.EvBus.Emit(ui.LogEv{ - Log: fmt.Sprintf("x : %d\n"+ - "y : %d\n"+ - "width : %d\n"+ - "height: %d\n"+ - "title : %s\n", win.X, win.Y, win.Width, win.Height, win.Title), - }) - - lc.EvBus.Emit(ui.WindowCreatedEv{Window: win}) -} +// func (lc *LayoutController) createWindow(win models.Window) { +// lc.EvBus.Emit(ui.LogEv{ +// Log: fmt.Sprintf("x : %d\n"+ +// "y : %d\n"+ +// "width : %d\n"+ +// "height: %d\n"+ +// "title : %s\n", win.X, win.Y, win.Width, win.Height, win.Title), +// }) +// +// lc.EvBus.Emit(ui.WindowCreatedEv{}) +// } diff --git a/tui/internal/controllers/mainc.go b/tui/internal/controllers/mainc.go index f08d63a..91a8917 100644 --- a/tui/internal/controllers/mainc.go +++ b/tui/internal/controllers/mainc.go @@ -2,11 +2,14 @@ package controllers import ( + "esdi/cdashdisplay" + helper "esdi/helpers" "esdi/peripheral" "esdi/tui/internal/dom" "esdi/tui/internal/events" "esdi/tui/internal/ui" "fmt" + "log/slog" "github.com/rivo/tview" ) @@ -17,46 +20,85 @@ type Ctrls struct { } type MainController struct { + Logger *slog.Logger App *tview.Application Dom *dom.DOM EvBus *events.Bus DevClerk *peripheral.PeripheralDeviceClerk + CDash *cdashdisplay.CDashDisplay } -func NewMainController() *MainController { +func NewMainController(logger *slog.Logger) *MainController { mc := &MainController{ + Logger: logger, App: tview.NewApplication(), Dom: dom.NewDOM(), EvBus: events.NewBus(), DevClerk: peripheral.NewPeripheralDeviceClerk(), + CDash: nil, } mc.EvBus.On(ui.RedrawEv{}, func(e any) { - mc.App.QueueUpdateDraw(func() {}) + go func() { + mc.App.QueueUpdateDraw(func() {}) + }() }) mc.EvBus.On(ui.ChangeFocusEv{}, func(e any) { - mc.App.SetFocus(e.(ui.ChangeFocusEv).Target) + go func() { + mc.App.SetFocus(e.(ui.ChangeFocusEv).Target) + }() }) mc.EvBus.On(ui.LogEv{}, func(e any) { - mc.EvBus.Emit(ui.PrintLogEv{Log: e.(ui.LogEv).Log}) + go func() { + mc.EvBus.Emit(ui.PrintLogEv{Log: e.(ui.LogEv).Log}) + }() + }) + + mc.EvBus.On(ui.CreateWindowEv{}, func(e any) { + go func() { + win := e.(ui.CreateWindowEv).Window + + uiWindow := cdashdisplay.UIWindow{ + Dims: cdashdisplay.UIDimensions{ + X0: win.X, + Y0: win.Y, + Width: win.Width, + Height: win.Height, + }, + Decor: cdashdisplay.DefaultDecorations, + Title: helper.B32(win.Title), + } + + wID, err := mc.CDash.CreateWindow(uiWindow) + if err != nil { + mc.EvBus.Emit(ui.PrintLogEv{Log: "failed to create window\n"}) + return + } + + mc.EvBus.Emit(ui.WindowCreatedEv{ID: wID, Title: win.Title}) + mc.EvBus.Emit(ui.PrintLogEv{Log: "Window created!\n"}) + }() + }) + + mc.EvBus.On(ui.DestroyWindowEv{}, func(e any) { + mc.Logger.Info(fmt.Sprintf("Called in to destroy win: %d", e.(ui.DestroyWindowEv).ID)) + mc.CDash.DestroyWindow(e.(ui.DestroyWindowEv).ID) }) mc.EvBus.On(ui.FindCDashDisplay{}, func(e any) { - err := mc.DevClerk.FindDevices() - if err != nil { - mc.EvBus.Emit(ui.LogEv{Log: "Error finding devices: " + err.Error() + "\n"}) - } + go func() { + mc.Logger.Info("Looking for CDashDisplay") + cdashdisplay.SetLogger(mc.Logger.With("[device]", "cdashdisplay")) - if len(mc.DevClerk.Devices) == 0 { - mc.EvBus.Emit(ui.LogEv{Log: " there are no devices\n"}) - } + display, err := cdashdisplay.NewCDashDisplay() + if err != nil { + return + } - for _, d := range mc.DevClerk.Devices { - msg := fmt.Sprintf(" [%2d] %s\n", d.ID, d.Name) - mc.EvBus.Emit(ui.LogEv{Log: msg}) - } + mc.CDash = display + }() }) return mc diff --git a/tui/internal/tui/tui.go b/tui/internal/tui/tui.go index 0d70e01..5944ef5 100644 --- a/tui/internal/tui/tui.go +++ b/tui/internal/tui/tui.go @@ -5,15 +5,16 @@ import ( "esdi/tui/internal/controllers" "esdi/tui/internal/views" "fmt" + "log/slog" "github.com/gdamore/tcell/v2" ) -func Start() error { +func Start(logger *slog.Logger) error { // The app starts running here! // // Set the event capture for the global app itself here - mc := controllers.NewMainController() + mc := controllers.NewMainController(logger.With("[ctrl]", "main")) mc.App.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { if event.Key() == tcell.KeyCtrlC || event.Rune() == 'q' { diff --git a/tui/internal/ui/events.go b/tui/internal/ui/events.go index dd91225..67273e8 100644 --- a/tui/internal/ui/events.go +++ b/tui/internal/ui/events.go @@ -30,10 +30,23 @@ type CreateWindowEv struct { Window models.Window } -type WindowCreatedEv struct { +type DestroyWindowEv struct { + ID int16 +} + +type LayoutRegisterWindowEv struct { Window models.Window } +type WindowCreatedEv struct { + ID int16 + Title string +} + +type WindowDestroyedEv struct { + ID int16 +} + type ErrorCreateWindowEv struct { Error error } diff --git a/tui/internal/views/layoutTool.go b/tui/internal/views/layoutTool.go index d368408..46182c7 100644 --- a/tui/internal/views/layoutTool.go +++ b/tui/internal/views/layoutTool.go @@ -18,6 +18,27 @@ const ( layoutToolActionPagesID = "layout-tool-action-pages" ) +func FindNodeByReference( + node *tview.TreeNode, + want any, +) *tview.TreeNode { + if node == nil { + return nil + } + + if node.GetReference() == want { + return node + } + + for _, child := range node.GetChildren() { + if found := FindNodeByReference(child, want); found != nil { + return found + } + } + + return nil +} + func BindWindowEvents( bus *events.Bus, doc *dom.DOM, @@ -38,9 +59,24 @@ func BindWindowEvents( return } - newWindow := tview.NewTreeNode(e.(ui.WindowCreatedEv).Window.Title) + newWindow := tview.NewTreeNode(e.(ui.WindowCreatedEv).Title). + SetReference(e.(ui.WindowCreatedEv).ID) root.AddChild(newWindow) }) + + bus.On(ui.WindowDestroyedEv{}, func(e any) { + root := tree.GetRoot() + if root == nil { + bus.Emit(ui.LogEv{Log: "unable to get current tree node"}) + } + + node := FindNodeByReference(root, e.(ui.WindowDestroyedEv).ID) + if node != nil { + root.RemoveChild(node) + } + // NODE: add a log here in case it fails so we know whats going on + }) + bus.On(ui.ErrorCreateWindowEv{}, func(e any) { bus.Emit(ui.LogEv{ Log: fmt.Sprintf( @@ -68,19 +104,21 @@ func layoutToolTreeViewEvents(bus *events.Bus, doc *dom.DOM, case 'x': // Delete selected window bus.Emit(ui.LogEv{Log: "calling delete window\n"}) + curNode := tree.GetCurrentNode() if curNode == nil { bus.Emit(ui.LogEv{Log: "unable to get current tree node"}) break } - root := tree.GetRoot() - if root == nil { - bus.Emit(ui.LogEv{Log: "unable to get current tree node"}) + ref := curNode.GetReference() + wID, ok := ref.(int16) + if !ok { + // Whatever, do something better here break } - root.RemoveChild(curNode) + bus.Emit(ui.DestroyWindowEv{ID: wID}) case 'm': // Go into move mode case 'e': diff --git a/tui/tui.go b/tui/tui.go index 875640f..58a141f 100644 --- a/tui/tui.go +++ b/tui/tui.go @@ -1,8 +1,11 @@ // Package tui package tui -import t "esdi/tui/internal/tui" +import ( + t "esdi/tui/internal/tui" + "log/slog" +) -func Run() error { - return t.Start() +func Run(logger *slog.Logger) error { + return t.Start(logger) }