Skip to content

Commit 71b9f33

Browse files
websocket timer change
1 parent 562a03e commit 71b9f33

File tree

1 file changed

+78
-34
lines changed

1 file changed

+78
-34
lines changed

src/frontend/src/services/WebSocketService.tsx

Lines changed: 78 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -47,60 +47,83 @@ class WebSocketService {
4747
private ws: WebSocket | null = null;
4848
private reconnectAttempts = 0;
4949
private maxReconnectAttempts = 5;
50-
private reconnectDelay = 1000;
50+
private reconnectDelay = 12000; // Changed from 1000ms to 12000ms (12 seconds)
5151
private listeners: Map<string, Set<(message: StreamMessage) => void>> = new Map();
5252
private planSubscriptions: Set<string> = new Set();
53+
private reconnectTimer: NodeJS.Timeout | null = null; // Add timer tracking
54+
private isConnecting = false; // Add connection state tracking
5355

5456
/**
5557
* Connect to WebSocket server
5658
*/
5759
connect(): Promise<void> {
5860
return new Promise((resolve, reject) => {
61+
// Prevent multiple simultaneous connection attempts
62+
if (this.isConnecting) {
63+
console.log('Connection attempt already in progress');
64+
return;
65+
}
66+
67+
// Clear any existing reconnection timer
68+
if (this.reconnectTimer) {
69+
clearTimeout(this.reconnectTimer);
70+
this.reconnectTimer = null;
71+
}
72+
5973
try {
74+
this.isConnecting = true;
75+
6076
// Get WebSocket URL from environment or default to localhost
6177
const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
6278
const wsHost = process.env.REACT_APP_WS_HOST || '127.0.0.1:8000';
6379
const processId = crypto.randomUUID(); // Generate unique process ID for this session
6480

65-
// const wsUrl = `${wsProtocol}//${wsHost}/api/v3/socket/${processId}`;
6681
// Build WebSocket URL with authentication headers as query parameters
67-
const userId = getUserId(); // Import this from config
68-
const wsUrl = `${wsProtocol}//${wsHost}/api/v3/socket/${processId}?user_id=${encodeURIComponent(userId)}`;
82+
const userId = getUserId(); // Import this from config
83+
const wsUrl = `${wsProtocol}//${wsHost}/api/v3/socket/${processId}?user_id=${encodeURIComponent(userId)}`;
6984

7085
console.log('Connecting to WebSocket:', wsUrl);
7186

7287
this.ws = new WebSocket(wsUrl);
7388

7489
this.ws.onopen = () => {
75-
console.log('WebSocket connected');
90+
console.log('WebSocket connected successfully');
7691
this.reconnectAttempts = 0;
92+
this.isConnecting = false;
7793
this.emit('connection_status', { connected: true });
7894
resolve();
7995
};
8096

8197
this.ws.onmessage = (event) => {
8298
try {
83-
const message: StreamMessage = JSON.parse(event.data);
99+
const message: StreamMessage = JSON.parse(event.data);
84100
this.handleMessage(message);
85101
} catch (error) {
86102
console.error('Error parsing WebSocket message:', error, 'Raw data:', event.data);
87103
this.emit('error', { error: 'Failed to parse WebSocket message' });
88104
}
89105
};
90106

91-
this.ws.onclose = () => {
92-
console.log('WebSocket disconnected');
107+
this.ws.onclose = (event) => {
108+
console.log('WebSocket disconnected', event.code, event.reason);
109+
this.isConnecting = false;
93110
this.emit('connection_status', { connected: false });
94-
this.attemptReconnect();
111+
112+
// Only attempt reconnect if it wasn't a manual disconnect
113+
if (event.code !== 1000) { // 1000 = normal closure
114+
this.attemptReconnect();
115+
}
95116
};
96117

97118
this.ws.onerror = (error) => {
98119
console.error('WebSocket error:', error);
120+
this.isConnecting = false;
99121
this.emit('error', { error: 'WebSocket connection failed' });
100122
reject(error);
101123
};
102124

103125
} catch (error) {
126+
this.isConnecting = false;
104127
reject(error);
105128
}
106129
});
@@ -110,11 +133,22 @@ class WebSocketService {
110133
* Disconnect from WebSocket server
111134
*/
112135
disconnect(): void {
136+
console.log('Manually disconnecting WebSocket');
137+
138+
// Clear any pending reconnection attempts
139+
if (this.reconnectTimer) {
140+
clearTimeout(this.reconnectTimer);
141+
this.reconnectTimer = null;
142+
}
143+
144+
this.reconnectAttempts = this.maxReconnectAttempts; // Prevent reconnection
145+
113146
if (this.ws) {
114-
this.ws.close();
147+
this.ws.close(1000, 'Manual disconnect'); // Use normal closure code
115148
this.ws = null;
116149
}
117150
this.planSubscriptions.clear();
151+
this.isConnecting = false;
118152
}
119153

120154
/**
@@ -209,9 +243,6 @@ class WebSocketService {
209243
/**
210244
* Handle incoming WebSocket messages
211245
*/
212-
/**
213-
* Handle incoming WebSocket messages
214-
*/
215246
private handleMessage(message: StreamMessage): void {
216247
console.log('WebSocket message received:', message);
217248

@@ -234,26 +265,39 @@ class WebSocketService {
234265
*/
235266
private attemptReconnect(): void {
236267
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
237-
console.log('Max reconnection attempts reached');
268+
console.log('Max reconnection attempts reached - stopping reconnect attempts');
238269
this.emit('error', { error: 'Max reconnection attempts reached' });
239270
return;
240271
}
241272

273+
// Prevent multiple simultaneous reconnection attempts
274+
if (this.isConnecting || this.reconnectTimer) {
275+
console.log('Reconnection attempt already in progress');
276+
return;
277+
}
278+
242279
this.reconnectAttempts++;
280+
// Use exponential backoff: 12s, 24s, 48s, 96s, 192s
243281
const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
244282

245-
console.log(`Attempting to reconnect in ${delay}ms (attempt ${this.reconnectAttempts})`);
283+
console.log(`Scheduling reconnection attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts} in ${delay/1000}s`);
246284

247-
setTimeout(() => {
285+
this.reconnectTimer = setTimeout(() => {
286+
this.reconnectTimer = null;
287+
console.log(`Attempting reconnection (attempt ${this.reconnectAttempts})`);
288+
248289
this.connect()
249290
.then(() => {
291+
console.log('Reconnection successful - re-subscribing to plans');
250292
// Re-subscribe to all plans
251293
this.planSubscriptions.forEach(planId => {
252294
this.subscribeToPlan(planId);
253295
});
254296
})
255297
.catch((error) => {
256298
console.error('Reconnection failed:', error);
299+
// The connect() method will trigger another reconnection attempt
300+
// through the onclose handler if needed
257301
});
258302
}, delay);
259303
}
@@ -277,28 +321,28 @@ class WebSocketService {
277321
}
278322

279323
/**
280-
* Send plan approval response
281-
*/
324+
* Send plan approval response
325+
*/
282326
sendPlanApprovalResponse(response: PlanApprovalResponseData): void {
283-
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
284-
console.error('WebSocket not connected - cannot send plan approval response');
285-
this.emit('error', { error: 'Cannot send plan approval response - WebSocket not connected' });
286-
return;
287-
}
327+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
328+
console.error('WebSocket not connected - cannot send plan approval response');
329+
this.emit('error', { error: 'Cannot send plan approval response - WebSocket not connected' });
330+
return;
331+
}
288332

289-
try {
290-
const message = {
291-
type: 'plan_approval_response',
292-
data: response
293-
};
294-
this.ws.send(JSON.stringify(message));
295-
console.log('Plan approval response sent:', response);
296-
} catch (error) {
297-
console.error('Failed to send plan approval response:', error);
298-
this.emit('error', { error: 'Failed to send plan approval response' });
333+
try {
334+
const message = {
335+
type: 'plan_approval_response',
336+
data: response
337+
};
338+
this.ws.send(JSON.stringify(message));
339+
console.log('Plan approval response sent:', response);
340+
} catch (error) {
341+
console.error('Failed to send plan approval response:', error);
342+
this.emit('error', { error: 'Failed to send plan approval response' });
343+
}
299344
}
300345
}
301-
}
302346

303347
// Export singleton instance
304348
export const webSocketService = new WebSocketService();

0 commit comments

Comments
 (0)