From 1a32c36b82a3da1c297073f05aceb288608e2d29 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Sun, 19 Nov 2023 00:30:31 +0100 Subject: [PATCH] fix: improve websocket usage error handling MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- .../ocpp/OCPPRequestService.ts | 78 +++++++++++-------- ui/web/src/composables/UIClient.ts | 10 ++- 2 files changed, 55 insertions(+), 33 deletions(-) diff --git a/src/charging-station/ocpp/OCPPRequestService.ts b/src/charging-station/ocpp/OCPPRequestService.ts index c8d4c2fa..b1b3edfa 100644 --- a/src/charging-station/ocpp/OCPPRequestService.ts +++ b/src/charging-station/ocpp/OCPPRequestService.ts @@ -384,57 +384,73 @@ export abstract class OCPPRequestService { responseCallback, errorCallback, ); - let sendError = false; // Check if wsConnection opened - const wsOpened = chargingStation.isWebSocketConnectionOpened() === true; - if (wsOpened) { + if (chargingStation.isWebSocketConnectionOpened() === true) { const beginId = PerformanceStatistics.beginMeasure(commandName); - try { - setTimeout(() => { - return errorCallback( + const sendTimeout = setTimeout(() => { + return errorCallback( + new OCPPError( + ErrorType.GENERIC_ERROR, + `Timeout for message id '${messageId}'`, + commandName, + (messagePayload as JsonObject)?.details ?? Constants.EMPTY_FROZEN_OBJECT, + ), + false, + ); + }, OCPPConstants.OCPP_WEBSOCKET_TIMEOUT); + chargingStation.wsConnection?.send(messageToSend, (error?: Error) => { + if (error && params?.skipBufferingOnError === false) { + // Buffer + chargingStation.bufferMessage(messageToSend); + // Reject and keep request in the cache + return reject( new OCPPError( ErrorType.GENERIC_ERROR, - `Timeout for message id '${messageId}'`, + `WebSocket errored for buffered message id '${messageId}' with content '${messageToSend}'`, commandName, - (messagePayload as JsonObject)?.details ?? Constants.EMPTY_FROZEN_OBJECT, + { name: error.name, message: error.message, stack: error.stack } ?? + Constants.EMPTY_FROZEN_OBJECT, ), - false, ); - }, OCPPConstants.OCPP_WEBSOCKET_TIMEOUT); - chargingStation.wsConnection?.send(messageToSend); - logger.debug( - `${chargingStation.logPrefix()} >> Command '${commandName}' sent ${OCPPServiceUtils.getMessageTypeString( - messageType, - )} payload: ${messageToSend}`, - ); - } catch (error) { - logger.error( - `${chargingStation.logPrefix()} >> Command '${commandName}' failed to send ${OCPPServiceUtils.getMessageTypeString( - messageType, - )} payload: ${messageToSend}:`, - error, - ); - sendError = true; - } + } else if (error) { + const ocppError = new OCPPError( + ErrorType.GENERIC_ERROR, + `WebSocket errored for non buffered message id '${messageId}' with content '${messageToSend}'`, + commandName, + { name: error.name, message: error.message, stack: error.stack } ?? + Constants.EMPTY_FROZEN_OBJECT, + ); + // Reject response + if (messageType !== MessageType.CALL_MESSAGE) { + return reject(ocppError); + } + // Reject and remove request from the cache + return errorCallback(ocppError, false); + } + clearTimeout(sendTimeout); + }); + logger.debug( + `${chargingStation.logPrefix()} >> Command '${commandName}' sent ${OCPPServiceUtils.getMessageTypeString( + messageType, + )} payload: ${messageToSend}`, + ); PerformanceStatistics.endMeasure(commandName, beginId); - } - const wsClosedOrErrored = !wsOpened || sendError === true; - if (wsClosedOrErrored && params?.skipBufferingOnError === false) { + } else if (params?.skipBufferingOnError === false) { // Buffer chargingStation.bufferMessage(messageToSend); // Reject and keep request in the cache return reject( new OCPPError( ErrorType.GENERIC_ERROR, - `WebSocket closed or errored for buffered message id '${messageId}' with content '${messageToSend}'`, + `WebSocket closed for buffered message id '${messageId}' with content '${messageToSend}'`, commandName, (messagePayload as JsonObject)?.details ?? Constants.EMPTY_FROZEN_OBJECT, ), ); - } else if (wsClosedOrErrored) { + } else { const ocppError = new OCPPError( ErrorType.GENERIC_ERROR, - `WebSocket closed or errored for non buffered message id '${messageId}' with content '${messageToSend}'`, + `WebSocket closed for non buffered message id '${messageId}' with content '${messageToSend}'`, commandName, (messagePayload as JsonObject)?.details ?? Constants.EMPTY_FROZEN_OBJECT, ); diff --git a/ui/web/src/composables/UIClient.ts b/ui/web/src/composables/UIClient.ts index c3107d63..745accdf 100644 --- a/ui/web/src/composables/UIClient.ts +++ b/ui/web/src/composables/UIClient.ts @@ -153,11 +153,17 @@ export class UIClient { this.openWS(); } if (this.ws.readyState === WebSocket.OPEN) { - setTimeout(() => { + const sendTimeout = setTimeout(() => { this.deleteResponseHandler(uuid); return reject(new Error(`Send request '${command}' message timeout`)); }, 60 * 1000); - this.ws.send(msg); + try { + this.ws.send(msg); + } catch (error) { + reject(error); + } finally { + clearTimeout(sendTimeout); + } } else { throw new Error(`Send request '${command}' message: connection not opened`); } -- 2.34.1