From b8e7725510996e236e5f36774967dae6ea989917 Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Mon, 6 Jan 2025 14:41:43 +0000 Subject: [PATCH] Added cobra-cli to stop using the os.Args[] and a logger --- cmd.go | 22 ++++++ cmdLiveTelemetry.go | 49 ++++++++++++++ cmdOfflineTelemetry.go | 61 +++++++++++++++++ esdiTelemetry.go | 127 +++++++++++++++++++++++++++++++++++ go.mod | 7 +- go.sum | 16 +++-- logger/logger.go | 22 ++++++ main.go | 149 +---------------------------------------- 8 files changed, 299 insertions(+), 154 deletions(-) create mode 100644 cmd.go create mode 100644 cmdLiveTelemetry.go create mode 100644 cmdOfflineTelemetry.go create mode 100644 esdiTelemetry.go create mode 100644 logger/logger.go diff --git a/cmd.go b/cmd.go new file mode 100644 index 0000000..ecb028f --- /dev/null +++ b/cmd.go @@ -0,0 +1,22 @@ +package main + +import ( + "github.com/spf13/cobra" + "os" +) + +// rootCmd represents the base command when called without any subcommands +var rootCmd = &cobra.Command{ + Use: "esdi", + Short: "CLI for the dashDisplay project", + Long: `Allows the configuration and communication with the dashDisplay`, +} + +// 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() { + err := rootCmd.Execute() + if err != nil { + os.Exit(1) + } +} diff --git a/cmdLiveTelemetry.go b/cmdLiveTelemetry.go new file mode 100644 index 0000000..a2e5f9d --- /dev/null +++ b/cmdLiveTelemetry.go @@ -0,0 +1,49 @@ +package main + +import ( + "esdi/logger" + "esdi/sources/iracing" + + "github.com/spf13/cobra" +) + +func liveTelemetryCmdAction(cmd *cobra.Command, args []string) { + log := logger.GetInstance() + + ddPort, _ := cmd.Flags().GetString("port") + outputFile, _ := cmd.Flags().GetString("outputFile") + + log.Printf("Called `live`:\nPort: '%s'\nOutFile: '%s'\n", ddPort, outputFile) + + esdi, err := ESDIInit(ddPort, 115200) + if err != nil { + log.Fatalf("Failed to get Desktop Interface: %v", err) + } + + irsdk, err := iracing.Init(nil) + if err != nil { + log.Fatalf("Failed to create iRacing interface: %v", err) + } + + esdi.Source = &irsdk + + esdi.telemetry() +} + +// removeLabelCmd represents the removeLabel command +var liveTelemetryCmd = &cobra.Command{ + Use: "live", + Short: "stream data directly from the game", + Long: ``, + Run: liveTelemetryCmdAction, +} + +func init() { + rootCmd.AddCommand(liveTelemetryCmd) + + // Declare the flags for this command + liveTelemetryCmd.Flags().StringP("port", "p", "", "dashDisplay Port") + + // Mark the required ones + liveTelemetryCmd.MarkFlagRequired("port") +} diff --git a/cmdOfflineTelemetry.go b/cmdOfflineTelemetry.go new file mode 100644 index 0000000..c71f270 --- /dev/null +++ b/cmdOfflineTelemetry.go @@ -0,0 +1,61 @@ +package main + +import ( + "os" + + "esdi/logger" + "esdi/sources/iracing" + + "github.com/spf13/cobra" +) + +func offlineTelemetryCmdAction(cmd *cobra.Command, args []string) { + log := logger.GetInstance() + + ddPort, _ := cmd.Flags().GetString("port") + inFile, _ := cmd.Flags().GetString("in") + outFile, _ := cmd.Flags().GetString("out") + + log.Printf("Called `offline`:\nPort: '%s'\nSource: '%s'\nOutFile: '%s'\n", ddPort, inFile, outFile) + + esdi, err := ESDIInit(ddPort, 115200) + if err != nil { + log.Fatalf("Failed to get Desktop Interface: %v", err) + } + + file, err := os.Open(inFile) + if err != nil { + log.Fatalf("Failed to open IBT file: %v", err) + } + + irsdk, err := iracing.Init(file) + if err != nil { + log.Fatalf("Failed to create iRacing interface: %v", err) + } + + irsdk.SDK.FileToExport = outFile + esdi.Source = &irsdk + + esdi.telemetry() +} + +// removeLabelCmd represents the removeLabel command +var offlineTelemetryCmd = &cobra.Command{ + Use: "offline", + Short: "stream data from a file", + Long: ``, + Run: offlineTelemetryCmdAction, +} + +func init() { + rootCmd.AddCommand(offlineTelemetryCmd) + + // Declare the flags for this command + offlineTelemetryCmd.Flags().StringP("port", "p", "", "dashDisplay Port") + offlineTelemetryCmd.Flags().StringP("in", "i", "", "source file") + offlineTelemetryCmd.Flags().StringP("out", "o", "", "output file") + + // Mark the required ones + offlineTelemetryCmd.MarkFlagRequired("port") + offlineTelemetryCmd.MarkFlagRequired("in") +} diff --git a/esdiTelemetry.go b/esdiTelemetry.go new file mode 100644 index 0000000..2e77116 --- /dev/null +++ b/esdiTelemetry.go @@ -0,0 +1,127 @@ +package main + +import ( + "bytes" + "encoding/binary" + "fmt" + "log" + "os" + "os/signal" + "strings" + "syscall" + "time" +) + +type GameSource interface { + GetData(string) (interface{}, error) + UpdateData() error +} + +type DataPacket struct { + Speed int32 + Gear int32 + RPM int32 +} + +func msToKph(v float32) int { + return int((3600 * v) / 1000) +} + +func (e *ESDI) telemetry() { + // Set the handlers + sigc := make(chan os.Signal, 1) + signal.Notify(sigc, + syscall.SIGHUP, + syscall.SIGINT, + syscall.SIGTERM, + syscall.SIGQUIT, + ) + done := make(chan struct{}) + + go func() { + s := <-sigc + switch s { + case syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGHUP: + fmt.Printf("Received signal: %v\n", s) + fmt.Print("\033[?25h\033[2J\033[H") + } + close(done) + e.Close() + }() + + lastTime := time.Now().UnixMilli() + lastDataSent := time.Now().UnixMilli() + for { + select { + case <-done: + return + default: + time.Sleep(time.Second / 60) + + var err error + var buffer strings.Builder + buffer.WriteString("\033[?25l\033[2J\033[H") + + err = e.Source.UpdateData() + if err != nil { + fmt.Printf("could not update data: %v", err) + continue + } + + curGear, err := e.Source.GetData("Gear") + if err != nil { + log.Fatalf("could not get field `Gear`: %v", err) + } + + curRPM, err := e.Source.GetData("RPM") + if err != nil { + log.Fatalf("could not get field `RPM`: %v", err) + } + + curSpeed, err := e.Source.GetData("Speed") + if err != nil { + log.Fatalf("could not get field `Speed`: %v", err) + } + + gear := int32(curGear.(int)) + rpm := int32(curRPM.(float32)) + speed := int32(msToKph(curSpeed.(float32))) + + buffer.WriteString(fmt.Sprintf("Gear: %d, RPM: %d, Speed: %d", gear, rpm, speed)) + + curTime := time.Now().UnixMilli() + message := fmt.Sprintf("%d,%d\n", gear-1, rpm) + buffer.WriteString("\n" + message) + + messageWasSentMark := "N" + if curTime-lastDataSent > 25 { + packet := DataPacket{ + Speed: speed, + Gear: gear, + RPM: rpm, + } + + var buf bytes.Buffer + err = binary.Write(&buf, binary.LittleEndian, packet) + + _, err = e.SerialConn.Write(buf.Bytes()) + if err != nil { + log.Printf("Unable to write data: %v", err) + break + } + + messageWasSentMark = "Y" + lastDataSent = curTime + } + + buffer.WriteString(" -> " + messageWasSentMark) + if curTime-lastTime > 100 { + fmt.Print(buffer.String()) + lastTime = curTime + } + + } + } + + e.Close() +} diff --git a/go.mod b/go.mod index 0c4ccf7..70c90dc 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,15 @@ module esdi go 1.23.2 require ( - github.com/ESilva15/gobngsdk v0.0.1 - github.com/ESilva15/goirsdk v0.0.0-20241105180527-300c86190b42 + github.com/ESilva15/gobngsdk v0.0.2 + github.com/ESilva15/goirsdk v0.0.4 + github.com/spf13/cobra v1.8.1 github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 ) require ( + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect golang.org/x/sys v0.26.0 // indirect golang.org/x/text v0.19.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index a9e5697..e6267de 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,17 @@ -github.com/ESilva15/gobngsdk v0.0.1 h1:EbzIp3pTycqOXbGEX8omhhyaR+N9vixQ8/xSXCbYplQ= -github.com/ESilva15/gobngsdk v0.0.1/go.mod h1:3BpgdToILwl+w3IjUJinK9AzYqUefnQBJbfYlR6Ibbk= -github.com/ESilva15/goirsdk v0.0.0-20241105180527-300c86190b42 h1:66k8QHzJWrC0Ke2OGQvjFkQUusGfASOsLzfOS0uHoII= -github.com/ESilva15/goirsdk v0.0.0-20241105180527-300c86190b42/go.mod h1:Vtqm7KbFJ4pjDSz2pUI4TaMWtnVQXn5m7Ee3vmo9yNc= +github.com/ESilva15/gobngsdk v0.0.2 h1:N6stNOE14Wg80BAZMFu/Qd9Qa/J0DmExCfdPoDf7lco= +github.com/ESilva15/gobngsdk v0.0.2/go.mod h1:cKLaZRgM0tGGXDvosaSjHnxeyC5Y6rg7zCLqEUJnOzw= +github.com/ESilva15/goirsdk v0.0.4 h1:bF4roqOsY4RsKzBjGTsuPuETTtmwccnzYBB2TlQ9mfE= +github.com/ESilva15/goirsdk v0.0.4/go.mod h1:Vtqm7KbFJ4pjDSz2pUI4TaMWtnVQXn5m7Ee3vmo9yNc= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +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= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= diff --git a/logger/logger.go b/logger/logger.go new file mode 100644 index 0000000..5e20bd6 --- /dev/null +++ b/logger/logger.go @@ -0,0 +1,22 @@ +package logger + +import ( + "log" + "os" + "sync" +) + +var l *log.Logger +var once sync.Once + +func createLogger() { + l = log.New(os.Stdout, "esdi", log.LstdFlags | log.Lshortfile) +} + +func GetInstance() *log.Logger { + once.Do(func() { + createLogger() + }) + + return l +} diff --git a/main.go b/main.go index 9c5fff9..6880c56 100644 --- a/main.go +++ b/main.go @@ -1,152 +1,5 @@ package main -import ( - "bytes" - "encoding/binary" - "esdi/sources/iracing" - "fmt" - "log" - "os" - "os/signal" - "strings" - "syscall" - "time" -) - -type GameSource interface { - GetData(string) (interface{}, error) - UpdateData() error -} - -type DataPacket struct { - Speed int32 - Gear int32 - RPM int32 -} - -func msToKph(v float32) int { - return int((3600 * v) / 1000) -} - func main() { - // Set the handlers - sigc := make(chan os.Signal, 1) - signal.Notify(sigc, - syscall.SIGHUP, - syscall.SIGINT, - syscall.SIGTERM, - syscall.SIGQUIT, - ) - done := make(chan struct{}) - - if len(os.Args) == 3 { - fmt.Printf("Port: %s\n", os.Args[1]) - fmt.Printf("File: %s\n", os.Args[2]) - } else { - log.Fatal("Wrong usage.") - } - - esdi, err := ESDIInit(os.Args[1], 115200) - if err != nil { - log.Fatalf("Failed to get Desktop Interface: %v", err) - } - - file, err := os.Open(os.Args[2]) - if err != nil { - log.Fatalf("Failed to open IBT file: %v", err) - } - - irsdk, err := iracing.Init(file) - if err != nil { - log.Fatalf("Failed to create iRacing interface: %v", err) - } - - esdi.Source = &irsdk - - go func() { - s := <-sigc - switch s { - case syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGHUP: - fmt.Printf("Received signal: %v\n", s) - fmt.Print("\033[?25h\033[2J\033[H") - } - close(done) - esdi.Close() - }() - - lastTime := time.Now().UnixMilli() - lastDataSent := time.Now().UnixMilli() - for { - select { - case <-done: - return - default: - time.Sleep(time.Second / 60) - - var err error - var buffer strings.Builder - buffer.WriteString("\033[?25l\033[2J\033[H") - - err = esdi.Source.UpdateData() - if err != nil { - fmt.Printf("could not update data: %v", err) - continue - } - - curGear, err := esdi.Source.GetData("Gear") - if err != nil { - log.Fatalf("could not get field `Gear`: %v", err) - } - - curRPM, err := esdi.Source.GetData("RPM") - if err != nil { - log.Fatalf("could not get field `RPM`: %v", err) - } - - curSpeed, err := esdi.Source.GetData("Speed") - if err != nil { - log.Fatalf("could not get field `Speed`: %v", err) - } - - gear := int32(curGear.(int)) - rpm := int32(curRPM.(float32)) - speed := int32(msToKph(curSpeed.(float32))) - - buffer.WriteString(fmt.Sprintf("Gear: %d, RPM: %d, Speed: %d", gear, rpm, speed)) - - curTime := time.Now().UnixMilli() - message := fmt.Sprintf("%d,%d\n", gear-1, rpm) - buffer.WriteString("\n" + message) - - messageWasSentMark := "N" - if curTime-lastDataSent > 25 { - packet := DataPacket{ - Speed: speed, - Gear: gear, - RPM: rpm, - } - - var buf bytes.Buffer - err = binary.Write(&buf, binary.LittleEndian, packet) - - _, err = esdi.SerialConn.Write(buf.Bytes()) - if err != nil { - log.Printf("Unable to write data: %v", err) - break - } - - messageWasSentMark = "Y" - lastDataSent = curTime - } - - buffer.WriteString(" -> " + messageWasSentMark) - if curTime-lastTime > 100 { - fmt.Print(buffer.String()) - lastTime = curTime - } - - } - } - - esdi.Close() + Execute() }