/** * GO RACER - Core Application & RFID Simulation Engine * Framework: Go Racer (goracermx.com) * Version: 1.0.0 * Hardware Integration: Go Gate (Start) & Go Win (RFID Finish Line) * Edge/Cloud: UnoQ Local Edge & Google Tech Stack (Go Global) * Theme: Electric Cyan (#00F0FF) / Dark Metallic */ // ========================================== // 1. DATA MODELS & INITIAL STATE // ========================================== const GoRacerConfig = { version: "1.0.0-edge", edgeDevice: "UnoQ-Gate01-Alpha", cloudEndpoint: "https://api.goracermx.com/v1/telemetry", syncIntervalMs: 3000, rfidScanThresholdMs: 15000, // min time between valid lap scans }; // Active Heat Rider Roster let heatRoster = [ { riderId: "R-101", number: "7", name: "Chase Sexton", class: "450 Pro", rfidTag: "RFID-7001-A", laps: 0, lastScanTime: null, lapTimes: [], bestLap: null, totalTime: 0, status: "READY" }, { riderId: "R-102", number: "18", name: "Jett Lawrence", class: "450 Pro", rfidTag: "RFID-1802-B", laps: 0, lastScanTime: null, lapTimes: [], bestLap: null, totalTime: 0, status: "READY" }, { riderId: "R-103", number: "3", name: "Eli Tomac", class: "450 Pro", rfidTag: "RFID-3003-C", laps: 0, lastScanTime: null, lapTimes: [], bestLap: null, totalTime: 0, status: "READY" }, { riderId: "R-104", number: "21", name: "Jason Anderson", class: "450 Pro", rfidTag: "RFID-2104-D", laps: 0, lastScanTime: null, lapTimes: [], bestLap: null, totalTime: 0, status: "READY" }, { riderId: "R-105", number: "E1", name: "Stark Varg Pro", class: "USA E MOTO", RFIDTag: "RFID-E105-E", laps: 0, lastScanTime: null, lapTimes: [], bestLap: null, totalTime: 0, status: "READY" }, ]; let raceState = { heatId: "HEAT-2026-MAIN-01", status: "STAGING", // STAGING, GATE_DROPPED, IN_PROGRESS, FINISHED startTime: null, gateDropTimestamp: null, totalLaps: 5, simInterval: null, cloudSyncBuffer: [], }; // ========================================== // 2. GO GATE - START LINE MECHANISM // ========================================== /** * Triggers the Go Gate physical/virtual release mechanism. */ function triggerGoGateRelease() { if (raceState.status === "IN_PROGRESS") { console.warn("[GO GATE] Race already in progress!"); return; } raceState.status = "GATE_DROPPED"; raceState.gateDropTimestamp = performance.now(); raceState.startTime = Date.now(); console.log(`[GO GATE] 🚦 GATE DROP RELEASED at ${new Date(raceState.startTime).toISOString()}`); // Update rider statuses heatRoster.forEach(rider => { rider.status = "RACING"; rider.lastScanTime = raceState.gateDropTimestamp; }); raceState.status = "IN_PROGRESS"; // Broadcast local UnoQ event broadcastUnoQEvent("GO_GATE_RELEASE", { heatId: raceState.heatId, timestamp: raceState.startTime, riderCount: heatRoster.length }); // Start RFID Auto Simulation for testing startRFIDSimulationEngine(); } // ========================================== // 3. GO WIN - RFID FINISH LINE TIMING // ========================================== /** * Processes an incoming RFID scan from the Go Win finish line structure. * @param {string} rfidTag - The transponder tag ID read by the RFID sensor array. */ function processGoWinRFIDScan(rfidTag) { if (raceState.status !== "IN_PROGRESS") { console.warn(`[GO WIN] Scan ignored for tag ${rfidTag} — Race not active.`); return null; } const rider = heatRoster.find(r => r.rfidTag === rfidTag || r.RFIDTag === rfidTag); if (!rider) { console.error(`[GO WIN] Unknown RFID tag scan: ${rfidTag}`); return null; } const now = performance.now(); const timeSinceLastScan = rider.lastScanTime ? (now - rider.lastScanTime) : 0; // Filter out immediate double-scans / sensor bounce if (rider.laps > 0 && timeSinceLastScan < 3000) { console.warn(`[GO WIN] Debounce filter active for #${rider.number} ${rider.name}`); return null; } // Calculate Lap Time in seconds const lapTimeSeconds = +(timeSinceLastScan / 1000).toFixed(3); rider.laps += 1; rider.lastScanTime = now; rider.lapTimes.push(lapTimeSeconds); rider.totalTime = +((now - raceState.gateDropTimestamp) / 1000).toFixed(3); // Check Best Lap if (!rider.bestLap || lapTimeSeconds < rider.bestLap) { rider.bestLap = lapTimeSeconds; } // Check heat finish condition if (rider.laps >= raceState.totalLaps) { rider.status = "FINISHED"; } const scanPayload = { event: "GO_WIN_LAP_SCAN", heatId: raceState.heatId, riderId: rider.riderId, number: rider.number, name: rider.name, lap: rider.laps, lapTime: lapTimeSeconds, bestLap: rider.bestLap, totalTime: rider.totalTime, timestamp: Date.now() }; console.log(`[GO WIN] 🏁 LAP ${rider.laps} RECORDED: #${rider.number} ${rider.name} - ${lapTimeSeconds}s (Best: ${rider.bestLap}s)`); // Buffer scan for Google Tech Stack / Go Global Cloud Sync raceState.cloudSyncBuffer.push(scanPayload); // Check if all riders finished if (heatRoster.every(r => r.status === "FINISHED")) { raceState.status = "FINISHED"; stopRFIDSimulationEngine(); console.log(`[GO RACER] 🏆 HEAT COMPLETE! Leaderboard finalized.`); } // Trigger UI Re-render updateLiveLeaderboardUI(); return scanPayload; } // ========================================== // 4. SIMULATION ENGINE (TESTING MODE) // ========================================== function startRFIDSimulationEngine() { if (raceState.simInterval) clearInterval(raceState.simInterval); console.log("[SIMULATOR] Starting live RFID finish line simulator..."); raceState.simInterval = setInterval(() => { if (raceState.status !== "IN_PROGRESS") return; // Pick a active rider randomly to pass finish line const activeRiders = heatRoster.filter(r => r.status === "RACING"); if (activeRiders.length === 0) return; const randomRider = activeRiders[Math.floor(Math.random() * activeRiders.length)]; const tag = randomRider.rfidTag || randomRider.RFIDTag; processGoWinRFIDScan(tag); }, 3500); // Trigger a finish line scan every 3.5 seconds } function stopRFIDSimulationEngine() { if (raceState.simInterval) { clearInterval(raceState.simInterval); raceState.simInterval = null; } } // ========================================== // 5. GO GLOBAL & UNOQ SYNC CONTROLLER // ========================================== function broadcastUnoQEvent(type, payload) { console.log(`[UNOQ EDGE] Local Event Broadcast [${type}]`, payload); } function syncToGoogleCloudBackbone() { if (raceState.cloudSyncBuffer.length === 0) return; const recordsToSync = [...raceState.cloudSyncBuffer]; raceState.cloudSyncBuffer = []; console.log(`[GO GLOBAL] ☁️ Syncing ${recordsToSync.length} telemetry records to Google Tech Stack...`); // Simulate API fetch POST to cloud setTimeout(() => { console.log(`[GO GLOBAL] ✅ Cloud Sync Successful (${recordsToSync.length} packets committed).`); }, 400); } // Periodic Cloud Sync Loop setInterval(syncToGoogleCloudBackbone, GoRacerConfig.syncIntervalMs); // ========================================== // 6. UI RENDERER & LEADERBOARD CONTROLLER // ========================================== function updateLiveLeaderboardUI() { // Sort roster by Laps (descending) and Total Time (ascending) const sortedRoster = [...heatRoster].sort((a, b) => { if (b.laps !== a.laps) return b.laps - a.laps; return a.totalTime - b.totalTime; }); // If running in browser DOM if (typeof document !== 'undefined') { const leaderboardContainer = document.getElementById('go-racer-leaderboard'); if (!leaderboardContainer) return; let html = `