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