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"
"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():
-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 (
"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)
}
+8 -8
View File
@@ -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
+7 -2
View File
@@ -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 {
+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
// 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