From 0887fbfd75a3555b1d673071fae233d0b32b9272 Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Tue, 7 Jul 2026 17:52:47 +0100 Subject: [PATCH] simplified a bit how the TelemetryField updaters work - now we only use one function to update the TelemetryField instead of having to fetch data and then update it. Still need to update the iRacing provider to use this pattern. Way better and creates better units. Got rid of that stupid long ass switch statement too --- providers/beamng/beamng.go | 252 ++++++++----------------------- providers/beamng/fieldMapping.go | 45 ------ providers/beamng/transforms.go | 128 +++++++++++----- providers/iracing/iracing.go | 16 +- telemetry/commonTransforms.go | 9 +- telemetry/data.go | 12 +- 6 files changed, 181 insertions(+), 281 deletions(-) delete mode 100644 providers/beamng/fieldMapping.go diff --git a/providers/beamng/beamng.go b/providers/beamng/beamng.go index e488c51..91ce8b6 100644 --- a/providers/beamng/beamng.go +++ b/providers/beamng/beamng.go @@ -8,18 +8,19 @@ import ( "sync" "time" - conv "esdi/conversions" "esdi/telemetry" bngsdk "github.com/ESilva15/gobngsdk" ) +// NOTE: document this please. What is a TelemetryData???? type BeamNG struct { SDK *bngsdk.BeamNGSDK // data handling - mut sync.Mutex - data *telemetry.TelemetryData + mut sync.Mutex + data *telemetry.TelemetryData + updaters [telemetry.MaxFields]func(*telemetry.TelemetryField) // stream control streamCh chan telemetry.TelemetryData @@ -39,12 +40,66 @@ func NewBeamNGProvider(ip string, port int) (*BeamNG, error) { return &BeamNG{}, err } - return &BeamNG{ + provider := &BeamNG{ streamCh: make(chan telemetry.TelemetryData, 1), data: telemetry.NewTelemetryData(), SDK: &beam, ticker: time.NewTicker(time.Second / 60), - }, nil + } + + provider.updaters = [telemetry.MaxFields]func(*telemetry.TelemetryField){ + telemetry.Speed: provider.updateSpeed, + telemetry.Gear: provider.updateGear, + telemetry.RPM: provider.updateRPM, + telemetry.FuelLevel: provider.fuelLevel, + // Engine Data + telemetry.OilPress: provider.oilPressure, + telemetry.OilTemp: provider.oilTemp, + telemetry.WaterTemp: provider.engTemp, + // Electrics (dash lights and so on) + telemetry.PitSpeedLimiter: provider.pitSpeedLimiter, + telemetry.LeftIndicator: provider.leftIndicator, + telemetry.RightIndicator: provider.rightIndicator, + telemetry.ABSWarningLight: provider.absLight, + telemetry.ParkingBrakeLight: provider.handbrakeLight, + telemetry.TCLight: provider.tcLight, + telemetry.BatteryLight: provider.batteryLight, + // Adjustements + telemetry.BrakeBias: provider.unused, + telemetry.ABSSetting: provider.unused, + telemetry.TCSetting: provider.unused, + telemetry.ThrottleSetting: provider.unused, + // Lap Data + telemetry.LapLastLapTime: provider.unused, + telemetry.LapNumber: provider.unused, + // Tire Data + telemetry.LFtempL: provider.unused, + telemetry.LFtempM: provider.unused, + telemetry.LFtempR: provider.unused, + telemetry.RFtempL: provider.unused, + telemetry.RFtempM: provider.unused, + telemetry.RFtempR: provider.unused, + telemetry.LRtempL: provider.unused, + telemetry.LRtempM: provider.unused, + telemetry.LRtempR: provider.unused, + telemetry.RRtempL: provider.unused, + telemetry.RRtempM: provider.unused, + telemetry.RRtempR: provider.unused, + // Session Data + telemetry.SessionTime: provider.unused, + telemetry.ReplaySessionTime: provider.unused, + telemetry.Empty: provider.unused, + // Virtual Fields -- fields derived from primitive fields + // RPM Dash Lights + telemetry.RPMStateColour: provider.unused, + // Fuel Calculator + telemetry.FCCurrentLap: provider.unused, + telemetry.FCLastLap: provider.unused, + telemetry.FCAverage: provider.unused, + telemetry.FCExpectedLaps: provider.unused, + } + + return provider, nil } func (b *BeamNG) StopStream() { @@ -103,185 +158,6 @@ func (b *BeamNG) Subscribe(requestFields map[int16]telemetry.FieldID) { ID: id, } - switch id { - case telemetry.Speed: - binding.Fetch = func() any { - return b.SDK.Data.Speed - } - binding.Transform = func(v any, out *telemetry.TelemetryField) { - out.Type = telemetry.DataTypeUINT16 - out.Raw = uint64(conv.MsToKph(v.(float32))) - } - case telemetry.Gear: - binding.Fetch = func() any { - return b.SDK.Data.Gear - } - binding.Transform = GearTransform - case telemetry.RPM: - binding.Fetch = func() any { - return b.SDK.Data.RPM - } - binding.Transform = func(v any, out *telemetry.TelemetryField) { - out.Type = telemetry.DataTypeUINT16 - out.Raw = uint64(uint16(v.(float32))) - } - case telemetry.FuelLevel: - binding.Fetch = func() any { - return b.SDK.Data.Fuel - } - binding.Transform = telemetry.FloatToStringTransform - // Engine Data - case telemetry.OilPress: - binding.Fetch = func() any { - return b.SDK.Data.OilPressure - } - binding.Transform = telemetry.FloatToStringTransform - case telemetry.OilTemp: - binding.Fetch = func() any { - return b.SDK.Data.OilTemp - } - binding.Transform = telemetry.FloatToStringTransform - case telemetry.WaterTemp: - binding.Fetch = func() any { - return b.SDK.Data.EngTemp - } - binding.Transform = telemetry.FloatToStringTransform - // Something else - case telemetry.PitSpeedLimiter: - binding.Fetch = func() any { - return b.SDK.Pitspeed() - } - binding.Transform = PitSpeedLimiterTransform - // Electrics (dash lights and so on) - case telemetry.LeftIndicator: - binding.Fetch = func() any { - return b.SDK.LeftIndicator() - } - binding.Transform = func(v any, out *telemetry.TelemetryField) { - out.Type = telemetry.DataTypeCHAR - - chr := ' ' - - if r, ok := v.(bool); ok { - if r { - chr = '<' - } - } - - out.Raw = uint64(chr) - } - case telemetry.RightIndicator: - binding.Fetch = func() any { - return b.SDK.RightIndicator() - } - binding.Transform = func(v any, out *telemetry.TelemetryField) { - out.Type = telemetry.DataTypeCHAR - - chr := ' ' - if r, ok := v.(bool); ok { - if r { - chr = '>' - } - } - - out.Raw = uint64(chr) - } - case telemetry.ABSWarningLight: - binding.Fetch = func() any { - return b.SDK.ABS() - } - binding.Transform = func(v any, out *telemetry.TelemetryField) { - out.Type = telemetry.DataTypeCHAR - - chr := ' ' - if r, ok := v.(bool); ok { - if r { - chr = 'A' - } - } - - out.Raw = uint64(chr) - } - case telemetry.ParkingBrakeLight: - binding.Fetch = func() any { - return b.SDK.Handbrake() - } - binding.Transform = func(v any, out *telemetry.TelemetryField) { - out.Type = telemetry.DataTypeCHAR - - chr := ' ' - if r, ok := v.(bool); ok { - if r { - chr = 'P' - } - } - - out.Raw = uint64(chr) - } - case telemetry.TCLight: - binding.Fetch = func() any { - return b.SDK.TractionControl() - } - binding.Transform = func(v any, out *telemetry.TelemetryField) { - out.Type = telemetry.DataTypeCHAR - - chr := ' ' - if r, ok := v.(bool); ok { - if r { - chr = 'T' - } - } - - out.Raw = uint64(chr) - } - case telemetry.BatteryLight: - binding.Fetch = func() any { - return b.SDK.BatteryLight() - } - binding.Transform = func(v any, out *telemetry.TelemetryField) { - out.Type = telemetry.DataTypeCHAR - - chr := ' ' - if r, ok := v.(bool); ok { - if r { - chr = 'B' - } - } - - out.Raw = uint64(chr) - } - // Adjustements - // case telemetry.BrakeBias: - // binding.Transform = telemetry.FloatToStringTransform - // case telemetry.ABSSetting: - // binding.Transform = telemetry.FloatToUInt8Transform - // case telemetry.TCSetting: - // binding.Transform = telemetry.FloatToUInt8Transform - // case telemetry.ThrottleSetting: - // binding.Transform = telemetry.FloatToUInt8Transform - // case telemetry.LFtempM: - // binding.Transform = func(v any, out *telemetry.TelemetryField) { - // out.Type = telemetry.DataTypeSTRING - // out.Str = strconv.FormatFloat(float64(v.(float32)), 'f', 1, 32) - // } - // case telemetry.SessionTime: - // binding.Transform = func(v any, out *telemetry.TelemetryField) { - // out.Type = telemetry.DataTypeSTRING - // out.Str = strconv.FormatFloat(v.(float64), 'f', 1, 32) - // } - // case telemetry.ReplaySessionTime: - // binding.Transform = func(v any, out *telemetry.TelemetryField) { - // out.Type = telemetry.DataTypeSTRING - // out.Str = strconv.FormatFloat(v.(float64), 'f', 1, 32) - // } - case telemetry.Empty: - binding.Transform = telemetry.EmptyTransform - // case telemetry.LapLastLapTime: - // binding.Transform = LapTimeTransform - // case telemetry.LapNumber: - // binding.Transform = telemetry.UInt8Transform - } - b.data.ActiveBinds = append(b.data.ActiveBinds, binding) boundCheck[id] = true } @@ -307,7 +183,8 @@ func (b *BeamNG) readData() { // Read 1 to 1 data slog.Debug("Reading normal data binds") for _, bind := range b.data.ActiveBinds { - bind.Transform(bind.Fetch(), &b.data.Values[bind.ID]) + slog.Debug("Current bind: ", "id", bind.ID) + b.updaters[bind.ID](&b.data.Values[bind.ID]) } // Set up virtual binds @@ -334,10 +211,7 @@ func (b *BeamNG) stream(ctx context.Context) { default: } - // We start by checking if we do or do not have data available - // if !b.isDataAvailable() { - // continue - // } + // NOTE: add a method to check if there's data available, or make this happen select { case <-ctx.Done(): diff --git a/providers/beamng/fieldMapping.go b/providers/beamng/fieldMapping.go deleted file mode 100644 index ce0cae2..0000000 --- a/providers/beamng/fieldMapping.go +++ /dev/null @@ -1,45 +0,0 @@ -package beamng - -// This file maps the data from the desktop provider data structure to iRacing - -// var internalToSDKFieldNames = map[telemetry.FieldID]string{ -// telemetry.Speed: "Speed", -// telemetry.Gear: "Gear", -// telemetry.RPM: "RPM", -// telemetry.FuelLevel: "Fuel", -// // Engine Data -// telemetry.OilPress: "OilPressure", -// telemetry.OilTemp: "OilTemp", -// telemetry.WaterTemp: "EngTemp", -// // Engine Warnings -// telemetry.PitSpeedLimiter: "irsdk_pitSpeedLimiter", -// // Electrics (dash lights and so on) -// telemetry.LeftIndicator: "sssssssss", -// telemetry.RightIndicator: "something", -// telemetry.Hazards: "something", -// // Ajudstements -// telemetry.BrakeBias: "dcBrakeBias", -// telemetry.ABSSetting: "dcABS", -// telemetry.TCSetting: "dcTractionControl", -// telemetry.ThrottleSetting: "dcThrottleShape", -// // Lap Data -// telemetry.LapLastLapTime: "LapLastLapTime", -// telemetry.LapNumber: "Lap", -// // Tire data -// telemetry.LFtempL: "LFtempCL", -// telemetry.LFtempM: "LFtempCM", -// telemetry.LFtempR: "LFtempCR", -// telemetry.RFtempL: "RFtempCL", -// telemetry.RFtempM: "RFtempCM", -// telemetry.RFtempR: "RFtempCR", -// telemetry.LRtempL: "LRtempCL", -// telemetry.LRtempM: "LRtempCM", -// telemetry.LRtempR: "LRtempCR", -// telemetry.RRtempL: "RRtempCL", -// telemetry.RRtempM: "RRtempCM", -// telemetry.RRtempR: "RRtempCR", -// // Session Data -// telemetry.SessionTime: "SessionTime", -// telemetry.ReplaySessionTime: "ReplaySessionTime", -// telemetry.Empty: "empty", -// } diff --git a/providers/beamng/transforms.go b/providers/beamng/transforms.go index 66f690e..ee751b8 100644 --- a/providers/beamng/transforms.go +++ b/providers/beamng/transforms.go @@ -2,7 +2,8 @@ package beamng import ( "strconv" - "time" + + conv "esdi/conversions" "esdi/telemetry" ) @@ -11,43 +12,102 @@ const ( LapTimeFormatStr = "04:05.000" ) -// NOTE: this is a copy from iracing provider. Change it however necessary -func LapTimeTransform(v any, out *telemetry.TelemetryField) { - lapTimeInSeconds := v.(float32) - - if lapTimeInSeconds < 0 { - lapTimeInSeconds = 0 - } - - wholeSeconds := int64(lapTimeInSeconds) - lapTime := time.Unix(wholeSeconds, int64((lapTimeInSeconds-float32(wholeSeconds))*1e9)) - - out.Type = telemetry.DataTypeSTRING - out.Str = lapTime.Format(LapTimeFormatStr) +func (b *BeamNG) unused(out *telemetry.TelemetryField) { + out.Unused() } -// NOTE: this is a copy from iracing provider. Change this however necessary in the future -// for beamng. -func PitSpeedLimiterTransform(v any, out *telemetry.TelemetryField) { - out.Type = telemetry.DataTypeSTRING - if v.(bool) { - out.Str = "PIT" - } else { - out.Str = " " - } +func (b *BeamNG) updateSpeed(out *telemetry.TelemetryField) { + out.Type = telemetry.DataTypeUINT16 + out.Raw = uint64(conv.MsToKph(b.SDK.Data.Speed)) } -func GearTransform(v any, out *telemetry.TelemetryField) { +func (b *BeamNG) updateGear(out *telemetry.TelemetryField) { out.Type = telemetry.DataTypeSTRING - - // NOTE: relook at this. Just copied it here but smells bad - gear := 0 - if val, ok := v.(int32); ok { - gear = int(val) - } else if val, ok := v.(int); ok { - gear = val - } - // NOTE: stupid idea but we can cache these values - out.Str = strconv.Itoa(gear) + out.Str = strconv.Itoa(int(b.SDK.Data.Gear)) +} + +func (b *BeamNG) updateRPM(out *telemetry.TelemetryField) { + out.Type = telemetry.DataTypeUINT16 + out.Raw = uint64(uint16(b.SDK.Data.RPM)) +} + +func (b *BeamNG) fuelLevel(out *telemetry.TelemetryField) { + telemetry.FloatToStringTransform(b.SDK.Data.Fuel, out) +} + +func (b *BeamNG) oilPressure(out *telemetry.TelemetryField) { + telemetry.FloatToStringTransform(b.SDK.Data.OilPressure, out) +} + +func (b *BeamNG) oilTemp(out *telemetry.TelemetryField) { + telemetry.FloatToStringTransform(b.SDK.Data.OilTemp, out) +} + +func (b *BeamNG) engTemp(out *telemetry.TelemetryField) { + telemetry.FloatToStringTransform(b.SDK.Data.EngTemp, out) +} + +// NOTE: find how to empty this +func (b *BeamNG) pitSpeedLimiter(out *telemetry.TelemetryField) { +} + +func (b *BeamNG) leftIndicator(out *telemetry.TelemetryField) { + chr := ' ' + if b.SDK.LeftIndicator() { + chr = '<' + } + + out.Type = telemetry.DataTypeCHAR + out.Raw = uint64(chr) +} + +func (b *BeamNG) rightIndicator(out *telemetry.TelemetryField) { + chr := ' ' + if b.SDK.RightIndicator() { + chr = '>' + } + + out.Type = telemetry.DataTypeCHAR + out.Raw = uint64(chr) +} + +func (b *BeamNG) absLight(out *telemetry.TelemetryField) { + chr := ' ' + if b.SDK.ABS() { + chr = 'A' + } + + out.Type = telemetry.DataTypeCHAR + out.Raw = uint64(chr) +} + +func (b *BeamNG) handbrakeLight(out *telemetry.TelemetryField) { + chr := ' ' + if b.SDK.Handbrake() { + chr = 'P' + } + + out.Type = telemetry.DataTypeCHAR + out.Raw = uint64(chr) +} + +func (b *BeamNG) tcLight(out *telemetry.TelemetryField) { + chr := ' ' + if b.SDK.TractionControl() { + chr = 'T' + } + + out.Type = telemetry.DataTypeCHAR + out.Raw = uint64(chr) +} + +func (b *BeamNG) batteryLight(out *telemetry.TelemetryField) { + chr := ' ' + if b.SDK.BatteryLight() { + chr = 'B' + } + + out.Type = telemetry.DataTypeCHAR + out.Raw = uint64(chr) } diff --git a/providers/iracing/iracing.go b/providers/iracing/iracing.go index f623233..97299ca 100644 --- a/providers/iracing/iracing.go +++ b/providers/iracing/iracing.go @@ -259,26 +259,26 @@ func (i *IRacing) Subscribe(requestFields map[int16]telemetry.FieldID) { out.Raw = uint64(uint16(v.(float32))) } case telemetry.FuelLevel: - binding.Transform = telemetry.FloatToStringTransform + binding.Transform = telemetry.FloatToStringTransformDEPRECATE // Engine Data case telemetry.OilPress: - binding.Transform = telemetry.FloatToStringTransform + binding.Transform = telemetry.FloatToStringTransformDEPRECATE case telemetry.OilTemp: - binding.Transform = telemetry.FloatToStringTransform + binding.Transform = telemetry.FloatToStringTransformDEPRECATE case telemetry.WaterTemp: - binding.Transform = telemetry.FloatToStringTransform + binding.Transform = telemetry.FloatToStringTransformDEPRECATE // Something else case telemetry.PitSpeedLimiter: binding.Transform = PitSpeedLimiterTransform // Adjustements case telemetry.BrakeBias: - binding.Transform = telemetry.FloatToStringTransform + binding.Transform = telemetry.FloatToStringTransformDEPRECATE case telemetry.ABSSetting: - binding.Transform = telemetry.FloatToUInt8Transform + binding.Transform = telemetry.FloatToUInt8TransformDEPRECATE case telemetry.TCSetting: - binding.Transform = telemetry.FloatToUInt8Transform + binding.Transform = telemetry.FloatToUInt8TransformDEPRECATE case telemetry.ThrottleSetting: - binding.Transform = telemetry.FloatToUInt8Transform + binding.Transform = telemetry.FloatToUInt8TransformDEPRECATE case telemetry.LFtempM: binding.Transform = func(v any, out *telemetry.TelemetryField) { out.Type = telemetry.DataTypeSTRING diff --git a/telemetry/commonTransforms.go b/telemetry/commonTransforms.go index 7580bd6..b24ef70 100644 --- a/telemetry/commonTransforms.go +++ b/telemetry/commonTransforms.go @@ -18,7 +18,12 @@ func UInt8Transform(v any, out *TelemetryField) { out.Raw = uint64(v.(int)) } -func FloatToStringTransform(v any, out *TelemetryField) { +func FloatToStringTransform(v float32, out *TelemetryField) { + out.Type = DataTypeSTRING + out.Str = strconv.FormatFloat(float64(v), 'f', 1, 32) +} + +func FloatToStringTransformDEPRECATE(v any, out *TelemetryField) { out.Type = DataTypeSTRING if v == nil { @@ -29,7 +34,7 @@ func FloatToStringTransform(v any, out *TelemetryField) { out.Str = strconv.FormatFloat(float64(v.(float32)), 'f', 1, 32) } -func FloatToUInt8Transform(v any, out *TelemetryField) { +func FloatToUInt8TransformDEPRECATE(v any, out *TelemetryField) { out.Type = DataTypeUINT8 if v == nil { diff --git a/telemetry/data.go b/telemetry/data.go index f2229af..99d72b3 100644 --- a/telemetry/data.go +++ b/telemetry/data.go @@ -32,10 +32,11 @@ type VirtualField interface { // NOTE: we must deprecate the `Key` member. We used it to map the internal // telemetry fields to the providers name. But we shouldn't be using `string` keys. type BoundField struct { - Key string + Key string // to be deprecated ID FieldID - Fetch func() any - Transform func(any, *TelemetryField) + Fetch func() any // to be deprecated + Transform func(any, *TelemetryField) // to be deprecated + Update func(out *TelemetryField) } var bufferPool = sync.Pool{ @@ -74,6 +75,11 @@ type TelemetryField struct { Str string // Only to be used with DataTypeSTRING } +func (tf *TelemetryField) Unused() { + tf.Type = DataTypeCHAR + tf.Raw = uint64('-') +} + // Pack will pack this current TelemetryField into bytes to send over the wire // Format: // 0x00 - Field ID