2025-08-24 14:04:17 +02:00
|
|
|
// WebSocket communication module
|
|
|
|
|
export class WebSocketManager {
|
|
|
|
|
constructor(onMessage, onStatusChange) {
|
|
|
|
|
this.websocket = null;
|
|
|
|
|
this.onMessage = onMessage;
|
|
|
|
|
this.onStatusChange = onStatusChange;
|
2025-08-25 10:14:03 +02:00
|
|
|
this.reconnectAttempts = 0;
|
|
|
|
|
this.maxReconnectAttempts = 5;
|
|
|
|
|
this.reconnectInterval = null;
|
|
|
|
|
this.lastMessageTime = 0;
|
|
|
|
|
this.messageCount = 0;
|
|
|
|
|
this.isManualDisconnect = false;
|
2025-08-24 14:04:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async connect() {
|
2025-08-25 10:14:03 +02:00
|
|
|
// Clear any existing reconnect interval
|
|
|
|
|
if (this.reconnectInterval) {
|
|
|
|
|
clearTimeout(this.reconnectInterval);
|
|
|
|
|
this.reconnectInterval = null;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-24 14:04:17 +02:00
|
|
|
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
|
|
|
const wsUrl = `${protocol}//${window.location.host}/ws`;
|
|
|
|
|
|
|
|
|
|
try {
|
2025-08-25 10:14:03 +02:00
|
|
|
console.log(`WebSocket connecting to ${wsUrl} (attempt ${this.reconnectAttempts + 1})`);
|
2025-08-24 14:04:17 +02:00
|
|
|
this.websocket = new WebSocket(wsUrl);
|
|
|
|
|
|
|
|
|
|
this.websocket.onopen = () => {
|
2025-08-25 10:14:03 +02:00
|
|
|
console.log('WebSocket connected successfully');
|
|
|
|
|
this.reconnectAttempts = 0; // Reset on successful connection
|
2025-08-24 14:04:17 +02:00
|
|
|
this.onStatusChange('connected');
|
|
|
|
|
};
|
|
|
|
|
|
2025-08-25 10:14:03 +02:00
|
|
|
this.websocket.onclose = (event) => {
|
|
|
|
|
console.log(`WebSocket closed: code=${event.code}, reason=${event.reason}, wasClean=${event.wasClean}`);
|
|
|
|
|
this.websocket = null;
|
|
|
|
|
|
|
|
|
|
if (!this.isManualDisconnect) {
|
|
|
|
|
this.onStatusChange('disconnected');
|
|
|
|
|
this.scheduleReconnect();
|
|
|
|
|
}
|
2025-08-24 14:04:17 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.websocket.onerror = (error) => {
|
|
|
|
|
console.error('WebSocket error:', error);
|
|
|
|
|
this.onStatusChange('disconnected');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.websocket.onmessage = (event) => {
|
|
|
|
|
try {
|
|
|
|
|
const message = JSON.parse(event.data);
|
2025-08-25 10:14:03 +02:00
|
|
|
this.lastMessageTime = Date.now();
|
|
|
|
|
this.messageCount++;
|
2025-08-24 14:04:17 +02:00
|
|
|
|
2025-08-25 10:14:03 +02:00
|
|
|
// Log message reception for debugging
|
|
|
|
|
if (this.messageCount % 10 === 0) {
|
|
|
|
|
console.debug(`Received ${this.messageCount} WebSocket messages`);
|
|
|
|
|
}
|
2025-08-24 14:04:17 +02:00
|
|
|
|
|
|
|
|
this.onMessage(message);
|
|
|
|
|
} catch (error) {
|
2025-08-25 10:14:03 +02:00
|
|
|
console.error('Failed to parse WebSocket message:', error, event.data);
|
2025-08-24 14:04:17 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('WebSocket connection failed:', error);
|
|
|
|
|
this.onStatusChange('disconnected');
|
2025-08-25 10:14:03 +02:00
|
|
|
this.scheduleReconnect();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scheduleReconnect() {
|
|
|
|
|
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
|
|
|
console.error(`Max reconnection attempts (${this.maxReconnectAttempts}) reached. Giving up.`);
|
|
|
|
|
this.onStatusChange('failed');
|
|
|
|
|
return;
|
2025-08-24 14:04:17 +02:00
|
|
|
}
|
2025-08-25 10:14:03 +02:00
|
|
|
|
|
|
|
|
this.reconnectAttempts++;
|
|
|
|
|
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000); // Exponential backoff, max 30s
|
|
|
|
|
|
|
|
|
|
console.log(`Scheduling WebSocket reconnection in ${delay}ms (attempt ${this.reconnectAttempts})`);
|
|
|
|
|
this.onStatusChange('reconnecting');
|
|
|
|
|
|
|
|
|
|
this.reconnectInterval = setTimeout(() => {
|
|
|
|
|
this.connect();
|
|
|
|
|
}, delay);
|
2025-08-24 14:04:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
disconnect() {
|
2025-08-25 10:14:03 +02:00
|
|
|
this.isManualDisconnect = true;
|
|
|
|
|
|
|
|
|
|
if (this.reconnectInterval) {
|
|
|
|
|
clearTimeout(this.reconnectInterval);
|
|
|
|
|
this.reconnectInterval = null;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-24 14:04:17 +02:00
|
|
|
if (this.websocket) {
|
|
|
|
|
this.websocket.close();
|
|
|
|
|
this.websocket = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-25 10:14:03 +02:00
|
|
|
|
|
|
|
|
getStats() {
|
|
|
|
|
return {
|
|
|
|
|
messageCount: this.messageCount,
|
|
|
|
|
lastMessageTime: this.lastMessageTime,
|
|
|
|
|
reconnectAttempts: this.reconnectAttempts,
|
|
|
|
|
isConnected: this.websocket && this.websocket.readyState === WebSocket.OPEN
|
|
|
|
|
};
|
|
|
|
|
}
|
2025-08-24 14:04:17 +02:00
|
|
|
}
|