/ web / src / lib / general_socket.svelte.ts
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  }