d10g's picture
Initial Import
c95ad37
"""
Core data models and types for the F1 Commentary Robot.
This module defines all enumerations, dataclasses, and type definitions
used throughout the system for race events, state tracking, and configuration.
"""
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from typing import Any, Dict, List, Optional
# ============================================================================
# Enumerations
# ============================================================================
class EventType(Enum):
"""Types of race events that can be detected."""
OVERTAKE = "overtake"
PIT_STOP = "pit_stop"
LEAD_CHANGE = "lead_change"
FASTEST_LAP = "fastest_lap"
INCIDENT = "incident"
FLAG = "flag"
SAFETY_CAR = "safety_car"
POSITION_UPDATE = "position_update"
class EventPriority(Enum):
"""Priority levels for event queue processing."""
CRITICAL = 1 # Incidents, safety car, lead changes
HIGH = 2 # Overtakes, pit stops
MEDIUM = 3 # Fastest laps
LOW = 4 # Routine position updates
class RacePhase(Enum):
"""Distinct periods of a race."""
START = "start" # Laps 1-3
MID_RACE = "mid_race" # Laps 4 to final-5
FINISH = "finish" # Final 5 laps
class Gesture(Enum):
"""Robot head movement gestures."""
NEUTRAL = "neutral"
NOD = "nod"
TURN_LEFT = "turn_left"
TURN_RIGHT = "turn_right"
EXCITED = "excited" # Quick nod + turn
CONCERNED = "concerned" # Slow tilt
# ============================================================================
# Base Event Classes
# ============================================================================
@dataclass
class RaceEvent:
"""Base class for all race events."""
event_type: EventType
timestamp: datetime
data: Dict[str, Any] = field(default_factory=dict)
# ============================================================================
# Specific Event Classes
# ============================================================================
@dataclass
class OvertakeEvent:
"""Event representing a driver overtaking another driver."""
overtaking_driver: str
overtaken_driver: str
new_position: int
lap_number: int
timestamp: datetime
@dataclass
class PitStopEvent:
"""Event representing a driver pit stop."""
driver: str
pit_count: int
pit_duration: float
tire_compound: str
lap_number: int
timestamp: datetime
@dataclass
class LeadChangeEvent:
"""Event representing a change in race leader."""
new_leader: str
old_leader: str
lap_number: int
timestamp: datetime
@dataclass
class FastestLapEvent:
"""Event representing a new fastest lap."""
driver: str
lap_time: float
lap_number: int
timestamp: datetime
@dataclass
class IncidentEvent:
"""Event representing a race incident."""
description: str
drivers_involved: List[str]
lap_number: int
timestamp: datetime
@dataclass
class SafetyCarEvent:
"""Event representing safety car deployment or withdrawal."""
status: str # "deployed", "in", "ending"
reason: str
lap_number: int
timestamp: datetime
@dataclass
class FlagEvent:
"""Event representing flag deployment."""
flag_type: str # "yellow", "red", "green", "blue"
sector: Optional[str]
lap_number: int
timestamp: datetime
@dataclass
class PositionUpdateEvent:
"""Event representing routine position update."""
positions: Dict[str, int] # driver_name -> position
lap_number: int
timestamp: datetime
# ============================================================================
# Race State Data Models
# ============================================================================
@dataclass
class DriverState:
"""State information for a single driver during a race."""
name: str
position: int
gap_to_leader: float = 0.0
gap_to_ahead: float = 0.0
pit_count: int = 0
current_tire: str = "unknown"
last_lap_time: float = 0.0
@dataclass
class RaceState:
"""Complete race state including all drivers and race metadata."""
drivers: List[DriverState] = field(default_factory=list)
current_lap: int = 0
total_laps: int = 0
race_phase: RacePhase = RacePhase.START
fastest_lap_driver: Optional[str] = None
fastest_lap_time: Optional[float] = None
safety_car_active: bool = False
flags: List[str] = field(default_factory=list)
def get_driver(self, driver_name: str) -> Optional[DriverState]:
"""Get driver state by name."""
for driver in self.drivers:
if driver.name == driver_name:
return driver
return None
def get_leader(self) -> Optional[DriverState]:
"""Get the current race leader."""
if not self.drivers:
return None
return min(self.drivers, key=lambda d: d.position)
def get_positions(self) -> List[DriverState]:
"""Get drivers sorted by position."""
return sorted(self.drivers, key=lambda d: d.position)
# ============================================================================
# Configuration Data Model
# ============================================================================
@dataclass
class Config:
"""System configuration parameters."""
# OpenF1 API
openf1_api_key: str = ""
openf1_base_url: str = "https://api.openf1.org/v1"
# ElevenLabs
elevenlabs_api_key: str = ""
elevenlabs_voice_id: str = ""
# AI Enhancement (optional)
ai_enabled: bool = False
ai_provider: str = "openai" # "openai", "huggingface", "none"
ai_api_key: Optional[str] = None
ai_model: str = "gpt-3.5-turbo"
# Polling intervals (seconds)
position_poll_interval: float = 1.0
laps_poll_interval: float = 2.0
pit_poll_interval: float = 1.0
race_control_poll_interval: float = 1.0
# Event queue
max_queue_size: int = 10
# Audio
audio_volume: float = 0.8
# Motion
movement_speed: float = 30.0 # degrees/second
enable_movements: bool = True
# Logging
log_level: str = "INFO"
log_file: str = "logs/f1_commentary.log"
# Mode
replay_mode: bool = False
replay_race_id: Optional[str] = None
replay_speed: float = 1.0