From 4a56d253e58ba38ac020b014f1b145b9a38bdb25 Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Tue, 7 Jan 2025 18:36:31 +0000 Subject: [PATCH] Refactor data reading and export --- diskSubHeader.go | 27 ++++++++++++ go.mod | 25 ++++++----- go.sum | 37 +++++++++++----- headers.go | 59 ++++++++++++++++++------- irsdk.go | 110 +++++++++++++++++++++-------------------------- sessionInfo.go | 35 +++++++++++++++ variables.go | 27 +++++++----- 7 files changed, 212 insertions(+), 108 deletions(-) diff --git a/diskSubHeader.go b/diskSubHeader.go index c46c566..1b42d24 100644 --- a/diskSubHeader.go +++ b/diskSubHeader.go @@ -1,6 +1,8 @@ package goirsdk import ( + "github.com/ESilva15/goirsdk/logger" + "bytes" "encoding/binary" "fmt" @@ -19,6 +21,31 @@ type DiskSubHeader struct { RecordCount int32 // RecordCount holds the number of data frames } +// readSubheader will read the subheader contents out of the telemetry data +func (i *IBT) readSubheader() error { + log := logger.GetInstance() + + var subheaderRaw [SubHeaderSize]byte + _, err := i.File.ReadAt(subheaderRaw[:], HeaderSize) + if err != nil { + return fmt.Errorf("Failed to read disk subheaders from file: %v", err) + } + i.SubHeaders, err = parseTelemetrySubHeader(subheaderRaw) + if err != nil { + return fmt.Errorf("Unable to parse disk subheaders from file: %v", err) + } + + // Write to the output file - TODO add the check + if i.IBTExport != nil { + err = i.exportIBT(subheaderRaw[:], HeaderSize) + if err != nil { + log.Printf("Failed to export subheaders: %v\n", err) + } + } + + return nil +} + // parseTelemetrySubHeader will return a pointer to a DiskSubHeader variable // or nil if an error occurs. In which case the error return value is more // valuable diff --git a/go.mod b/go.mod index 3ab5727..a5d1d92 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,15 @@ -module github.com/ESilva15/goirsdk - -go 1.23.2 - -require ( - github.com/google/go-cmp v0.6.0 - golang.org/x/sys v0.26.0 - golang.org/x/text v0.19.0 - gopkg.in/yaml.v3 v3.0.1 -) +module github.com/ESilva15/goirsdk + +go 1.23.2 + +require ( + github.com/google/go-cmp v0.6.0 + golang.org/x/sys v0.29.0 + golang.org/x/text v0.19.0 + gopkg.in/yaml.v3 v3.0.1 +) + +require ( + golang.org/x/lint v0.0.0-20241112194109-818c5a804067 // indirect + golang.org/x/tools v0.29.0 // indirect +) diff --git a/go.sum b/go.sum index 07f4adc..9c37840 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,27 @@ -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/lint v0.0.0-20241112194109-818c5a804067 h1:adDmSQyFTCiv19j015EGKJBoaa7ElV0Q1Wovb/4G7NA= +golang.org/x/lint v0.0.0-20241112194109-818c5a804067/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/headers.go b/headers.go index 0ba3ca2..803effc 100644 --- a/headers.go +++ b/headers.go @@ -1,6 +1,8 @@ package goirsdk import ( + "github.com/ESilva15/goirsdk/logger" + "bytes" "encoding/binary" "fmt" @@ -39,6 +41,47 @@ type TelemetryHeaders struct { BufOffset int32 } +// readHeader will read the header out of the telemetry data +func (i *IBT) readHeader() error { + log := logger.GetInstance() + + var headerRaw [FileHeaderSize]byte + _, err := i.File.ReadAt(headerRaw[:], 0) + if err != nil { + return fmt.Errorf("Failed to read headers from file: %v", err) + } + i.Headers, err = parseTelemetryHeader(headerRaw) + if err != nil { + return fmt.Errorf("Unable to read headers from file: %v", err) + } + + // Write to the output file - TODO: this should only write if necessary + if i.IBTExport != nil { + err = i.exportIBT(headerRaw[:], 0) + if err != nil { + log.Printf("Failed to export headers: %v\n", err) + } + } + + return nil +} + +// parseTelemetryHeader will read the IBT file headers from a correctly sized +// buffer. +// You need to pass a the first FILE_HEADER_SIZE bytes of the buffer +func parseTelemetryHeader(buf [FileHeaderSize]byte) (*TelemetryHeaders, error) { + // utils.HexDump(buf[:]) + // fmt.Printf("Len: %d\n", len(buf)) + + dst := TelemetryHeaders{} + err := binary.Read(bytes.NewBuffer(buf[:]), binary.LittleEndian, &dst) + if err != nil { + return nil, fmt.Errorf("unable to unpack data: %v", err) + } + + return &dst, nil +} + // ToString renders a string showing the values of the struct func (th *TelemetryHeaders) ToString() string { return fmt.Sprintf( @@ -61,19 +104,3 @@ func (th *TelemetryHeaders) ToString() string { th.NumBuf, th.NumBuf, th.BufLen, th.BufLen, th.BufOffset, th.BufOffset, ) } - -// parseTelemetryHeader will read the IBT file headers from a correctly sized -// buffer. -// You need to pass a the first FILE_HEADER_SIZE bytes of the buffer -func parseTelemetryHeader(buf [FileHeaderSize]byte) (*TelemetryHeaders, error) { - // utils.HexDump(buf[:]) - // fmt.Printf("Len: %d\n", len(buf)) - - dst := TelemetryHeaders{} - err := binary.Read(bytes.NewBuffer(buf[:]), binary.LittleEndian, &dst) - if err != nil { - return nil, fmt.Errorf("unable to unpack data: %v", err) - } - - return &dst, nil -} diff --git a/irsdk.go b/irsdk.go index b9729ce..b8f9f63 100644 --- a/irsdk.go +++ b/irsdk.go @@ -2,13 +2,14 @@ package goirsdk import ( - "github.com/ESilva15/goirsdk/logger" + // "github.com/ESilva15/goirsdk/logger" "fmt" "os" "io" + "github.com/ESilva15/goirsdk/logger" "github.com/ESilva15/goirsdk/winutils" "gopkg.in/yaml.v3" ) @@ -31,14 +32,16 @@ type Reader interface { // IBT struct will hold the relevant data for a given IBT file type IBT struct { - File Reader // Source of the data - FileToExport *os.File // If set, it will export the IBT data to the file - YAMLExport *os.File // If set, it will export the session YAML to the file - Headers *TelemetryHeaders // IBT file Headers - SubHeaders *DiskSubHeader // IBT file Sub Headers - SessionInfo *SessionInfoYAML // IBT file Session Info - Vars *TelemetryVars // Vars will hold the telemetry data - winUtils *winutils.IRacingWinUtils // WinUtils gives access to the system utilities + File Reader // Source of the data + IBTExport *os.File // If set, it will export the IBT data to the file + IBTExportPath string // Path for IBT export + YAMLExport *os.File // If set, it will export the session YAML to the file + YAMLExportPath string // Path for YAML export + Headers *TelemetryHeaders // IBT file Headers + SubHeaders *DiskSubHeader // IBT file Sub Headers + SessionInfo *SessionInfoYAML // IBT file Session Info + Vars *TelemetryVars // Vars will hold the telemetry data + winUtils *winutils.IRacingWinUtils // WinUtils gives access to the system utilities } func (i *IBT) IsConnected() bool { @@ -54,9 +57,12 @@ func (i *IBT) IsConnected() bool { return false } -func (i *IBT) exportYAML(path string) error { - file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) +func (i *IBT) exportYAML() error { + log := logger.GetInstance() + + file, err := os.OpenFile(i.YAMLExportPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { + log.Printf("Failed to open file for YAML export: %v\n", err) return fmt.Errorf("failed to open output file for YAML: %v", err) } defer file.Close() @@ -65,29 +71,47 @@ func (i *IBT) exportYAML(path string) error { err = enc.Encode(i.SessionInfo) if err != nil { + log.Printf("Failed to write into file for YAML export: %v\n", err) return fmt.Errorf("failed to write YAML contents to file: %v", err) } return nil } +func (i *IBT) exportIBT(data []byte, offset int64) error { + log := logger.GetInstance() + + _, err := i.IBTExport.WriteAt(data, offset) + + if err != nil { + i.IBTExport.Close() + i.IBTExport = nil + log.Println("Won't attempt to export anymore") + return err + } + + return nil +} + // Init serves to initialize and get a hold of a IBT struct func Init(f Reader, exportTelem string, exportYAML string) (*IBT, error) { - log := logger.GetInstance() + // log := logger.GetInstance() // Read the header of the file var err error ibt := IBT{ - File: f, - FileToExport: nil, - YAMLExport: nil, - Vars: &TelemetryVars{}, - winUtils: nil, + File: f, + IBTExport: nil, + IBTExportPath: exportTelem, + YAMLExport: nil, + YAMLExportPath: exportYAML, + Vars: &TelemetryVars{}, + winUtils: nil, } // If requested to output to a telemetry file if exportTelem != "" { - ibt.FileToExport, err = os.OpenFile(exportTelem, os.O_CREATE|os.O_RDWR, 0644) + ibt.IBTExport, err = os.OpenFile(exportTelem, os.O_CREATE|os.O_RDWR, 0644) if err != nil { return nil, fmt.Errorf("failed to open ibt export file: %v", err) } @@ -122,59 +146,21 @@ func Init(f Reader, exportTelem string, exportYAML string) (*IBT, error) { } // Read the file headers - var headerRaw [FileHeaderSize]byte - _, err = ibt.File.ReadAt(headerRaw[:], 0) + err = ibt.readHeader() if err != nil { - return nil, fmt.Errorf("Failed to read headers from file: %v", err) - } - ibt.Headers, err = parseTelemetryHeader(headerRaw) - if err != nil { - return nil, fmt.Errorf("Unable to read headers from file: %v", err) - } - // Write to the output file - _, err = ibt.FileToExport.WriteAt(headerRaw[:], 0) - if err != nil { - log.Fatal(err) + return nil, err } // Read the disk sub headers - var subheaderRaw [SubHeaderSize]byte - _, err = ibt.File.ReadAt(subheaderRaw[:], HeaderSize) + err = ibt.readSubheader() if err != nil { - return nil, fmt.Errorf("Failed to read disk subheaders from file: %v", err) - } - ibt.SubHeaders, err = parseTelemetrySubHeader(subheaderRaw) - if err != nil { - return nil, fmt.Errorf("Unable to parse disk subheaders from file: %v", err) - } - // Write to the output file - _, err = ibt.FileToExport.WriteAt(subheaderRaw[:], HeaderSize) - if err != nil { - log.Fatal(err) + return nil, err } // Read session info string - sessionInfoStringRaw := make([]byte, ibt.Headers.SessionInfoLength) - _, err = ibt.File.ReadAt(sessionInfoStringRaw, int64(ibt.Headers.SessionInfoOffset)) + err = ibt.readSessionInfo() if err != nil { - return nil, fmt.Errorf("Failed to read sessionInfoString from file: %v", err) - } - // Write to the output file - _, err = ibt.FileToExport.WriteAt(sessionInfoStringRaw[:], int64(ibt.Headers.SessionInfoOffset)) - if err != nil { - log.Fatal(err) - } - - ibt.SessionInfo, err = parseSessionInfo(sessionInfoStringRaw, ibt.Headers.SessionInfoLength) - if err != nil { - return nil, fmt.Errorf("Unable to parse SessionInfoString from file: %v", err) - } - // Write to YAML output file - if exportYAML != "" { - err := ibt.exportYAML(exportYAML) - if err != nil { - return nil, err - } + return nil, err } // Read the telemetry vars info diff --git a/sessionInfo.go b/sessionInfo.go index f6a1688..c9f3256 100644 --- a/sessionInfo.go +++ b/sessionInfo.go @@ -1,7 +1,10 @@ package goirsdk import ( + "github.com/ESilva15/goirsdk/logger" + "encoding/json" + "fmt" "log" "strings" @@ -307,6 +310,38 @@ type Driver struct { TeamIncidentCount int `yaml:"TeamIncidentCount"` } +// readSessionInfo will read the session info yaml out of the telemetry data +func (i *IBT) readSessionInfo() error { + log := logger.GetInstance() + + sessionInfoStringRaw := make([]byte, i.Headers.SessionInfoLength) + _, err := i.File.ReadAt(sessionInfoStringRaw, int64(i.Headers.SessionInfoOffset)) + if err != nil { + return fmt.Errorf("Failed to read sessionInfoString from file: %v", err) + } + + // Write to the output file + _, err = i.IBTExport.WriteAt(sessionInfoStringRaw[:], int64(i.Headers.SessionInfoOffset)) + if err != nil { + return fmt.Errorf("Failed to export session info string: %v\n", err) + } + + i.SessionInfo, err = parseSessionInfo(sessionInfoStringRaw, i.Headers.SessionInfoLength) + if err != nil { + return fmt.Errorf("Unable to parse SessionInfoString from file: %v", err) + } + + // Write to YAML output file + if i.YAMLExportPath != "" { + err := i.exportYAML() + if err != nil { + log.Println("Failed to export YAML string: %v\n", err) + } + } + + return nil +} + // parseSessionInfo will parse the sessionInfo buffer into the SessionInfoYAML // struct func parseSessionInfo(buf []byte, len int32) (*SessionInfoYAML, error) { diff --git a/variables.go b/variables.go index f3bc9d6..c1fff56 100644 --- a/variables.go +++ b/variables.go @@ -108,9 +108,12 @@ func (i *IBT) readVariablerHeaders() error { return err } - _, err = i.FileToExport.WriteAt(rbuf, int64(i.Headers.VarHeaderOffset+k*VarHeaderSize)) - if err != nil { - log.Fatal(err) + if i.IBTExport != nil { + err = i.exportIBT(rbuf, int64(i.Headers.VarHeaderOffset+k*VarHeaderSize)) + if err != nil { + // Don't outright kill it here - maybe nowhere else + log.Printf("Failed to export variable contents: %v\n", err) + } } var dst IBTVar @@ -203,9 +206,11 @@ func (i *IBT) Update(timeout time.Duration) (IRacingState, error) { return Failed, err } - _, err = i.FileToExport.WriteAt(buf, int64(i.Headers.BufOffset+i.Vars.RecorderTick*i.Headers.BufLen)) - if err != nil { - log.Fatalf("Failed to write to file [2]: %v", err) + if i.IBTExport != nil { + err = i.exportIBT(buf, int64(i.Headers.BufOffset+i.Vars.RecorderTick*i.Headers.BufLen)) + if err != nil { + log.Printf("Failed to export live telemetry data: %v", err) + } } err = i.readData(buf) @@ -226,10 +231,12 @@ func (i *IBT) Update(timeout time.Duration) (IRacingState, error) { // Make this happen in a different thread, or have this send to a queue that has a thread // writing to a file - _, err = i.FileToExport.WriteAt(buf, int64(start)) - if err != nil { - log.Fatalf("Failed to write to file [1]: %v\n", err) - } + if i.IBTExport != nil { + err = i.exportIBT(buf, int64(start)) + if err != nil { + log.Printf("Failed to export offline telemetry data: %v", err) + } + } if err == io.EOF { return Ended, nil