mirror of
https://github.com/AmanTahiliani/box-box.git
synced 2026-08-07 11:54:59 -04:00
Subscribe to SignalR Position.z and CarData.z, decode DEFLATE payloads, stream throttled positions over SSE, and render live car dots against cached track outline bounds with tap telemetry. Alignment spike: no checked-in or locally cached real Position.z samples were available in this isolated worktree; verified both streams expose the official raw F1 X/Y/Z coordinate contract and implemented shared-bounds normalization against prefetched OpenF1 outlines. Fallback build-outline-from-stream was not taken. Playwright remains out of scope for live rendering because BOXBOX_DISABLE_LIVE is used there; coverage is via parser, web handler/SSE, pure transform, and seeded component tests.
167 lines
5.1 KiB
Go
167 lines
5.1 KiB
Go
package live
|
|
|
|
import (
|
|
"encoding/json"
|
|
"time"
|
|
)
|
|
|
|
// F1SignalRMessage is the top-level envelope from the official F1 SignalR feed.
|
|
type F1SignalRMessage struct {
|
|
M []struct {
|
|
A []json.RawMessage `json:"A"`
|
|
} `json:"M"`
|
|
R json.RawMessage `json:"R"`
|
|
}
|
|
|
|
// F1TimingLine is a single driver's timing row from TimingData.
|
|
type F1TimingLine struct {
|
|
GapToLeader interface{} `json:"GapToLeader"`
|
|
IntervalToPositionAhead struct {
|
|
Value interface{} `json:"Value"`
|
|
} `json:"IntervalToPositionAhead"`
|
|
Position interface{} `json:"Position"`
|
|
RacingNumber string `json:"RacingNumber"`
|
|
LastLapTime struct {
|
|
Value string `json:"Value"`
|
|
PersonalFastest bool `json:"PersonalFastest"`
|
|
OverallFastest bool `json:"OverallFastest"`
|
|
} `json:"LastLapTime"`
|
|
BestLapTime struct {
|
|
Value string `json:"Value"`
|
|
PersonalFastest bool `json:"PersonalFastest"`
|
|
OverallFastest bool `json:"OverallFastest"`
|
|
Lap int `json:"Lap"`
|
|
} `json:"BestLapTime"`
|
|
InPit interface{} `json:"InPit"`
|
|
PitOut interface{} `json:"PitOut"`
|
|
Retired interface{} `json:"Retired"`
|
|
KnockedOut interface{} `json:"KnockedOut"`
|
|
Cutoff interface{} `json:"Cutoff"`
|
|
NumberOfLaps interface{} `json:"NumberOfLaps"`
|
|
Sectors json.RawMessage `json:"Sectors"`
|
|
Speeds map[string]json.RawMessage `json:"Speeds"`
|
|
}
|
|
|
|
// F1DriverListEntry is driver metadata from the DriverList topic.
|
|
type F1DriverListEntry struct {
|
|
RacingNumber string `json:"RacingNumber"`
|
|
BroadcastName string `json:"BroadcastName"`
|
|
Tla string `json:"Tla"`
|
|
TeamName string `json:"TeamName"`
|
|
TeamColour string `json:"TeamColour"`
|
|
FirstName string `json:"FirstName"`
|
|
LastName string `json:"LastName"`
|
|
}
|
|
|
|
// LiveTyreData holds current tyre compound and age for a driver.
|
|
type LiveTyreData struct {
|
|
Compound string // SOFT, MEDIUM, HARD, INTERMEDIATE, WET
|
|
New bool
|
|
Age int // laps on current set
|
|
}
|
|
|
|
// LiveRCMessage is a parsed race control message.
|
|
type LiveRCMessage struct {
|
|
Time string // "15:04" formatted
|
|
Category string // Flag, SafetyCar, Drs, Other
|
|
Flag string // GREEN, YELLOW, RED, etc.
|
|
Message string
|
|
Lap int
|
|
}
|
|
|
|
// LiveWeatherData holds session weather readings.
|
|
type LiveWeatherData struct {
|
|
AirTemp float64
|
|
TrackTemp float64
|
|
Humidity float64
|
|
WindSpeed float64
|
|
WindDir int
|
|
Rainfall bool
|
|
}
|
|
|
|
// LiveSessionMeta holds session and meeting metadata.
|
|
type LiveSessionMeta struct {
|
|
MeetingName string
|
|
CircuitName string
|
|
SessionType string
|
|
SessionName string
|
|
}
|
|
|
|
// LivePositionData is the latest raw F1 GPS position for one driver.
|
|
type LivePositionData struct {
|
|
X float64 `json:"x"`
|
|
Y float64 `json:"y"`
|
|
Z float64 `json:"z"`
|
|
Status string `json:"status"`
|
|
}
|
|
|
|
// LiveTelemetryData is the latest car telemetry for one driver.
|
|
type LiveTelemetryData struct {
|
|
Speed int `json:"Speed"`
|
|
Throttle int `json:"Throttle"`
|
|
Brake int `json:"Brake"`
|
|
DRS int `json:"DRS"`
|
|
NGear int `json:"NGear"`
|
|
RPM int `json:"RPM"`
|
|
}
|
|
|
|
// LiveSectorData holds a single sector time and flags.
|
|
type LiveSectorData struct {
|
|
Value string
|
|
PersonalFastest bool
|
|
OverallFastest bool
|
|
}
|
|
|
|
// LiveDriverData is the normalized timing state for one driver.
|
|
type LiveDriverData struct {
|
|
RacingNumber string
|
|
Position int
|
|
PrevPosition int
|
|
GapToLeader string
|
|
Interval string
|
|
LastLapTime string
|
|
LastLapPB bool // personal best
|
|
LastLapOB bool // overall best
|
|
BestLapTime string
|
|
BestLapPB bool // just set a new personal best
|
|
BestLapOB bool // overall fastest in session
|
|
BestLapNum int // lap number when best was set
|
|
InPit bool
|
|
PitOut bool
|
|
Retired bool
|
|
KnockedOut bool // eliminated in qualifying
|
|
Cutoff bool // currently in elimination zone (danger zone)
|
|
OnFlyingLap bool // currently running a timed lap (derived from sector state)
|
|
NumberOfLaps int
|
|
SpeedTrap string // fastest recorded speed at speed trap
|
|
Sectors [3]LiveSectorData
|
|
}
|
|
|
|
// LiveStintData is one stint in a driver's tyre history.
|
|
type LiveStintData struct {
|
|
Compound string
|
|
New bool
|
|
Laps int
|
|
}
|
|
|
|
// LiveStreamData is an immutable snapshot of all live timing state.
|
|
type LiveStreamData struct {
|
|
Drivers map[string]LiveDriverData
|
|
DriverInfo map[string]F1DriverListEntry
|
|
Tyres map[string]LiveTyreData
|
|
Telemetry map[string]LiveTelemetryData
|
|
RCMessages []LiveRCMessage
|
|
Weather LiveWeatherData
|
|
Session LiveSessionMeta
|
|
TrackStatus string // "1"=green "2"=yellow "4"=SC "5"=red "6"=VSC
|
|
CurrentLap int
|
|
TotalLaps int
|
|
Clock string // "HH:MM:SS" remaining at ClockRefTime
|
|
ClockRefTime time.Time // UTC when Clock was accurate
|
|
ClockExtrapolating bool // true = actively counting down
|
|
Stints map[string][]LiveStintData
|
|
Positions map[string]LivePositionData `json:"-"`
|
|
PositionUpdated bool `json:"-"`
|
|
SnapshotUpdated bool `json:"-"`
|
|
}
|