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
This commit is contained in:
2026-07-07 17:52:47 +01:00
parent 94ba0e2539
commit 0887fbfd75
6 changed files with 181 additions and 281 deletions
+63 -189
View File
@@ -8,18 +8,19 @@ import (
"sync" "sync"
"time" "time"
conv "esdi/conversions"
"esdi/telemetry" "esdi/telemetry"
bngsdk "github.com/ESilva15/gobngsdk" bngsdk "github.com/ESilva15/gobngsdk"
) )
// NOTE: document this please. What is a TelemetryData????
type BeamNG struct { type BeamNG struct {
SDK *bngsdk.BeamNGSDK SDK *bngsdk.BeamNGSDK
// data handling // data handling
mut sync.Mutex mut sync.Mutex
data *telemetry.TelemetryData data *telemetry.TelemetryData
updaters [telemetry.MaxFields]func(*telemetry.TelemetryField)
// stream control // stream control
streamCh chan telemetry.TelemetryData streamCh chan telemetry.TelemetryData
@@ -39,12 +40,66 @@ func NewBeamNGProvider(ip string, port int) (*BeamNG, error) {
return &BeamNG{}, err return &BeamNG{}, err
} }
return &BeamNG{ provider := &BeamNG{
streamCh: make(chan telemetry.TelemetryData, 1), streamCh: make(chan telemetry.TelemetryData, 1),
data: telemetry.NewTelemetryData(), data: telemetry.NewTelemetryData(),
SDK: &beam, SDK: &beam,
ticker: time.NewTicker(time.Second / 60), 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() { func (b *BeamNG) StopStream() {
@@ -103,185 +158,6 @@ func (b *BeamNG) Subscribe(requestFields map[int16]telemetry.FieldID) {
ID: id, 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) b.data.ActiveBinds = append(b.data.ActiveBinds, binding)
boundCheck[id] = true boundCheck[id] = true
} }
@@ -307,7 +183,8 @@ func (b *BeamNG) readData() {
// Read 1 to 1 data // Read 1 to 1 data
slog.Debug("Reading normal data binds") slog.Debug("Reading normal data binds")
for _, bind := range b.data.ActiveBinds { 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 // Set up virtual binds
@@ -334,10 +211,7 @@ func (b *BeamNG) stream(ctx context.Context) {
default: default:
} }
// We start by checking if we do or do not have data available // NOTE: add a method to check if there's data available, or make this happen
// if !b.isDataAvailable() {
// continue
// }
select { select {
case <-ctx.Done(): case <-ctx.Done():
-45
View File
@@ -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",
// }
+94 -34
View File
@@ -2,7 +2,8 @@ package beamng
import ( import (
"strconv" "strconv"
"time"
conv "esdi/conversions"
"esdi/telemetry" "esdi/telemetry"
) )
@@ -11,43 +12,102 @@ const (
LapTimeFormatStr = "04:05.000" LapTimeFormatStr = "04:05.000"
) )
// NOTE: this is a copy from iracing provider. Change it however necessary func (b *BeamNG) unused(out *telemetry.TelemetryField) {
func LapTimeTransform(v any, out *telemetry.TelemetryField) { out.Unused()
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)
} }
// NOTE: this is a copy from iracing provider. Change this however necessary in the future func (b *BeamNG) updateSpeed(out *telemetry.TelemetryField) {
// for beamng. out.Type = telemetry.DataTypeUINT16
func PitSpeedLimiterTransform(v any, out *telemetry.TelemetryField) { out.Raw = uint64(conv.MsToKph(b.SDK.Data.Speed))
out.Type = telemetry.DataTypeSTRING
if v.(bool) {
out.Str = "PIT"
} else {
out.Str = " "
}
} }
func GearTransform(v any, out *telemetry.TelemetryField) { func (b *BeamNG) updateGear(out *telemetry.TelemetryField) {
out.Type = telemetry.DataTypeSTRING 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 // 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)
} }
+8 -8
View File
@@ -259,26 +259,26 @@ func (i *IRacing) Subscribe(requestFields map[int16]telemetry.FieldID) {
out.Raw = uint64(uint16(v.(float32))) out.Raw = uint64(uint16(v.(float32)))
} }
case telemetry.FuelLevel: case telemetry.FuelLevel:
binding.Transform = telemetry.FloatToStringTransform binding.Transform = telemetry.FloatToStringTransformDEPRECATE
// Engine Data // Engine Data
case telemetry.OilPress: case telemetry.OilPress:
binding.Transform = telemetry.FloatToStringTransform binding.Transform = telemetry.FloatToStringTransformDEPRECATE
case telemetry.OilTemp: case telemetry.OilTemp:
binding.Transform = telemetry.FloatToStringTransform binding.Transform = telemetry.FloatToStringTransformDEPRECATE
case telemetry.WaterTemp: case telemetry.WaterTemp:
binding.Transform = telemetry.FloatToStringTransform binding.Transform = telemetry.FloatToStringTransformDEPRECATE
// Something else // Something else
case telemetry.PitSpeedLimiter: case telemetry.PitSpeedLimiter:
binding.Transform = PitSpeedLimiterTransform binding.Transform = PitSpeedLimiterTransform
// Adjustements // Adjustements
case telemetry.BrakeBias: case telemetry.BrakeBias:
binding.Transform = telemetry.FloatToStringTransform binding.Transform = telemetry.FloatToStringTransformDEPRECATE
case telemetry.ABSSetting: case telemetry.ABSSetting:
binding.Transform = telemetry.FloatToUInt8Transform binding.Transform = telemetry.FloatToUInt8TransformDEPRECATE
case telemetry.TCSetting: case telemetry.TCSetting:
binding.Transform = telemetry.FloatToUInt8Transform binding.Transform = telemetry.FloatToUInt8TransformDEPRECATE
case telemetry.ThrottleSetting: case telemetry.ThrottleSetting:
binding.Transform = telemetry.FloatToUInt8Transform binding.Transform = telemetry.FloatToUInt8TransformDEPRECATE
case telemetry.LFtempM: case telemetry.LFtempM:
binding.Transform = func(v any, out *telemetry.TelemetryField) { binding.Transform = func(v any, out *telemetry.TelemetryField) {
out.Type = telemetry.DataTypeSTRING out.Type = telemetry.DataTypeSTRING
+7 -2
View File
@@ -18,7 +18,12 @@ func UInt8Transform(v any, out *TelemetryField) {
out.Raw = uint64(v.(int)) 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 out.Type = DataTypeSTRING
if v == nil { if v == nil {
@@ -29,7 +34,7 @@ func FloatToStringTransform(v any, out *TelemetryField) {
out.Str = strconv.FormatFloat(float64(v.(float32)), 'f', 1, 32) 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 out.Type = DataTypeUINT8
if v == nil { if v == nil {
+9 -3
View File
@@ -32,10 +32,11 @@ type VirtualField interface {
// NOTE: we must deprecate the `Key` member. We used it to map the internal // 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. // telemetry fields to the providers name. But we shouldn't be using `string` keys.
type BoundField struct { type BoundField struct {
Key string Key string // to be deprecated
ID FieldID ID FieldID
Fetch func() any Fetch func() any // to be deprecated
Transform func(any, *TelemetryField) Transform func(any, *TelemetryField) // to be deprecated
Update func(out *TelemetryField)
} }
var bufferPool = sync.Pool{ var bufferPool = sync.Pool{
@@ -74,6 +75,11 @@ type TelemetryField struct {
Str string // Only to be used with DataTypeSTRING 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 // Pack will pack this current TelemetryField into bytes to send over the wire
// Format: // Format:
// 0x00 - Field ID // 0x00 - Field ID