1 import OCPPError from
'../../exception/OCPPError';
2 import PerformanceStatistics from
'../../performance/PerformanceStatistics';
3 import { EmptyObject
} from
'../../types/EmptyObject';
4 import { HandleErrorParams
} from
'../../types/Error';
5 import { JsonObject
, JsonType
} from
'../../types/JsonType';
6 import { ErrorType
} from
'../../types/ocpp/ErrorType';
7 import { MessageType
} from
'../../types/ocpp/MessageType';
9 IncomingRequestCommand
,
14 } from
'../../types/ocpp/Requests';
15 import { ErrorResponse
, Response
} from
'../../types/ocpp/Responses';
16 import Constants from
'../../utils/Constants';
17 import logger from
'../../utils/Logger';
18 import Utils from
'../../utils/Utils';
19 import type ChargingStation from
'../ChargingStation';
20 import type OCPPResponseService from
'./OCPPResponseService';
22 export default abstract class OCPPRequestService
{
23 private static instance
: OCPPRequestService
| null = null;
25 private readonly ocppResponseService
: OCPPResponseService
;
27 protected constructor(ocppResponseService
: OCPPResponseService
) {
28 this.ocppResponseService
= ocppResponseService
;
29 this.requestHandler
.bind(this);
30 this.sendResponse
.bind(this);
31 this.sendError
.bind(this);
34 public static getInstance
<T
extends OCPPRequestService
>(
35 this: new (ocppResponseService
: OCPPResponseService
) => T
,
36 ocppResponseService
: OCPPResponseService
38 if (!OCPPRequestService
.instance
) {
39 OCPPRequestService
.instance
= new this(ocppResponseService
);
41 return OCPPRequestService
.instance
as T
;
44 public async sendResponse(
45 chargingStation
: ChargingStation
,
47 messagePayload
: JsonType
,
48 commandName
: IncomingRequestCommand
49 ): Promise
<ResponseType
> {
51 // Send response message
52 return await this.internalSendMessage(
56 MessageType
.CALL_RESULT_MESSAGE
,
60 this.handleRequestError(chargingStation
, commandName
, error
as Error);
64 public async sendError(
65 chargingStation
: ChargingStation
,
68 commandName
: RequestCommand
| IncomingRequestCommand
69 ): Promise
<ResponseType
> {
72 return await this.internalSendMessage(
76 MessageType
.CALL_ERROR_MESSAGE
,
80 this.handleRequestError(chargingStation
, commandName
, error
as Error);
84 protected async sendMessage(
85 chargingStation
: ChargingStation
,
87 messagePayload
: JsonType
,
88 commandName
: RequestCommand
,
89 params
: RequestParams
= {
90 skipBufferingOnError
: false,
91 triggerMessage
: false,
93 ): Promise
<ResponseType
> {
95 return await this.internalSendMessage(
99 MessageType
.CALL_MESSAGE
,
104 this.handleRequestError(chargingStation
, commandName
, error
as Error, { throwError
: false });
108 private async internalSendMessage(
109 chargingStation
: ChargingStation
,
111 messagePayload
: JsonType
| OCPPError
,
112 messageType
: MessageType
,
113 commandName
?: RequestCommand
| IncomingRequestCommand
,
114 params
: RequestParams
= {
115 skipBufferingOnError
: false,
116 triggerMessage
: false,
118 ): Promise
<ResponseType
> {
120 (chargingStation
.isInUnknownState() && commandName
=== RequestCommand
.BOOT_NOTIFICATION
) ||
121 (!chargingStation
.getOcppStrictCompliance() && chargingStation
.isInUnknownState()) ||
122 chargingStation
.isInAcceptedState() ||
123 (chargingStation
.isInPendingState() &&
124 (params
.triggerMessage
|| messageType
=== MessageType
.CALL_RESULT_MESSAGE
))
126 // eslint-disable-next-line @typescript-eslint/no-this-alias
128 // Send a message through wsConnection
129 return Utils
.promiseWithTimeout(
130 new Promise((resolve
, reject
) => {
131 const messageToSend
= this.buildMessageToSend(
140 if (chargingStation
.getEnableStatistics()) {
141 chargingStation
.performanceStatistics
.addRequestStatistic(commandName
, messageType
);
143 // Check if wsConnection opened
144 if (chargingStation
.isWebSocketConnectionOpened()) {
146 const beginId
= PerformanceStatistics
.beginMeasure(commandName
);
147 // FIXME: Handle sending error
148 chargingStation
.wsConnection
.send(messageToSend
);
149 PerformanceStatistics
.endMeasure(commandName
, beginId
);
151 `${chargingStation.logPrefix()} >> Command '${commandName}' sent ${this.getMessageTypeString(
153 )} payload: ${messageToSend}`
155 } else if (!params
.skipBufferingOnError
) {
157 chargingStation
.bufferMessage(messageToSend
);
158 const ocppError
= new OCPPError(
159 ErrorType
.GENERIC_ERROR
,
160 `WebSocket closed for buffered message id '${messageId}' with content '${messageToSend}'`,
162 (messagePayload
as JsonObject
)?.details
?? {}
164 if (messageType
=== MessageType
.CALL_MESSAGE
) {
165 // Reject it but keep the request in the cache
166 return reject(ocppError
);
168 return errorCallback(ocppError
, false);
171 return errorCallback(
173 ErrorType
.GENERIC_ERROR
,
174 `WebSocket closed for non buffered message id '${messageId}' with content '${messageToSend}'`,
176 (messagePayload
as JsonObject
)?.details
?? {}
182 if (messageType
!== MessageType
.CALL_MESSAGE
) {
184 return resolve(messagePayload
);
188 * Function that will receive the request's response
191 * @param requestPayload
193 async function responseCallback(
195 requestPayload
: JsonType
197 if (chargingStation
.getEnableStatistics()) {
198 chargingStation
.performanceStatistics
.addRequestStatistic(
200 MessageType
.CALL_RESULT_MESSAGE
203 // Handle the request's response
205 await self.ocppResponseService
.responseHandler(
207 commandName
as RequestCommand
,
215 chargingStation
.requests
.delete(messageId
);
220 * Function that will receive the request's error response
223 * @param requestStatistic
225 function errorCallback(error
: OCPPError
, requestStatistic
= true): void {
226 if (requestStatistic
&& chargingStation
.getEnableStatistics()) {
227 chargingStation
.performanceStatistics
.addRequestStatistic(
229 MessageType
.CALL_ERROR_MESSAGE
233 `${chargingStation.logPrefix()} Error %j occurred when calling command %s with message data %j`,
238 chargingStation
.requests
.delete(messageId
);
242 Constants
.OCPP_WEBSOCKET_TIMEOUT
,
244 ErrorType
.GENERIC_ERROR
,
245 `Timeout for message id '${messageId}'`,
247 (messagePayload
as JsonObject
)?.details
?? {}
250 messageType
=== MessageType
.CALL_MESSAGE
&& chargingStation
.requests
.delete(messageId
);
255 ErrorType
.SECURITY_ERROR
,
256 `Cannot send command ${commandName} payload when the charging station is in ${chargingStation.getRegistrationStatus()} state on the central server`,
261 private buildMessageToSend(
262 chargingStation
: ChargingStation
,
264 messagePayload
: JsonType
| OCPPError
,
265 messageType
: MessageType
,
266 commandName
?: RequestCommand
| IncomingRequestCommand
,
267 responseCallback
?: (payload
: JsonType
, requestPayload
: JsonType
) => Promise
<void>,
268 errorCallback
?: (error
: OCPPError
, requestStatistic
?: boolean) => void
270 let messageToSend
: string;
272 switch (messageType
) {
274 case MessageType
.CALL_MESSAGE
:
276 chargingStation
.requests
.set(messageId
, [
280 messagePayload
as JsonType
,
282 messageToSend
= JSON
.stringify([
287 ] as OutgoingRequest
);
290 case MessageType
.CALL_RESULT_MESSAGE
:
292 messageToSend
= JSON
.stringify([messageType
, messageId
, messagePayload
] as Response
);
295 case MessageType
.CALL_ERROR_MESSAGE
:
296 // Build Error Message
297 messageToSend
= JSON
.stringify([
300 (messagePayload
as OCPPError
)?.code
?? ErrorType
.GENERIC_ERROR
,
301 (messagePayload
as OCPPError
)?.message
?? '',
302 (messagePayload
as OCPPError
)?.details
?? { commandName
},
306 return messageToSend
;
309 private getMessageTypeString(messageType
: MessageType
): string {
310 switch (messageType
) {
311 case MessageType
.CALL_MESSAGE
:
313 case MessageType
.CALL_RESULT_MESSAGE
:
315 case MessageType
.CALL_ERROR_MESSAGE
:
320 private handleRequestError(
321 chargingStation
: ChargingStation
,
322 commandName
: RequestCommand
| IncomingRequestCommand
,
324 params
: HandleErrorParams
<EmptyObject
> = { throwError
: true }
326 logger
.error(chargingStation
.logPrefix() + ' Request command %s error: %j', commandName
, error
);
327 if (params
?.throwError
) {
332 // eslint-disable-next-line @typescript-eslint/no-unused-vars
333 public abstract requestHandler
<Request
extends JsonType
, Response
extends JsonType
>(
334 chargingStation
: ChargingStation
,
335 commandName
: RequestCommand
,
336 commandParams
?: JsonType
,
337 params
?: RequestParams
338 ): Promise
<Response
>;