diff --git a/go.mod b/go.mod index fc9b9f2..1da441c 100644 --- a/go.mod +++ b/go.mod @@ -13,10 +13,10 @@ require ( ) require ( - buf.build/gen/go/mpapenbr/iracelog/grpc/go v1.5.1-20240623151922-fb549bddc8ee.1 - buf.build/gen/go/mpapenbr/iracelog/protocolbuffers/go v1.34.2-20240623151922-fb549bddc8ee.2 + buf.build/gen/go/mpapenbr/iracelog/grpc/go v1.5.1-20240818164234-8da4d13e1b88.1 + buf.build/gen/go/mpapenbr/iracelog/protocolbuffers/go v1.34.2-20240818164234-8da4d13e1b88.2 github.com/google/uuid v1.6.0 - github.com/mpapenbr/goirsdk v0.6.1 + github.com/mpapenbr/goirsdk v0.7.0 github.com/samber/lo v1.46.0 github.com/stretchr/testify v1.9.0 golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 @@ -34,8 +34,8 @@ require ( github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect golang.org/x/net v0.25.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/sys v0.24.0 // indirect + golang.org/x/text v0.17.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect ) diff --git a/go.sum b/go.sum index 106b9a0..6b5f100 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ -buf.build/gen/go/mpapenbr/iracelog/grpc/go v1.5.1-20240623151922-fb549bddc8ee.1 h1:lPlhiINx98V6y5AtBdLvqpJuinC8phWAF2dmi8Z9QKs= -buf.build/gen/go/mpapenbr/iracelog/grpc/go v1.5.1-20240623151922-fb549bddc8ee.1/go.mod h1:PHNMs6PIYA0TiUfjMeqK1nkfNrUIcA5vCn5VnFYGWEE= -buf.build/gen/go/mpapenbr/iracelog/protocolbuffers/go v1.34.2-20240623151922-fb549bddc8ee.2 h1:eeJL3v/jqCHRmXwnT3bxrYQegpsAw6cv6RgiTUs44DM= -buf.build/gen/go/mpapenbr/iracelog/protocolbuffers/go v1.34.2-20240623151922-fb549bddc8ee.2/go.mod h1:Nz+56AmPchS6dX/7Rc7t15BbsL+9eEHWvD0DpdMO7OA= +buf.build/gen/go/mpapenbr/iracelog/grpc/go v1.5.1-20240818164234-8da4d13e1b88.1 h1:x0o4fkCloKkDnGd0CHcMNMjygkzRIAta4gvwsLndCbc= +buf.build/gen/go/mpapenbr/iracelog/grpc/go v1.5.1-20240818164234-8da4d13e1b88.1/go.mod h1:2z7WgkT7hbeML1+5oyD2cif0VsujRrkyfzVHI35nYgQ= +buf.build/gen/go/mpapenbr/iracelog/protocolbuffers/go v1.34.2-20240818164234-8da4d13e1b88.2 h1:7mJORHD56rYpxIkVwoZTFbgz6lEeVWvaZy1F1EkWezo= +buf.build/gen/go/mpapenbr/iracelog/protocolbuffers/go v1.34.2-20240818164234-8da4d13e1b88.2/go.mod h1:Nz+56AmPchS6dX/7Rc7t15BbsL+9eEHWvD0DpdMO7OA= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -27,8 +27,8 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mpapenbr/goirsdk v0.6.1 h1:yn7M3HW9enpDr/k+Nx8T4fFbgIHe4GnpjUUi3PYS+Bs= -github.com/mpapenbr/goirsdk v0.6.1/go.mod h1:mxcnGuogAfsI9gLCxLkFQqcgNGgWvuktMFoZczfpddQ= +github.com/mpapenbr/goirsdk v0.7.0 h1:Zi+CBogQPqo0jXDE32m5tu3FLwNFOfvrTg9PU45h96s= +github.com/mpapenbr/goirsdk v0.7.0/go.mod h1:ZEW55Aq5nT04GmhvyGibTwdtMiiwvDaMExV/0FTsZzM= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -78,10 +78,10 @@ golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= diff --git a/internal/processor/cardata.go b/internal/processor/cardata.go index 2500d49..e0afcb0 100644 --- a/internal/processor/cardata.go +++ b/internal/processor/cardata.go @@ -3,7 +3,9 @@ package processor import ( "fmt" + commonv1 "buf.build/gen/go/mpapenbr/iracelog/protocolbuffers/go/iracelog/common/v1" racestatev1 "buf.build/gen/go/mpapenbr/iracelog/protocolbuffers/go/iracelog/racestate/v1" + trackv1 "buf.build/gen/go/mpapenbr/iracelog/protocolbuffers/go/iracelog/track/v1" "github.com/mpapenbr/goirsdk/irsdk" "github.com/mpapenbr/go-racelogger/log" @@ -58,6 +60,7 @@ type carRun struct{} func (cr *carRun) Enter(cd *CarData) { log.Info("Entering state: carRun") } func (cr *carRun) Exit(cd *CarData) { log.Info("Leaving state: carRun") } + func (cr *carRun) UpdatePre(cd *CarData, cw *carWorkData) { if cw.trackPos == -1 { cd.state = CarStateOut @@ -70,6 +73,7 @@ func (cr *carRun) UpdatePre(cd *CarData, cw *carWorkData) { cd.copyWorkData(cw) if cw.pit { cd.state = CarStatePit + handleInlap(cd, cw) cd.pitstops += 1 cd.setState(&carPit{}) return @@ -83,10 +87,25 @@ func (cr *carRun) UpdatePost(cd *CarData) { } } +func handleInlap(cd *CarData, cw *carWorkData) { + // rare case: car just left pit and immediately entered pit again + if cd.startOutLap > 0 { + cd.inlapTime = cw.sessionTime - cd.startOutLap + cd.lapMode = commonv1.LapMode_LAP_MODE_INOUTLAP + cd.startOutLap = 0 + return + } + + cd.inlaptiming.lap.markStop(cw.sessionTime) + cd.inlapTime = cd.inlaptiming.lap.duration.time + cd.lapMode = commonv1.LapMode_LAP_MODE_INLAP +} + type carSlow struct{} func (cs *carSlow) Enter(cd *CarData) { log.Info("Entering state: carSlow") } func (cs *carSlow) Exit(cd *CarData) { log.Info("Leaving state: carSlow") } + func (cs *carSlow) UpdatePre(cd *CarData, cw *carWorkData) { if cw.trackPos == -1 { cd.state = CarStateOut @@ -99,6 +118,7 @@ func (cs *carSlow) UpdatePre(cd *CarData, cw *carWorkData) { cd.copyWorkData(cw) if cw.pit { cd.state = CarStatePit + handleInlap(cd, cw) cd.pitstops += 1 cd.setState(&carPit{}) return @@ -135,6 +155,7 @@ func (cp *carPit) UpdatePre(cd *CarData, cw *carWorkData) { if !cw.pit { cd.state = CarStateRun cd.stintLap = 1 + cd.startOutLap = cw.sessionTime cd.setState(&carRun{}) return } @@ -175,12 +196,14 @@ func (co *carOut) UpdatePost(cd *CarData) {} type carWorkData struct { carIdx int32 trackPos float64 + trackLoc int32 pos int32 pic int32 lap int32 lc int32 pit bool tireCompound int32 + sessionTime float64 } // CarData is a struct that contains the logic to process data for a single car data. @@ -190,6 +213,7 @@ type CarData struct { msgData map[string]interface{} state string trackPos float64 + trackLoc int32 bestLap TimeWithMarker lastLap TimeWithMarker currentSector int @@ -206,9 +230,14 @@ type CarData struct { tireCompound int currentState carState laptiming *CarLaptiming + inlaptiming *CarLaptiming carDriverProc *CarDriverProc pitBoundaryProc *PitBoundaryProc gpd *GlobalProcessingData + lapMode commonv1.LapMode + startOutLap float64 // session time when car exited pit road + inlapTime float64 // gets computed on pit entry + outlapTime float64 // gets computed after pit exit on s/f } //nolint:whitespace // can't get different linters happy @@ -220,6 +249,7 @@ func NewCarData( reportLapStatus ReportTimingStatus, ) *CarData { laptiming := NewCarLaptiming(len(gpd.TrackInfo.Sectors), reportLapStatus) + inlaptiming := NewCarLaptiming(len(gpd.TrackInfo.Sectors), nil) ret := CarData{ carIdx: carIdx, currentState: &carInit{}, @@ -227,6 +257,7 @@ func NewCarData( carDriverProc: carDriverProc, pitBoundaryProc: pitBoundaryProc, laptiming: laptiming, + inlaptiming: inlaptiming, gpd: gpd, currentSector: -1, lastLap: TimeWithMarker{time: -1, marker: ""}, @@ -256,6 +287,7 @@ func (cd *CarData) setState(s carState) { cd.currentState.Enter(cd) } +//nolint:funlen,cyclop // ok here func (cd *CarData) prepareGrpcData() *racestatev1.Car { convertSectors := func(sectors []*SectionTiming) []*racestatev1.TimeWithMarker { ret := make([]*racestatev1.TimeWithMarker, len(sectors)) @@ -279,7 +311,12 @@ func (cd *CarData) prepareGrpcData() *racestatev1.Car { } return racestatev1.CarState_CAR_STATE_UNSPECIFIED } - + isPitEntryAfterSf := func(t *trackv1.Track) bool { + if t.PitInfo != nil && t.PitInfo.LaneLength > 0 { + return t.PitInfo.Entry < t.PitInfo.Exit + } + return false + } ret := &racestatev1.Car{ CarIdx: cd.carIdx, Pos: int32(cd.pos), @@ -299,7 +336,27 @@ func (cd *CarData) prepareGrpcData() *racestatev1.Car { Sectors: convertSectors(cd.laptiming.sectors), State: convertState(cd.state), } + if cd.inlapTime > 0 { + ret.TimeInfo = &racestatev1.TimeInfo{ + Time: float32(cd.inlapTime), + LapMode: cd.lapMode, + LapNo: int32(cd.lap), + } + if isPitEntryAfterSf(cd.gpd.TrackInfo) { + ret.TimeInfo.LapNo = int32(cd.lc) + } + } + if cd.outlapTime > 0 { + ret.TimeInfo = &racestatev1.TimeInfo{ + Time: float32(cd.outlapTime), + LapMode: commonv1.LapMode_LAP_MODE_OUTLAP, + LapNo: int32(cd.lc), + } + } + // reset special values + cd.inlapTime = 0 + cd.outlapTime = 0 return ret } @@ -357,9 +414,22 @@ func (cd *CarData) markSectorsAsOld() { func (cd *CarData) startLap(t float64) { cd.laptiming.lap.markStart(t) + // start the inlap timing only when car is on track + // otherwise we can't calculate the inlap time correctly + // for tracks where the pit is behind the s/f line + // for example: Interlagos, Mount Panorama + if cd.trackLoc == int32(irsdk.TrackLocationOnTrack) { + cd.inlaptiming.lap.markStart(t) // we may need this when car enters pit road + } } func (cd *CarData) stopLap(t float64) float64 { + if cd.startOutLap > 0 { + if cd.state != CarStatePit { + cd.outlapTime = t - cd.startOutLap + } + cd.startOutLap = 0 + } return cd.laptiming.lap.markStop(t) } @@ -375,17 +445,20 @@ func (cd *CarData) setStandingsLaptime(t float64) { cd.laptiming.lap.duration.time = t } -//nolint:lll // wrapping is not helpful here +//nolint:lll,errcheck // by design func (cd *CarData) extractIrsdkData(api *irsdk.Irsdk) *carWorkData { cw := carWorkData{} cw.carIdx = cd.carIdx + cw.sessionTime = justValue(api.GetDoubleValue("SessionTime")).(float64) cw.trackPos = float64(justValue(api.GetValue("CarIdxLapDistPct")).([]float32)[cd.carIdx]) + cw.trackLoc = justValue(api.GetValue("CarIdxTrackSurface")).([]int32)[cd.carIdx] cw.pos = justValue(api.GetValue("CarIdxPosition")).([]int32)[cd.carIdx] cw.pic = justValue(api.GetValue("CarIdxClassPosition")).([]int32)[cd.carIdx] cw.lap = justValue(api.GetValue("CarIdxLap")).([]int32)[cd.carIdx] cw.lc = justValue(api.GetValue("CarIdxLapCompleted")).([]int32)[cd.carIdx] cw.pit = justValue(api.GetValue("CarIdxOnPitRoad")).([]bool)[cd.carIdx] cw.tireCompound = justValue(api.GetValue("CarIdxTireCompound")).([]int32)[cd.carIdx] + // maybe put this into the CarStint? // value not unique // when wet race: 0=DRY, 1=WET (EventID 314) @@ -396,10 +469,12 @@ func (cd *CarData) extractIrsdkData(api *irsdk.Irsdk) *carWorkData { func (cd *CarData) copyWorkData(cw *carWorkData) { cd.trackPos = gate(cw.trackPos) + cd.trackLoc = cw.trackLoc cd.pos = int(cw.pos) cd.pic = int(cw.pic) cd.lap = int(cw.lap) cd.lc = int(cw.lc) + cd.tireCompound = int(cw.tireCompound) cd.dist = 0 cd.interval = 0 diff --git a/internal/racelogger.go b/internal/racelogger.go index c1cd83b..2034f8f 100644 --- a/internal/racelogger.go +++ b/internal/racelogger.go @@ -189,15 +189,15 @@ func (r *Racelogger) RegisterProvider(eventName, eventDescription string) error r.eventKey = r.config.eventKeyFunc(irYaml) event.Key = r.eventKey - r.globalData = processor.GlobalProcessingData{ - TrackInfo: track, - EventDataInfo: event, - } - err = r.dataprovider.RegisterProvider(event, track, r.config.recordingMode) + resp, err := r.dataprovider.RegisterProvider(event, track, r.config.recordingMode) if err != nil { return err } + r.globalData = processor.GlobalProcessingData{ + TrackInfo: resp.Track, + EventDataInfo: event, + } r.setupMainLoop() return nil diff --git a/pkg/cmd/logimport/import.go b/pkg/cmd/logimport/import.go index 8069ba9..f4060ce 100644 --- a/pkg/cmd/logimport/import.go +++ b/pkg/cmd/logimport/import.go @@ -141,7 +141,8 @@ func (p *importProc) sendMesage(msg protoreflect.Message) error { } } } - return p.dpc.RegisterProvider(req.Event, req.Track, p.recordingMode) + _, err := p.dpc.RegisterProvider(req.Event, req.Track, p.recordingMode) + return err case *providerv1.UnregisterEventRequest: req, _ := msg.Interface().(*providerv1.UnregisterEventRequest) eventKey := req.EventSelector.GetKey() diff --git a/pkg/grpc/dataprovider.go b/pkg/grpc/dataprovider.go index 3812e86..73fe04c 100644 --- a/pkg/grpc/dataprovider.go +++ b/pkg/grpc/dataprovider.go @@ -71,15 +71,15 @@ func (dpc *DataProviderClient) RegisterProvider( event *eventv1.Event, track *trackv1.Track, recordingMode providerv1.RecordingMode, -) error { +) (*providerv1.RegisterEventResponse, error) { req := providerv1.RegisterEventRequest{ Event: event, Track: track, Key: event.Key, RecordingMode: recordingMode, } //nolint:errcheck // by design dpc.msgLogger.Log(req.ProtoReflect()) - _, err := dpc.providerClient.RegisterEvent( + resp, err := dpc.providerClient.RegisterEvent( dpc.prepareContext(context.Background()), &req) - return err + return resp, err } func (dpc *DataProviderClient) prepareContext(ctx context.Context) context.Context {