general_socket.svelte.ts
1 import { ws_url } from './utils'; 2 import { wsStatus, type SystemData } from './types'; 3 4 export const gdata = $state({ 5 data: null as SystemData | null, 6 prevDataPoints: [] as SystemData[], 7 status: wsStatus.INIT as wsStatus 8 }); 9 10 let g_ws: WebSocket | null = null; 11 let reconnectAttempt = 0; 12 let reconnectTimeout: number | null = null; 13 let isReconnecting = false; 14 15 function getReconnectDelay(): number { 16 // Backoff with a maximum of 10 seconds 17 return Math.min(2000 * (reconnectAttempt + 1), 10000); 18 } 19 20 function scheduleReconnect() { 21 if (isReconnecting) return; 22 23 isReconnecting = true; 24 const delay = getReconnectDelay(); 25 26 //console.log(`Scheduling reconnect attempt in ${delay}ms`); 27 28 if (reconnectTimeout !== null) { 29 clearTimeout(reconnectTimeout); 30 } 31 32 reconnectTimeout = setTimeout(() => { 33 reconnectAttempt++;; 34 isReconnecting = false; 35 open_ws(); 36 }, delay) as unknown as number; 37 } 38 39 export function close_ws() { 40 if (g_ws !== null) { 41 g_ws.close(); 42 } 43 } 44 45 export function open_ws() { 46 // Clean up existing connection if any 47 if (g_ws !== null) { 48 try { 49 g_ws.onopen = null; 50 g_ws.onclose = null; 51 g_ws.onerror = null; 52 g_ws.onmessage = null; 53 g_ws.close(); 54 } catch (e) { 55 console.error("Error while closing existing WebSocket:", e); 56 } 57 g_ws = null; 58 } 59 60 try { 61 g_ws = new WebSocket(import.meta.env.PROD 62 ? ws_url('ws/g') 63 : "ws://localhost:30000/ws/g"); 64 } 65 catch (e) { 66 console.error("WebSocket connection failed: ", e); 67 gdata.status = wsStatus.ERROR; 68 scheduleReconnect(); 69 return; 70 } 71 72 g_ws.onopen = function (event) { 73 gdata.status = wsStatus.WAITING; 74 //console.log("WebSocket opened:", event); 75 reconnectAttempt = 0; 76 } 77 78 g_ws.onerror = function (event) { 79 gdata.status = wsStatus.ERROR; 80 console.error("WebSocket error observed:", event); 81 // We'll let onclose handle the reconnection 82 } 83 84 g_ws.onclose = function (event) { 85 gdata.status = wsStatus.DISCONNECTED; 86 //console.log("WebSocket closed:", event); 87 scheduleReconnect(); 88 } 89 90 g_ws.onmessage = async function (event) { 91 gdata.status = wsStatus.CONNECTED; 92 const compressedData = await event.data.arrayBuffer(); 93 const decompressStream = new DecompressionStream('gzip'); 94 const decompressedStream = new ReadableStream({ 95 start(controller) { 96 controller.enqueue(compressedData); 97 controller.close(); 98 } 99 }).pipeThrough(decompressStream); 100 101 const responseData = await new Response(decompressedStream).text(); 102 103 if (gdata.data !== null) 104 gdata.prevDataPoints.push(gdata.data); 105 106 if (gdata.prevDataPoints.length > 60) { 107 gdata.prevDataPoints.shift(); 108 } 109 gdata.data = JSON.parse(responseData) as SystemData; 110 } 111 }