diff --git a/ai_intelligence_layer/static/dashboard.html b/ai_intelligence_layer/static/dashboard.html index 1072211..cb6a5d5 100644 --- a/ai_intelligence_layer/static/dashboard.html +++ b/ai_intelligence_layer/static/dashboard.html @@ -3,7 +3,8 @@ - F1 AI Intelligence Layer - Vehicle Dashboard + Vehicle Dashboard +
-

🏎️ F1 AI Intelligence Layer Dashboard

-

Real-time vehicle telemetry, strategy generation, and control outputs

-
- ● Connecting... +

PERFORMANCE REPORT

+

REAL-TIME VEHICLE TELEMETRY • STRATEGY GENERATION • CONTROL OUTPUTS

+
+ ● CONNECTING...
- No vehicle connections yet. Waiting for Pi clients to connect... + ⚠️ NO VEHICLE CONNECTIONS • AWAITING PI CLIENT TELEMETRY STREAM... +
+
+ + + @@ -251,13 +663,31 @@ let ws = null; let reconnectInterval = null; + function updateStatus() { + const statusElement = document.getElementById('wsStatus'); + const connectedVehicles = Array.from(vehicles.values()).filter(v => v.connected); + + if (!ws || ws.readyState !== WebSocket.OPEN) { + statusElement.textContent = '● WS DISCONNECTED'; + statusElement.className = 'status disconnected'; + } else if (connectedVehicles.length === 0) { + statusElement.textContent = '● NO VEHICLES'; + statusElement.className = 'status disconnected'; + } else if (connectedVehicles.length === 1) { + statusElement.textContent = '● 1 VEHICLE ACTIVE'; + statusElement.className = 'status connected'; + } else { + statusElement.textContent = `● ${connectedVehicles.length} VEHICLES ACTIVE`; + statusElement.className = 'status connected'; + } + } + function connect() { ws = new WebSocket('ws://localhost:9000/ws/dashboard'); ws.onopen = () => { console.log('Dashboard WebSocket connected'); - document.getElementById('wsStatus').textContent = '● Connected'; - document.getElementById('wsStatus').className = 'status connected'; + updateStatus(); clearInterval(reconnectInterval); }; @@ -268,8 +698,7 @@ ws.onclose = () => { console.log('Dashboard WebSocket disconnected'); - document.getElementById('wsStatus').textContent = '● Disconnected'; - document.getElementById('wsStatus').className = 'status disconnected'; + updateStatus(); // Try to reconnect every 3 seconds if (!reconnectInterval) { @@ -307,6 +736,7 @@ currentControls: { brake_bias: 5, differential_slip: 5 }, currentStrategy: null }); + updateStatus(); renderVehicles(); } } @@ -315,6 +745,7 @@ if (vehicles.has(vehicleId)) { const vehicle = vehicles.get(vehicleId); vehicle.connected = false; + updateStatus(); renderVehicles(); } } @@ -347,7 +778,7 @@ const container = document.getElementById('vehicles'); if (vehicles.size === 0) { - container.innerHTML = '
No vehicle connections yet. Waiting for Pi clients to connect...
'; + container.innerHTML = '
⚠️ NO VEHICLE CONNECTIONS • AWAITING PI CLIENT TELEMETRY STREAM...
'; return; } @@ -358,16 +789,16 @@ card.className = 'vehicle-card'; const statusBadge = vehicle.connected - ? '● Connected' - : '● Disconnected'; + ? '● CONNECTED' + : '● DISCONNECTED'; let strategyHtml = ''; if (vehicle.currentStrategy) { strategyHtml = `

- Current Strategy: ${vehicle.currentStrategy.strategy_name} - ${vehicle.currentStrategy.risk_level} + ACTIVE STRATEGY: ${vehicle.currentStrategy.strategy_name.toUpperCase()} + ${vehicle.currentStrategy.risk_level.toUpperCase()}

${vehicle.currentStrategy.brief_description || ''}

@@ -377,8 +808,8 @@ card.innerHTML = `
-

Vehicle #${vehicleId}

- Connected: ${new Date(vehicle.connectedAt).toLocaleString()} +

VEHICLE #${vehicleId}

+ CONNECTED: ${new Date(vehicle.connectedAt).toLocaleString().toUpperCase()}
${statusBadge}
@@ -389,29 +820,29 @@
${vehicle.laps.length}
-
Last Lap
+
Current Lap
${vehicle.laps.length > 0 ? vehicle.laps[vehicle.laps.length - 1].lap : 'N/A'}
-
Strategies Generated
+
Strategies
${vehicle.laps.filter(l => l.strategy).length}
- Brake Bias: + Brake Bias ${vehicle.currentControls.brake_bias}
- Differential Slip: + Diff Slip ${vehicle.currentControls.differential_slip}
${strategyHtml} -

Lap History

+

LAP HISTORY

${renderLapTable(vehicle.laps)} `; @@ -421,7 +852,7 @@ function renderLapTable(laps) { if (laps.length === 0) { - return '
No lap data yet...
'; + return '
NO LAP DATA RECORDED YET...
'; } // Reverse to show most recent first @@ -431,26 +862,26 @@ - - - - - - - - + + + + + + + + `; - reversedLaps.forEach(lap => { + reversedLaps.forEach((lap, index) => { const controls = lap.control_output || { brake_bias: '-', differential_slip: '-' }; - const strategy = lap.strategy ? lap.strategy.strategy_name : '-'; - const trendBadge = lap.pace_trend ? `${lap.pace_trend}` : '-'; + const strategy = lap.strategy ? lap.strategy.strategy_name.toUpperCase() : '-'; + const trendBadge = lap.pace_trend ? `${lap.pace_trend.toUpperCase()}` : '-'; tableHtml += ` - + @@ -467,6 +898,117 @@ return tableHtml; } + function showLapDetails(lap) { + const modal = document.getElementById('lapModal'); + const modalTitle = document.getElementById('modalTitle'); + const modalBody = document.getElementById('modalBody'); + + modalTitle.textContent = `LAP ${lap.lap} DETAILS`; + + let bodyHtml = ''; + + // Telemetry data section + bodyHtml += ` + + `; + + // Control outputs section + if (lap.control_output) { + bodyHtml += ` + + `; + } + + // Strategy section + if (lap.strategy) { + bodyHtml += ` + + `; + } else { + bodyHtml += ` + + `; + } + + modalBody.innerHTML = bodyHtml; + modal.style.display = 'block'; + } + + // Modal close handlers + document.addEventListener('DOMContentLoaded', function() { + const modal = document.getElementById('lapModal'); + const closeBtn = document.querySelector('.close'); + + closeBtn.onclick = function() { + modal.style.display = 'none'; + } + + window.onclick = function(event) { + if (event.target == modal) { + modal.style.display = 'none'; + } + } + }); + // Initialize connection connect();
LapTire DegPace TrendCliff RiskBrake BiasDiff SlipStrategyTimeLAPTIRE DEGPACE TRENDCLIFF RISKBRAKEDIFFSTRATEGYTIME
${lap.lap} ${(lap.tire_degradation_rate * 100).toFixed(0)}% ${trendBadge}