Refactor data reading and export

This commit is contained in:
2025-01-07 18:36:31 +00:00
parent a3418e0c95
commit 4a56d253e5
7 changed files with 212 additions and 108 deletions
+27
View File
@@ -1,6 +1,8 @@
package goirsdk package goirsdk
import ( import (
"github.com/ESilva15/goirsdk/logger"
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
@@ -19,6 +21,31 @@ type DiskSubHeader struct {
RecordCount int32 // RecordCount holds the number of data frames 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 // parseTelemetrySubHeader will return a pointer to a DiskSubHeader variable
// or nil if an error occurs. In which case the error return value is more // or nil if an error occurs. In which case the error return value is more
// valuable // valuable
+15 -10
View File
@@ -1,10 +1,15 @@
module github.com/ESilva15/goirsdk module github.com/ESilva15/goirsdk
go 1.23.2 go 1.23.2
require ( require (
github.com/google/go-cmp v0.6.0 github.com/google/go-cmp v0.6.0
golang.org/x/sys v0.26.0 golang.org/x/sys v0.29.0
golang.org/x/text v0.19.0 golang.org/x/text v0.19.0
gopkg.in/yaml.v3 v3.0.1 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
)
+27 -10
View File
@@ -1,10 +1,27 @@
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/lint v0.0.0-20241112194109-818c5a804067 h1:adDmSQyFTCiv19j015EGKJBoaa7ElV0Q1Wovb/4G7NA=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/lint v0.0.0-20241112194109-818c5a804067/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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=
+43 -16
View File
@@ -1,6 +1,8 @@
package goirsdk package goirsdk
import ( import (
"github.com/ESilva15/goirsdk/logger"
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
@@ -39,6 +41,47 @@ type TelemetryHeaders struct {
BufOffset int32 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 // ToString renders a string showing the values of the struct
func (th *TelemetryHeaders) ToString() string { func (th *TelemetryHeaders) ToString() string {
return fmt.Sprintf( return fmt.Sprintf(
@@ -61,19 +104,3 @@ func (th *TelemetryHeaders) ToString() string {
th.NumBuf, th.NumBuf, th.BufLen, th.BufLen, th.BufOffset, th.BufOffset, 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
}
+48 -62
View File
@@ -2,13 +2,14 @@
package goirsdk package goirsdk
import ( import (
"github.com/ESilva15/goirsdk/logger" // "github.com/ESilva15/goirsdk/logger"
"fmt" "fmt"
"os" "os"
"io" "io"
"github.com/ESilva15/goirsdk/logger"
"github.com/ESilva15/goirsdk/winutils" "github.com/ESilva15/goirsdk/winutils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -31,14 +32,16 @@ type Reader interface {
// IBT struct will hold the relevant data for a given IBT file // IBT struct will hold the relevant data for a given IBT file
type IBT struct { type IBT struct {
File Reader // Source of the data File Reader // Source of the data
FileToExport *os.File // If set, it will export the IBT data to the file IBTExport *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 IBTExportPath string // Path for IBT export
Headers *TelemetryHeaders // IBT file Headers YAMLExport *os.File // If set, it will export the session YAML to the file
SubHeaders *DiskSubHeader // IBT file Sub Headers YAMLExportPath string // Path for YAML export
SessionInfo *SessionInfoYAML // IBT file Session Info Headers *TelemetryHeaders // IBT file Headers
Vars *TelemetryVars // Vars will hold the telemetry data SubHeaders *DiskSubHeader // IBT file Sub Headers
winUtils *winutils.IRacingWinUtils // WinUtils gives access to the system utilities 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 { func (i *IBT) IsConnected() bool {
@@ -54,9 +57,12 @@ func (i *IBT) IsConnected() bool {
return false return false
} }
func (i *IBT) exportYAML(path string) error { func (i *IBT) exportYAML() error {
file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) log := logger.GetInstance()
file, err := os.OpenFile(i.YAMLExportPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil { 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) return fmt.Errorf("failed to open output file for YAML: %v", err)
} }
defer file.Close() defer file.Close()
@@ -65,29 +71,47 @@ func (i *IBT) exportYAML(path string) error {
err = enc.Encode(i.SessionInfo) err = enc.Encode(i.SessionInfo)
if err != nil { 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 fmt.Errorf("failed to write YAML contents to file: %v", err)
} }
return nil 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 // Init serves to initialize and get a hold of a IBT struct
func Init(f Reader, exportTelem string, exportYAML string) (*IBT, error) { func Init(f Reader, exportTelem string, exportYAML string) (*IBT, error) {
log := logger.GetInstance() // log := logger.GetInstance()
// Read the header of the file // Read the header of the file
var err error var err error
ibt := IBT{ ibt := IBT{
File: f, File: f,
FileToExport: nil, IBTExport: nil,
YAMLExport: nil, IBTExportPath: exportTelem,
Vars: &TelemetryVars{}, YAMLExport: nil,
winUtils: nil, YAMLExportPath: exportYAML,
Vars: &TelemetryVars{},
winUtils: nil,
} }
// If requested to output to a telemetry file // If requested to output to a telemetry file
if exportTelem != "" { 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 { if err != nil {
return nil, fmt.Errorf("failed to open ibt export file: %v", err) 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 // Read the file headers
var headerRaw [FileHeaderSize]byte err = ibt.readHeader()
_, err = ibt.File.ReadAt(headerRaw[:], 0)
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to read headers from file: %v", err) return nil, 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)
} }
// Read the disk sub headers // Read the disk sub headers
var subheaderRaw [SubHeaderSize]byte err = ibt.readSubheader()
_, err = ibt.File.ReadAt(subheaderRaw[:], HeaderSize)
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to read disk subheaders from file: %v", err) return nil, 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)
} }
// Read session info string // Read session info string
sessionInfoStringRaw := make([]byte, ibt.Headers.SessionInfoLength) err = ibt.readSessionInfo()
_, err = ibt.File.ReadAt(sessionInfoStringRaw, int64(ibt.Headers.SessionInfoOffset))
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to read sessionInfoString from file: %v", err) return nil, 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
}
} }
// Read the telemetry vars info // Read the telemetry vars info
+35
View File
@@ -1,7 +1,10 @@
package goirsdk package goirsdk
import ( import (
"github.com/ESilva15/goirsdk/logger"
"encoding/json" "encoding/json"
"fmt"
"log" "log"
"strings" "strings"
@@ -307,6 +310,38 @@ type Driver struct {
TeamIncidentCount int `yaml:"TeamIncidentCount"` 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 // parseSessionInfo will parse the sessionInfo buffer into the SessionInfoYAML
// struct // struct
func parseSessionInfo(buf []byte, len int32) (*SessionInfoYAML, error) { func parseSessionInfo(buf []byte, len int32) (*SessionInfoYAML, error) {
+17 -10
View File
@@ -108,9 +108,12 @@ func (i *IBT) readVariablerHeaders() error {
return err return err
} }
_, err = i.FileToExport.WriteAt(rbuf, int64(i.Headers.VarHeaderOffset+k*VarHeaderSize)) if i.IBTExport != nil {
if err != nil { err = i.exportIBT(rbuf, int64(i.Headers.VarHeaderOffset+k*VarHeaderSize))
log.Fatal(err) 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 var dst IBTVar
@@ -203,9 +206,11 @@ func (i *IBT) Update(timeout time.Duration) (IRacingState, error) {
return Failed, err return Failed, err
} }
_, err = i.FileToExport.WriteAt(buf, int64(i.Headers.BufOffset+i.Vars.RecorderTick*i.Headers.BufLen)) if i.IBTExport != nil {
if err != nil { err = i.exportIBT(buf, int64(i.Headers.BufOffset+i.Vars.RecorderTick*i.Headers.BufLen))
log.Fatalf("Failed to write to file [2]: %v", err) if err != nil {
log.Printf("Failed to export live telemetry data: %v", err)
}
} }
err = i.readData(buf) 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 // Make this happen in a different thread, or have this send to a queue that has a thread
// writing to a file // writing to a file
_, err = i.FileToExport.WriteAt(buf, int64(start)) if i.IBTExport != nil {
if err != nil { err = i.exportIBT(buf, int64(start))
log.Fatalf("Failed to write to file [1]: %v\n", err) if err != nil {
} log.Printf("Failed to export offline telemetry data: %v", err)
}
}
if err == io.EOF { if err == io.EOF {
return Ended, nil return Ended, nil