Merge branch 'main' of https://github.com/pulipakaa24/HPCSimSite
This commit is contained in:
@@ -9,7 +9,7 @@ Connects to AI Intelligence Layer via WebSocket and:
|
||||
4. Generates voice announcements for strategy updates
|
||||
|
||||
Usage:
|
||||
python simulate_pi_websocket.py --interval 5 --ws-url ws://localhost:9000/ws/pi --enable-voice
|
||||
python simulate_pi_websocket.py --interval 5 --ws-url ws://192.168.137.134:9000/ws/pi --enable-voice
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -306,7 +306,7 @@ class VoiceAnnouncer:
|
||||
class PiSimulator:
|
||||
"""WebSocket-based Pi simulator with control feedback and voice announcements."""
|
||||
|
||||
def __init__(self, csv_path: Path, ws_url: str, interval: float = 60.0, enrichment_url: str = "http://localhost:8000", voice_enabled: bool = False):
|
||||
def __init__(self, csv_path: Path, ws_url: str, interval: float = 60.0, enrichment_url: str = "http://192.168.137.134:8000", voice_enabled: bool = False):
|
||||
self.csv_path = csv_path
|
||||
self.ws_url = ws_url
|
||||
self.enrichment_url = enrichment_url
|
||||
@@ -734,14 +734,14 @@ async def main():
|
||||
parser.add_argument(
|
||||
"--ws-url",
|
||||
type=str,
|
||||
default="ws://localhost:9000/ws/pi",
|
||||
help="WebSocket URL for AI layer (default: ws://localhost:9000/ws/pi)"
|
||||
default="ws://192.168.137.134:9000/ws/pi",
|
||||
help="WebSocket URL for AI layer (default: ws://192.168.137.134:9000/ws/pi)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--enrichment-url",
|
||||
type=str,
|
||||
default="http://localhost:8000",
|
||||
help="Enrichment service URL (default: http://localhost:8000)"
|
||||
default="http://192.168.137.134:8000",
|
||||
help="Enrichment service URL (default: http://192.168.137.134:8000)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--csv",
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Quick test script for ElevenLabs voice announcements.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, '.')
|
||||
|
||||
try:
|
||||
from elevenlabs.client import ElevenLabs
|
||||
from elevenlabs import save
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
# Check API key
|
||||
api_key = os.getenv("ELEVENLABS_API_KEY")
|
||||
if not api_key:
|
||||
print("✗ ELEVENLABS_API_KEY not found in environment")
|
||||
print("Create a .env file with: ELEVENLABS_API_KEY=your_key_here")
|
||||
sys.exit(1)
|
||||
|
||||
# Initialize client with same settings as voice_service.py
|
||||
client = ElevenLabs(api_key=api_key)
|
||||
voice_id = "mbBupyLcEivjpxh8Brkf" # Rachel voice
|
||||
|
||||
# Test message
|
||||
test_message = "Lap 3. Strategy: Conservative One Stop. Brake bias forward for turn in. Current tire degradation suggests extended first stint."
|
||||
|
||||
print(f"Testing ElevenLabs voice announcement...")
|
||||
print(f"Voice ID: {voice_id} (Rachel)")
|
||||
print(f"Message: {test_message}")
|
||||
print("-" * 60)
|
||||
|
||||
# Synthesize
|
||||
audio = client.text_to_speech.convert(
|
||||
voice_id=voice_id,
|
||||
text=test_message,
|
||||
model_id="eleven_multilingual_v2",
|
||||
voice_settings={
|
||||
"stability": 0.4,
|
||||
"similarity_boost": 0.95,
|
||||
"style": 0.7,
|
||||
"use_speaker_boost": True
|
||||
}
|
||||
)
|
||||
|
||||
# Save audio
|
||||
output_dir = Path("data/audio")
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
output_path = output_dir / "test_voice.mp3"
|
||||
|
||||
save(audio, str(output_path))
|
||||
print(f"✓ Audio saved to: {output_path}")
|
||||
|
||||
# Play audio
|
||||
print("✓ Playing audio...")
|
||||
if sys.platform == "darwin": # macOS
|
||||
os.system(f"afplay {output_path}")
|
||||
elif sys.platform == "linux":
|
||||
os.system(f"mpg123 {output_path} || ffplay -nodisp -autoexit {output_path}")
|
||||
elif sys.platform == "win32":
|
||||
os.system(f"start {output_path}")
|
||||
|
||||
print("✓ Voice test completed successfully!")
|
||||
|
||||
except ImportError as e:
|
||||
print(f"✗ elevenlabs not available: {e}")
|
||||
print("Install with: pip install elevenlabs python-dotenv")
|
||||
except Exception as e:
|
||||
print(f"✗ Voice test failed: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
@@ -1,157 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Quick test to verify WebSocket control system.
|
||||
Tests the complete flow: Pi → AI → Control Commands
|
||||
"""
|
||||
import asyncio
|
||||
import json
|
||||
import sys
|
||||
|
||||
try:
|
||||
import websockets
|
||||
except ImportError:
|
||||
print("Error: websockets not installed")
|
||||
print("Run: pip install websockets")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
async def test_websocket():
|
||||
"""Test WebSocket connection and control flow."""
|
||||
|
||||
ws_url = "ws://localhost:9000/ws/pi"
|
||||
print(f"Testing WebSocket connection to {ws_url}")
|
||||
print("-" * 60)
|
||||
|
||||
try:
|
||||
async with websockets.connect(ws_url) as websocket:
|
||||
print("✓ WebSocket connected!")
|
||||
|
||||
# 1. Receive welcome message
|
||||
welcome = await websocket.recv()
|
||||
welcome_data = json.loads(welcome)
|
||||
print(f"✓ Welcome message: {welcome_data.get('message')}")
|
||||
|
||||
# 2. Send test telemetry (lap 1)
|
||||
test_payload = {
|
||||
"type": "telemetry",
|
||||
"lap_number": 1,
|
||||
"enriched_telemetry": {
|
||||
"lap": 1,
|
||||
"tire_degradation_rate": 0.15,
|
||||
"pace_trend": "stable",
|
||||
"tire_cliff_risk": 0.05,
|
||||
"optimal_pit_window": [25, 30],
|
||||
"performance_delta": 0.0
|
||||
},
|
||||
"race_context": {
|
||||
"race_info": {
|
||||
"track_name": "Monza",
|
||||
"total_laps": 51,
|
||||
"current_lap": 1,
|
||||
"weather_condition": "Dry",
|
||||
"track_temp_celsius": 28.0
|
||||
},
|
||||
"driver_state": {
|
||||
"driver_name": "Test Driver",
|
||||
"current_position": 5,
|
||||
"current_tire_compound": "medium",
|
||||
"tire_age_laps": 1,
|
||||
"fuel_remaining_percent": 98.0
|
||||
},
|
||||
"competitors": []
|
||||
}
|
||||
}
|
||||
|
||||
print("\n→ Sending lap 1 telemetry...")
|
||||
await websocket.send(json.dumps(test_payload))
|
||||
|
||||
# 3. Wait for response (short timeout for first laps)
|
||||
response = await asyncio.wait_for(websocket.recv(), timeout=5.0)
|
||||
response_data = json.loads(response)
|
||||
|
||||
if response_data.get("type") == "control_command":
|
||||
print("✓ Received control command!")
|
||||
print(f" Brake Bias: {response_data.get('brake_bias')}/10")
|
||||
print(f" Differential Slip: {response_data.get('differential_slip')}/10")
|
||||
print(f" Message: {response_data.get('message', 'N/A')}")
|
||||
else:
|
||||
print(f"✗ Unexpected response: {response_data}")
|
||||
|
||||
# 4. Send two more laps to trigger strategy generation
|
||||
for lap_num in [2, 3]:
|
||||
test_payload["lap_number"] = lap_num
|
||||
test_payload["enriched_telemetry"]["lap"] = lap_num
|
||||
test_payload["race_context"]["race_info"]["current_lap"] = lap_num
|
||||
|
||||
print(f"\n→ Sending lap {lap_num} telemetry...")
|
||||
await websocket.send(json.dumps(test_payload))
|
||||
|
||||
# Lap 3 triggers Gemini, so expect two responses
|
||||
if lap_num == 3:
|
||||
print(f" (lap 3 will trigger strategy generation - may take 10-30s)")
|
||||
|
||||
# First response: immediate acknowledgment
|
||||
response1 = await asyncio.wait_for(websocket.recv(), timeout=5.0)
|
||||
response1_data = json.loads(response1)
|
||||
print(f"✓ Immediate response: {response1_data.get('message', 'Processing...')}")
|
||||
|
||||
# Second response: strategy-based controls
|
||||
print(" Waiting for strategy generation to complete...")
|
||||
response2 = await asyncio.wait_for(websocket.recv(), timeout=45.0)
|
||||
response2_data = json.loads(response2)
|
||||
|
||||
if response2_data.get("type") == "control_command_update":
|
||||
print(f"✓ Lap {lap_num} strategy-based control received!")
|
||||
print(f" Brake Bias: {response2_data.get('brake_bias')}/10")
|
||||
print(f" Differential Slip: {response2_data.get('differential_slip')}/10")
|
||||
|
||||
strategy = response2_data.get('strategy_name')
|
||||
if strategy and strategy != "N/A":
|
||||
print(f" Strategy: {strategy}")
|
||||
print(f" Total Strategies: {response2_data.get('total_strategies')}")
|
||||
print("✓ Strategy generation successful!")
|
||||
else:
|
||||
print(f"✗ Unexpected response: {response2_data}")
|
||||
else:
|
||||
# Laps 1-2: just one response
|
||||
response = await asyncio.wait_for(websocket.recv(), timeout=5.0)
|
||||
response_data = json.loads(response)
|
||||
|
||||
if response_data.get("type") == "control_command":
|
||||
print(f"✓ Lap {lap_num} control command received!")
|
||||
print(f" Brake Bias: {response_data.get('brake_bias')}/10")
|
||||
print(f" Differential Slip: {response_data.get('differential_slip')}/10")
|
||||
|
||||
# 5. Disconnect
|
||||
print("\n→ Sending disconnect...")
|
||||
await websocket.send(json.dumps({"type": "disconnect"}))
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("✓ ALL TESTS PASSED!")
|
||||
print("=" * 60)
|
||||
print("\nWebSocket control system is working correctly.")
|
||||
print("Ready to run: python scripts/simulate_pi_websocket.py")
|
||||
|
||||
except websockets.exceptions.WebSocketException as e:
|
||||
print(f"\n✗ WebSocket error: {e}")
|
||||
print("\nMake sure the AI Intelligence Layer is running:")
|
||||
print(" cd ai_intelligence_layer && python main.py")
|
||||
sys.exit(1)
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
print("\n✗ Timeout waiting for response")
|
||||
print("AI layer may be processing (Gemini API can be slow)")
|
||||
print("Check ai_intelligence_layer logs for details")
|
||||
sys.exit(1)
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n✗ Unexpected error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("WebSocket Control System Test")
|
||||
print("=" * 60)
|
||||
asyncio.run(test_websocket())
|
||||
Reference in New Issue
Block a user