Trying to figure a way of mapping the telemetry agents fields back to my UI
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
package iracing
|
||||
|
||||
import (
|
||||
"esdi/telemetry"
|
||||
)
|
||||
|
||||
type FieldMapper struct {
|
||||
SDKKey string
|
||||
DataType telemetry.DataType
|
||||
Transform func(any) uint64
|
||||
}
|
||||
|
||||
// NOTE: Update the iracing SDK to write data to the same map ALWAYS, then
|
||||
// I can bind that address and read directly from there on the transform
|
||||
type boundField struct {
|
||||
Key string
|
||||
ID telemetry.FieldID
|
||||
Transform func(any, *telemetry.TelemetryField)
|
||||
}
|
||||
|
||||
var internalToSDKFieldNames = map[telemetry.FieldID]string{
|
||||
telemetry.Speed: "Speed",
|
||||
telemetry.Gear: "Gear",
|
||||
telemetry.RPM: "RPM",
|
||||
}
|
||||
@@ -2,12 +2,15 @@ package iracing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
conv "esdi/conversions"
|
||||
"esdi/telemetry"
|
||||
telem "esdi/telemetry"
|
||||
|
||||
"github.com/ESilva15/goirsdk"
|
||||
@@ -15,11 +18,13 @@ import (
|
||||
|
||||
// IRacing is our iRacing telemetry data provider - its a TelemetryProvider interface
|
||||
type IRacing struct {
|
||||
SDK *goirsdk.IBT
|
||||
logger *slog.Logger
|
||||
SDK *goirsdk.IBT
|
||||
|
||||
// Data Handling
|
||||
mut sync.Mutex
|
||||
data *telem.TelemetryData
|
||||
mut sync.Mutex
|
||||
data *telem.TelemetryData
|
||||
activeBindings []boundField
|
||||
|
||||
// Timing information
|
||||
ticker *time.Ticker // ticker will keep polling intervals constant
|
||||
@@ -27,10 +32,10 @@ type IRacing struct {
|
||||
// Stream
|
||||
streamCh chan telem.TelemetryData
|
||||
streamCancel context.CancelFunc
|
||||
// isRunning bool
|
||||
}
|
||||
|
||||
func NewIRacingProvider(
|
||||
logger *slog.Logger,
|
||||
source string,
|
||||
telemOut string,
|
||||
yamlOut string,
|
||||
@@ -52,6 +57,7 @@ func NewIRacingProvider(
|
||||
}
|
||||
|
||||
return &IRacing{
|
||||
logger: logger,
|
||||
SDK: sdk,
|
||||
data: telem.NewTelemetryData(),
|
||||
streamCh: make(chan telem.TelemetryData),
|
||||
@@ -68,7 +74,13 @@ func (i *IRacing) stream(ctx context.Context) {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-i.ticker.C:
|
||||
i.ReadData()
|
||||
i.readData()
|
||||
|
||||
// Create a snapshot
|
||||
i.mut.Lock()
|
||||
// snapshot := *i.data
|
||||
i.logger.Debug(fmt.Sprintf("%v\n", i.data.Values))
|
||||
i.mut.Unlock()
|
||||
|
||||
// Publish data
|
||||
select {
|
||||
@@ -81,7 +93,7 @@ func (i *IRacing) stream(ctx context.Context) {
|
||||
}()
|
||||
}
|
||||
|
||||
func (i *IRacing) ReadData() {
|
||||
func (i *IRacing) readData() {
|
||||
i.mut.Lock()
|
||||
defer i.mut.Unlock()
|
||||
|
||||
@@ -92,37 +104,21 @@ func (i *IRacing) ReadData() {
|
||||
return
|
||||
}
|
||||
|
||||
i.readVehicleData()
|
||||
// Read binded data
|
||||
for _, b := range i.activeBindings {
|
||||
v := i.SDK.Vars.Vars[b.Key].Value
|
||||
// NOTE: for the love of god, find a way of avoiding this shit
|
||||
b.Transform(v, &i.data.Values[b.ID+telem.FirstField])
|
||||
}
|
||||
|
||||
i.data.PenultimateDataPoll = i.data.LastDataPoll
|
||||
i.data.LastDataPoll = time.Now()
|
||||
}
|
||||
|
||||
func (i *IRacing) readVehicleData() {
|
||||
curGear := i.SDK.Vars.Vars["Gear"].Value
|
||||
curRPM := i.SDK.Vars.Vars["RPM"].Value
|
||||
curSpeed := i.SDK.Vars.Vars["Speed"].Value
|
||||
|
||||
speed := conv.MsToKph(curSpeed.(float32))
|
||||
gear := uint8(curGear.(int))
|
||||
rpm := uint16(curRPM.(float32))
|
||||
|
||||
i.data.Values[telem.Speed] = telem.TelemetryField{
|
||||
Type: telem.DataTypeUINT16,
|
||||
Raw: uint64(speed),
|
||||
}
|
||||
|
||||
i.data.Values[telem.Gear] = telem.TelemetryField{
|
||||
Type: telem.DataTypeUINT8,
|
||||
Raw: uint64(gear),
|
||||
}
|
||||
|
||||
i.data.Values[telem.RPM] = telem.TelemetryField{
|
||||
Type: telem.DataTypeUINT16,
|
||||
Raw: uint64(rpm),
|
||||
}
|
||||
}
|
||||
// Telemetry Provider Interface
|
||||
|
||||
// Stream returns a channel that we will use to funnel the telemetry data back to the
|
||||
// UI, which then should broadcast it to the devices
|
||||
func (i *IRacing) Stream() (<-chan telem.TelemetryData, error) {
|
||||
var ctx context.Context
|
||||
ctx, i.streamCancel = context.WithCancel(context.Background())
|
||||
@@ -132,3 +128,46 @@ func (i *IRacing) Stream() (<-chan telem.TelemetryData, error) {
|
||||
|
||||
return i.streamCh, nil
|
||||
}
|
||||
|
||||
func (i *IRacing) Subscribe(requestFields []telem.FieldID) {
|
||||
i.logger.Debug(fmt.Sprintf("Len Req: %d\n", len(requestFields)))
|
||||
|
||||
i.activeBindings = make([]boundField, 0, len(requestFields))
|
||||
|
||||
for _, id := range requestFields {
|
||||
// Translate the UI FieldIDs to this provider's field names
|
||||
sdkKey, ok := internalToSDKFieldNames[id+telem.FirstField]
|
||||
if !ok {
|
||||
i.logger.Debug("failed to get internal id")
|
||||
// Need to find a way to pass a message saying something wasn't right
|
||||
continue
|
||||
}
|
||||
|
||||
binding := boundField{
|
||||
Key: sdkKey,
|
||||
ID: id,
|
||||
}
|
||||
|
||||
switch id {
|
||||
case telem.Speed - telem.FirstField:
|
||||
binding.Transform = func(v any, out *telem.TelemetryField) {
|
||||
out.Type = telemetry.DataTypeUINT16
|
||||
out.Raw = uint64(conv.MsToKph(v.(float32)))
|
||||
}
|
||||
case telem.Gear - telem.FirstField:
|
||||
binding.Transform = func(v any, out *telem.TelemetryField) {
|
||||
out.Type = telemetry.DataTypeUINT8
|
||||
out.Raw = uint64(v.(int))
|
||||
}
|
||||
case telem.RPM - telem.FirstField:
|
||||
binding.Transform = func(v any, out *telem.TelemetryField) {
|
||||
out.Type = telemetry.DataTypeUINT16
|
||||
out.Raw = uint64(uint16(v.(float32)))
|
||||
}
|
||||
}
|
||||
|
||||
i.activeBindings = append(i.activeBindings, binding)
|
||||
}
|
||||
|
||||
i.logger.Debug(fmt.Sprintf("Subscribed: %+v\n", i.activeBindings))
|
||||
}
|
||||
|
||||
@@ -3,4 +3,5 @@ package telemetry
|
||||
|
||||
type TelemetryProvider interface {
|
||||
Stream() (<-chan TelemetryData, error)
|
||||
Subscribe([]FieldID)
|
||||
}
|
||||
|
||||
@@ -78,6 +78,9 @@ func (mc *DeviceController) AddDeviceAPIListItems() {
|
||||
"streaming-tool",
|
||||
mc.StreamCtrl.StreamView.TextView,
|
||||
)
|
||||
|
||||
mc.StreamCtrl.SetInternalState()
|
||||
|
||||
mc.App.SetFocus(mc.StreamCtrl.StreamView.TextView)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"esdi/telemetry"
|
||||
"esdi/tui/internal/services"
|
||||
"esdi/tui/internal/views"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
@@ -75,10 +77,27 @@ func (sc *StreamingCtrl) Start() {
|
||||
isDrawing.Store(true)
|
||||
|
||||
sc.App.QueueUpdateDraw(func() {
|
||||
sc.Messages <- "got data\n"
|
||||
sc.Messages <- fmt.Sprintf("got data: %+v\n", &msg)
|
||||
sc.StreamView.Update(&msg)
|
||||
isDrawing.Store(false)
|
||||
})
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// SetInternalState is used to update the stuff in here, for example, the user
|
||||
// goes into the layout tool, sets up the data to transmit to his devices and
|
||||
// then comes here to stream that data. We call this to set the fields the user
|
||||
// has subscrived to in his tooling
|
||||
func (sc *StreamingCtrl) SetInternalState() {
|
||||
// Get the subscribed fields
|
||||
fields := make([]telemetry.FieldID, 0, len(sc.Service.CDash.State.Layout.Windows))
|
||||
for _, w := range sc.Service.CDash.State.Layout.Windows {
|
||||
fieldID, _ := telemetry.GetFieldID(w.UIData.TelemetryField)
|
||||
fields = append(fields, fieldID-telemetry.FirstField)
|
||||
}
|
||||
|
||||
sc.TelemServ.ActiveProvider.Subscribe(fields)
|
||||
|
||||
sc.Messages <- fmt.Sprintf("Subscribed Fields: %+v [%d]\n", fields, len(fields))
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package services
|
||||
|
||||
import (
|
||||
providerir "esdi/providers/iracing"
|
||||
"log/slog"
|
||||
|
||||
telemetry "esdi/telemetry"
|
||||
)
|
||||
@@ -9,16 +10,19 @@ import (
|
||||
// TelemetryService will be our base struct to handle telemetry data
|
||||
// It should hook to a data sink and handle it like iRacing, BeamNG, AC and so on
|
||||
type TelemetryService struct {
|
||||
logger *slog.Logger
|
||||
ActiveProvider telemetry.TelemetryProvider
|
||||
}
|
||||
|
||||
func NewTelemetryService() *TelemetryService {
|
||||
return &TelemetryService{}
|
||||
func NewTelemetryService(logger *slog.Logger) *TelemetryService {
|
||||
return &TelemetryService{
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TelemetryService) setIRacingProvider() {
|
||||
path := "/home/esilva/Desktop/projetos/simracing_peripherals/testTelemetry/supercars_indianapolis.ibt"
|
||||
provider, _ := providerir.NewIRacingProvider(path, "", "")
|
||||
provider, _ := providerir.NewIRacingProvider(t.logger, path, "", "")
|
||||
|
||||
t.ActiveProvider = provider
|
||||
}
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ func NewControlPanel(logger *slog.Logger) *ControlPanel {
|
||||
}
|
||||
|
||||
devService := services.NewCDashService(logger)
|
||||
telemService := services.NewTelemetryService().SetProvider("iRacing")
|
||||
telemService := services.NewTelemetryService(logger).SetProvider("iRacing")
|
||||
if telemService == nil {
|
||||
panic("failed to create the telemetry service")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user