diff --git a/tui/internal/controllers/mainc.go b/tui/internal/controllers/mainc.go index a94d9a5..a13c181 100644 --- a/tui/internal/controllers/mainc.go +++ b/tui/internal/controllers/mainc.go @@ -27,22 +27,24 @@ type Ctrls struct { } type MainController struct { - Logger *slog.Logger - App *tview.Application - Dom *dom.DOM - EvBus *events.Bus - DevClerk *peripheral.PeripheralDeviceClerk - CDash *cdashdisplay.CDashDisplay + Logger *slog.Logger + App *tview.Application + Dom *dom.DOM + EvBus *events.Bus + DevClerk *peripheral.PeripheralDeviceClerk + CDash *cdashdisplay.CDashDisplay + StreamStrl *StreamingCtrl } func NewMainController(logger *slog.Logger) *MainController { pLogger = logger mc := &MainController{ - App: tview.NewApplication(), - Dom: dom.NewDOM(), - EvBus: events.NewBus(), - DevClerk: peripheral.NewPeripheralDeviceClerk(), - CDash: nil, + App: tview.NewApplication(), + Dom: dom.NewDOM(), + EvBus: events.NewBus(), + DevClerk: peripheral.NewPeripheralDeviceClerk(), + CDash: nil, + StreamStrl: NewStreamingCtrl(), } mc.EvBus.On(ui.RedrawEv{}, func(e any) { @@ -186,5 +188,13 @@ func NewMainController(logger *slog.Logger) *MainController { mc.App.Draw() }) + mc.EvBus.On(ui.StartStreamingReqEv{}, func(e any) { + mc.StreamStrl.Start(mc.EvBus) + }) + + mc.EvBus.On(ui.StopStreamingReqEv{}, func(e any) { + mc.StreamStrl.Stop(mc.EvBus) + }) + return mc } diff --git a/tui/internal/controllers/sources.go b/tui/internal/controllers/sources.go deleted file mode 100644 index 2d32936..0000000 --- a/tui/internal/controllers/sources.go +++ /dev/null @@ -1 +0,0 @@ -package controllers diff --git a/tui/internal/controllers/streaming.go b/tui/internal/controllers/streaming.go new file mode 100644 index 0000000..424947e --- /dev/null +++ b/tui/internal/controllers/streaming.go @@ -0,0 +1,189 @@ +package controllers + +import ( + esdi "esdi/oldEsdi" + "fmt" + "log" + "os" + "strings" + "sync" + "time" + + "esdi/tui/internal/events" + "esdi/tui/internal/ui" + + "github.com/ESilva15/goirsdk" +) + +// Car data lengths +const ( + SpeedLen = 5 + GearLen = 3 + RpmLen = 6 + BrakeBiasLen = 6 +) + +type StreamingCtrl struct { + LastMessageTime time.Time + Data *esdi.SimulationData + DataView *esdi.DataPacket + InitialTime time.Time + LastTime time.Time + Mut sync.Mutex + Irsdk *goirsdk.IBT +} + +func NewStreamingCtrl() *StreamingCtrl { + return &StreamingCtrl{ + Data: &esdi.SimulationData{}, + DataView: &esdi.DataPacket{}, + InitialTime: time.Now(), + LastTime: time.Now(), + } +} + +func (sc *StreamingCtrl) Stop(bus *events.Bus) { +} + +func (sc *StreamingCtrl) Start(bus *events.Bus) { + // Open the telemetry file + file, err := os.Open("/home/esilva/Desktop/projetos/simracing_peripherals/testTelemetry/supercars_indianapolis.ibt") + if err != nil { + log.Fatalf("Failed to open IBT file: %v", err) + } + + sc.Irsdk, err = goirsdk.Init(file, "./out.ibt", "./out.yaml") + if err != nil { + log.Fatalf("Failed to load iRacing data") + } + + startTime := time.Now() + + go sc.ReadData() + + // global irsdk is now here + go func() { + ticker := time.NewTicker(time.Second / 60) + defer ticker.Stop() + + counter := 0 + for t := range ticker.C { + // whatever + currentTime := time.Now() + + delta := currentTime.Sub(startTime) + + str := sc.Stringified() + fmt.Sprintf("%v - %7d\n%5f - %5f", + t.UTC(), counter, delta.Seconds(), delta.Seconds()/float64(counter)) + + bus.Emit(ui.StreamDataEv{Str: str}) + counter++ + } + }() +} + +func (sc *StreamingCtrl) ReadData() { + dataReaderTicker := time.NewTicker(time.Second / 60) + defer dataReaderTicker.Stop() + + sc.InitialTime = time.Now() + for _ = range dataReaderTicker.C { + var err error + + sc.Mut.Lock() + _, err = sc.Irsdk.Update(time.Millisecond * 100) + if err != nil { + fmt.Printf("could not update data: %v", err) + continue + } + sc.Mut.Unlock() + + sc.getVehicleData() + + // Test the actual dataPacket we are sending over the wire + copyBytes(sc.DataView.Speed[:], SpeedLen, fmt.Sprintf("%3d", sc.Data.Speed)) + copyBytes(sc.DataView.Gear[:], GearLen, fmt.Sprintf("%2d", sc.Data.Gear)) + copyBytes(sc.DataView.RPM[:], RpmLen, fmt.Sprintf("%3d", sc.Data.RPM)) + + sc.LastMessageTime = time.Now() + } +} + +func (sc *StreamingCtrl) getVehicleData() { + sc.Mut.Lock() + curGear := sc.Irsdk.Vars.Vars["Gear"].Value + curRPM := sc.Irsdk.Vars.Vars["RPM"].Value + curSpeed := sc.Irsdk.Vars.Vars["Speed"].Value + + sc.Data.Gear = int32(curGear.(int)) + sc.Data.RPM = int32(curRPM.(float32)) + sc.Data.Speed = int32(msToKph(curSpeed.(float32))) + sc.Mut.Unlock() +} + +func copyBytes(dest []byte, destSize int, src string) { + copy(dest[:], []byte(src)) + dest[min(destSize-1, len(src))] = '\x00' +} + +func msToKph(v float32) int { + return int((3600 * v) / 1000) +} + +// Move this function to the views +func (sc *StreamingCtrl) Stringified() string { + var buffer strings.Builder + + sc.Mut.Lock() + sessionTimeR := sc.Irsdk.Vars.Vars["SessionTime"].Value + sessionTime := float64(sessionTimeR.(float64)) + + currTime := time.Now() + delta := currTime.Sub(sc.LastTime) + buffer.WriteString(fmt.Sprintf("[%s]\n", currTime.Format("2006/01/02 15:04:05.000"))) + buffer.WriteString(fmt.Sprintf("Delta: %d [%f]\n\n", delta.Milliseconds(), 1000.0/60.0)) + sc.LastTime = currTime + + elapsed := currTime.Sub(sc.InitialTime) + softwareElapsed := time.Unix(0, 0).Add(elapsed).Format("04:05.000") + sessionElapsed := time.Unix(0, 0). + Add(time.Duration(sessionTime * float64(time.Second))). + Format("04:05.000") + + buffer.WriteString(fmt.Sprintf("Elapsed (software): %s\n", + softwareElapsed)) + buffer.WriteString(fmt.Sprintf("Elapsed (session): %s\n\n", + sessionElapsed)) + + // buffer.WriteString("Car data:\n") + buffer.WriteString(fmt.Sprintf("Gear: %d, RPM: %d, Speed: %d\n\n", + sc.Data.Gear, sc.Data.RPM, sc.Data.Speed)) + + // buffer.WriteString("Fuel data:\n") + // buffer.WriteString(fmt.Sprintf("Fuel Est: %s\n\n", e.dataPacket.FuelEst)) + + // buffer.WriteString("Lap data:\n") + // buffer.WriteString(fmt.Sprintf("Delta: [%s] [%f] [%s]\n", e.dataPacket.DeltaToBestLap, + // e.data.LapDeltaFloat, lapTimeDeltaRepresentation(e.data.LapDeltaFloat))) + // buffer.WriteString(fmt.Sprintf("LapTime: %s\n", e.dataPacket.CurrLapTime)) + // buffer.WriteString(fmt.Sprintf("Best Lap Time: %s\n", e.dataPacket.BestLapTime)) + // buffer.WriteString(fmt.Sprintf("Last Lap Time: %s\n", e.dataPacket.LastLapTime)) + // buffer.WriteString(fmt.Sprintf("LapBestNLapTi: %f\n\n", e.data.LapBestNLapTime)) + + // buffer.WriteString("Position data:\n") + // buffer.WriteString(fmt.Sprintf("Pos: %d\n", e.dataPacket.Position)) + + // for p, v := range e.dataPacket.Standings { + // s := fmt.Sprintf("[%2d] %s %-16s %-16s\n", + // p+1, v.Lap, string(bytes.Trim(v.DriverName[:], "\x00")), v.TimeBehindString) + // buffer.WriteString(s) + // } + + // buffer.WriteString(fmt.Sprintf("Size: %v\n", binary.Size(DataPacket{}))) + // buffer.WriteString(fmt.Sprintf("Recv: %d\n", e.data.Recv)) + // buffer.WriteString(fmt.Sprintf("Recv Err: %v\n", e.data.ReadError)) + + sc.Mut.Unlock() + + return buffer.String() +} diff --git a/tui/internal/ui/events.go b/tui/internal/ui/events.go index df4687d..49ffce6 100644 --- a/tui/internal/ui/events.go +++ b/tui/internal/ui/events.go @@ -93,3 +93,14 @@ type ErrorFormParseEv struct { type FindCDashDisplay struct{} // Device events -------------------------------------------------------------- + +// + Stream events ------------------------------------------------------------ + +type StartStreamingReqEv struct{} +type StopStreamingReqEv struct{} + +type StreamDataEv struct { + Str string +} + +// - Stream events ------------------------------------------------------------ diff --git a/tui/internal/views/stream.go b/tui/internal/views/stream.go index f7ef1dc..f8f1d88 100644 --- a/tui/internal/views/stream.go +++ b/tui/internal/views/stream.go @@ -1,18 +1,11 @@ package views import ( - esdi "esdi/oldEsdi" "esdi/tui/internal/dom" "esdi/tui/internal/events" "esdi/tui/internal/ui" - "fmt" - "log" "os" - "strings" - "sync" - "time" - "github.com/ESilva15/goirsdk" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) @@ -29,54 +22,6 @@ const ( streamingBoxID = "streaming-box" ) -var ( - lastMessageTime time.Time - data *esdi.SimulationData = &esdi.SimulationData{} - dataView *esdi.DataPacket = &esdi.DataPacket{} - initialTime = time.Now() - lastTime = time.Now() - mu sync.Mutex - irsdk *goirsdk.IBT -) - -func setUpSource(bus *events.Bus, doc *dom.DOM, tv *tview.TextView) { - // Open the telemetry file - file, err := os.Open("/home/esilva/Desktop/projetos/simracing_peripherals/testTelemetry/supercars_indianapolis.ibt") - if err != nil { - log.Fatalf("Failed to open IBT file: %v", err) - } - - irsdk, err = goirsdk.Init(file, "./out.ibt", "./out.yaml") - if err != nil { - log.Fatalf("Failed to load iRacing data") - } - - startTime := time.Now() - - go readData() - - // global irsdk is now here - go func() { - ticker := time.NewTicker(time.Second / 60) - defer ticker.Stop() - - counter := 0 - for t := range ticker.C { - // whatever - currentTime := time.Now() - - delta := currentTime.Sub(startTime) - - str := stringified() + fmt.Sprintf("%v - %7d\n%5f - %5f", - t.UTC(), counter, delta.Seconds(), delta.Seconds()/float64(counter)) - - tv.SetText(str) - bus.Emit(ui.ForceRedraw{}) - counter++ - } - }() -} - func streamingWindow(bus *events.Bus, doc *dom.DOM) { bus.Emit(ui.LogEv{Log: "creating streaming window"}) @@ -84,9 +29,9 @@ func streamingWindow(bus *events.Bus, doc *dom.DOM) { box := tview.NewTextView() box.SetBorder(true).SetTitle("STREAMING") box.SetInputCapture(func(ev *tcell.EventKey) *tcell.EventKey { - switch ev.Rune() { - case 'q': - // well, I really need to design the UI to work properly + switch ev.Key() { + case tcell.KeyEsc: + // well, I really need to design the UI (as if lmao) better os.Exit(0) } @@ -119,148 +64,12 @@ func streamingWindow(bus *events.Bus, doc *dom.DOM) { // Add it to the action pages and view it AddAndShowPage(bus, doc, actionPages, boxNode, true) - setUpSource(bus, doc, box) -} + bus.Emit(ui.StartStreamingReqEv{}) -func stringified() string { - var buffer strings.Builder + bus.On(ui.StreamDataEv{}, func(e any) { + sd := e.(ui.StreamDataEv) + box.SetText(sd.Str) - mu.Lock() - sessionTimeR := irsdk.Vars.Vars["SessionTime"].Value - sessionTime := float64(sessionTimeR.(float64)) - - currTime := time.Now() - delta := currTime.Sub(lastTime) - buffer.WriteString(fmt.Sprintf("[%s]\n", currTime.Format("2006/01/02 15:04:05.000"))) - buffer.WriteString(fmt.Sprintf("Delta: %d [%f]\n\n", delta.Milliseconds(), 1000.0/60.0)) - lastTime = currTime - - elapsed := currTime.Sub(initialTime) - softwareElapsed := time.Unix(0, 0).Add(elapsed).Format("04:05.000") - sessionElapsed := time.Unix(0, 0). - Add(time.Duration(sessionTime * float64(time.Second))). - Format("04:05.000") - - buffer.WriteString(fmt.Sprintf("Elapsed (software): %s\n", - softwareElapsed)) - buffer.WriteString(fmt.Sprintf("Elapsed (session): %s\n\n", - sessionElapsed)) - - // buffer.WriteString("Car data:\n") - buffer.WriteString(fmt.Sprintf("Gear: %d, RPM: %d, Speed: %d\n\n", - data.Gear, data.RPM, data.Speed)) - - // buffer.WriteString("Fuel data:\n") - // buffer.WriteString(fmt.Sprintf("Fuel Est: %s\n\n", e.dataPacket.FuelEst)) - - // buffer.WriteString("Lap data:\n") - // buffer.WriteString(fmt.Sprintf("Delta: [%s] [%f] [%s]\n", e.dataPacket.DeltaToBestLap, - // e.data.LapDeltaFloat, lapTimeDeltaRepresentation(e.data.LapDeltaFloat))) - // buffer.WriteString(fmt.Sprintf("LapTime: %s\n", e.dataPacket.CurrLapTime)) - // buffer.WriteString(fmt.Sprintf("Best Lap Time: %s\n", e.dataPacket.BestLapTime)) - // buffer.WriteString(fmt.Sprintf("Last Lap Time: %s\n", e.dataPacket.LastLapTime)) - // buffer.WriteString(fmt.Sprintf("LapBestNLapTi: %f\n\n", e.data.LapBestNLapTime)) - - // buffer.WriteString("Position data:\n") - // buffer.WriteString(fmt.Sprintf("Pos: %d\n", e.dataPacket.Position)) - - // for p, v := range e.dataPacket.Standings { - // s := fmt.Sprintf("[%2d] %s %-16s %-16s\n", - // p+1, v.Lap, string(bytes.Trim(v.DriverName[:], "\x00")), v.TimeBehindString) - // buffer.WriteString(s) - // } - - // buffer.WriteString(fmt.Sprintf("Size: %v\n", binary.Size(DataPacket{}))) - // buffer.WriteString(fmt.Sprintf("Recv: %d\n", e.data.Recv)) - // buffer.WriteString(fmt.Sprintf("Recv Err: %v\n", e.data.ReadError)) - - mu.Unlock() - - return buffer.String() -} - -func msToKph(v float32) int { - return int((3600 * v) / 1000) -} - -func lapTimeRepresentation(t float32, f string) string { - if t < 0 { - t = 0 - } - - wholeSeconds := int64(t) - lapTime := time.Unix(wholeSeconds, int64((t-float32(wholeSeconds))*1e9)) - - return lapTime.Format(f) -} - -func lapTimeDeltaRepresentation(t float32) string { - sign := '-' - if t < 0 { - sign = '+' - t = -t - } - - // Cap to 99.9 max - if t > 99.9 { - t = 99.9 - } - - if t >= 1 { - // Round to nearest tenth - rounded := float32(int(t*10+0.5)) / 10 - return fmt.Sprintf("%c%.1f", sign, rounded) - } - - // t < 1: round to nearest tenth and remove leading zero (e.g., "0.1" → ".1") - rounded := float32(int(t*10+0.5)) / 10 - s := fmt.Sprintf("%.1f", rounded) - if strings.HasPrefix(s, "0") { - s = s[1:] - } - return fmt.Sprintf("%c%s", sign, s) -} - -func readData() { - dataReaderTicker := time.NewTicker(time.Second / 60) - defer dataReaderTicker.Stop() - - initialTime = time.Now() - for _ = range dataReaderTicker.C { - var err error - - mu.Lock() - _, err = irsdk.Update(time.Millisecond * 100) - if err != nil { - fmt.Printf("could not update data: %v", err) - continue - } - mu.Unlock() - - getVehicleData() - - // Test the actual dataPacket we are sending over the wire - copyBytes(dataView.Speed[:], SpeedLen, fmt.Sprintf("%3d", data.Speed)) - copyBytes(dataView.Gear[:], GearLen, fmt.Sprintf("%2d", data.Gear)) - copyBytes(dataView.RPM[:], RpmLen, fmt.Sprintf("%3d", data.RPM)) - - lastMessageTime = time.Now() - } -} - -func getVehicleData() { - mu.Lock() - curGear := irsdk.Vars.Vars["Gear"].Value - curRPM := irsdk.Vars.Vars["RPM"].Value - curSpeed := irsdk.Vars.Vars["Speed"].Value - - data.Gear = int32(curGear.(int)) - data.RPM = int32(curRPM.(float32)) - data.Speed = int32(msToKph(curSpeed.(float32))) - mu.Unlock() -} - -func copyBytes(dest []byte, destSize int, src string) { - copy(dest[:], []byte(src)) - dest[min(destSize-1, len(src))] = '\x00' + bus.Emit(ui.ForceRedraw{}) + }) }