3 IncomingRequestCommand
,
7 } from
'../../types/ocpp/Requests';
9 import type ChargingStation from
'../ChargingStation';
10 import Constants from
'../../utils/Constants';
11 import { EmptyObject
} from
'../../types/EmptyObject';
12 import { ErrorType
} from
'../../types/ocpp/ErrorType';
13 import { HandleErrorParams
} from
'../../types/Error';
14 import { JsonType
} from
'../../types/JsonType';
15 import { MessageType
} from
'../../types/ocpp/MessageType';
16 import OCPPError from
'../../exception/OCPPError';
17 import type OCPPResponseService from
'./OCPPResponseService';
18 import PerformanceStatistics from
'../../performance/PerformanceStatistics';
19 import Utils from
'../../utils/Utils';
20 import logger from
'../../utils/Logger';
22 export default abstract class OCPPRequestService
{
23 private static readonly instances
: Map
<string, OCPPRequestService
> = new Map
<
28 protected readonly chargingStation
: ChargingStation
;
29 private readonly ocppResponseService
: OCPPResponseService
;
31 protected constructor(
32 chargingStation
: ChargingStation
,
33 ocppResponseService
: OCPPResponseService
35 this.chargingStation
= chargingStation
;
36 this.ocppResponseService
= ocppResponseService
;
37 this.sendMessageHandler
.bind(this);
40 public static getInstance
<T
extends OCPPRequestService
>(
41 this: new (chargingStation
: ChargingStation
, ocppResponseService
: OCPPResponseService
) => T
,
42 chargingStation
: ChargingStation
,
43 ocppResponseService
: OCPPResponseService
45 if (!OCPPRequestService
.instances
.has(chargingStation
.id
)) {
46 OCPPRequestService
.instances
.set(
48 new this(chargingStation
, ocppResponseService
)
51 return OCPPRequestService
.instances
.get(chargingStation
.id
) as T
;
54 public async sendResult(
56 messagePayload
: JsonType
,
57 commandName
: IncomingRequestCommand
58 ): Promise
<ResponseType
> {
60 // Send result message
61 return await this.internalSendMessage(
64 MessageType
.CALL_RESULT_MESSAGE
,
68 this.handleRequestError(commandName
, error
as Error);
72 public async sendError(
75 commandName
: IncomingRequestCommand
76 ): Promise
<ResponseType
> {
79 return await this.internalSendMessage(
82 MessageType
.CALL_ERROR_MESSAGE
,
86 this.handleRequestError(commandName
, error
as Error);
90 protected async sendMessage(
92 messagePayload
: JsonType
,
93 commandName
: RequestCommand
,
94 params
: SendParams
= {
95 skipBufferingOnError
: false,
96 triggerMessage
: false,
98 ): Promise
<ResponseType
> {
100 return await this.internalSendMessage(
103 MessageType
.CALL_MESSAGE
,
108 this.handleRequestError(commandName
, error
as Error, { throwError
: false });
112 private async internalSendMessage(
114 messagePayload
: JsonType
| OCPPError
,
115 messageType
: MessageType
,
116 commandName
?: RequestCommand
| IncomingRequestCommand
,
117 params
: SendParams
= {
118 skipBufferingOnError
: false,
119 triggerMessage
: false,
121 ): Promise
<ResponseType
> {
123 (this.chargingStation
.isInUnknownState() &&
124 commandName
=== RequestCommand
.BOOT_NOTIFICATION
) ||
125 (!this.chargingStation
.getOcppStrictCompliance() &&
126 this.chargingStation
.isInUnknownState()) ||
127 this.chargingStation
.isInAcceptedState() ||
128 (this.chargingStation
.isInPendingState() && params
.triggerMessage
)
130 // eslint-disable-next-line @typescript-eslint/no-this-alias
132 // Send a message through wsConnection
133 return Utils
.promiseWithTimeout(
134 new Promise((resolve
, reject
) => {
135 const messageToSend
= this.buildMessageToSend(
143 if (this.chargingStation
.getEnableStatistics()) {
144 this.chargingStation
.performanceStatistics
.addRequestStatistic(
149 // Check if wsConnection opened
150 if (this.chargingStation
.isWebSocketConnectionOpened()) {
152 const beginId
= PerformanceStatistics
.beginMeasure(commandName
);
153 // FIXME: Handle sending error
154 this.chargingStation
.wsConnection
.send(messageToSend
);
155 PerformanceStatistics
.endMeasure(commandName
, beginId
);
156 } else if (!params
.skipBufferingOnError
) {
158 this.chargingStation
.bufferMessage(messageToSend
);
159 const ocppError
= new OCPPError(
160 ErrorType
.GENERIC_ERROR
,
161 `WebSocket closed for buffered message id '${messageId}' with content '${messageToSend}'`,
163 (messagePayload
?.details
as JsonType
) ?? {}
165 if (messageType
=== MessageType
.CALL_MESSAGE
) {
166 // Reject it but keep the request in the cache
167 return reject(ocppError
);
169 return rejectCallback(ocppError
, false);
172 return rejectCallback(
174 ErrorType
.GENERIC_ERROR
,
175 `WebSocket closed for non buffered message id '${messageId}' with content '${messageToSend}'`,
177 (messagePayload
?.details
as JsonType
) ?? {}
183 if (messageType
!== MessageType
.CALL_MESSAGE
) {
185 return resolve(messagePayload
);
189 * Function that will receive the request's response
192 * @param requestPayload
194 async function responseCallback(
195 payload
: JsonType
| string,
196 requestPayload
: JsonType
198 if (self.chargingStation
.getEnableStatistics()) {
199 self.chargingStation
.performanceStatistics
.addRequestStatistic(
201 MessageType
.CALL_RESULT_MESSAGE
204 // Handle the request's response
206 await self.ocppResponseService
.handleResponse(
207 commandName
as RequestCommand
,
216 self.chargingStation
.requests
.delete(messageId
);
221 * Function that will receive the request's error response
224 * @param requestStatistic
226 function rejectCallback(error
: OCPPError
, requestStatistic
= true): void {
227 if (requestStatistic
&& self.chargingStation
.getEnableStatistics()) {
228 self.chargingStation
.performanceStatistics
.addRequestStatistic(
230 MessageType
.CALL_ERROR_MESSAGE
234 `${self.chargingStation.logPrefix()} Error %j occurred when calling command %s with message data %j`,
239 self.chargingStation
.requests
.delete(messageId
);
243 Constants
.OCPP_WEBSOCKET_TIMEOUT
,
245 ErrorType
.GENERIC_ERROR
,
246 `Timeout for message id '${messageId}'`,
248 (messagePayload
?.details
as JsonType
) ?? {}
251 messageType
=== MessageType
.CALL_MESSAGE
&&
252 this.chargingStation
.requests
.delete(messageId
);
257 ErrorType
.SECURITY_ERROR
,
258 `Cannot send command ${commandName} payload when the charging station is in ${this.chargingStation.getRegistrationStatus()} state on the central server`,
263 private buildMessageToSend(
265 messagePayload
: JsonType
| OCPPError
,
266 messageType
: MessageType
,
267 commandName
?: RequestCommand
| IncomingRequestCommand
,
268 responseCallback
?: (payload
: JsonType
| string, requestPayload
: JsonType
) => Promise
<void>,
269 rejectCallback
?: (error
: OCPPError
, requestStatistic
?: boolean) => void
271 let messageToSend
: string;
273 switch (messageType
) {
275 case MessageType
.CALL_MESSAGE
:
277 this.chargingStation
.requests
.set(messageId
, [
283 messageToSend
= JSON
.stringify([messageType
, messageId
, commandName
, messagePayload
]);
286 case MessageType
.CALL_RESULT_MESSAGE
:
288 messageToSend
= JSON
.stringify([messageType
, messageId
, messagePayload
]);
291 case MessageType
.CALL_ERROR_MESSAGE
:
292 // Build Error Message
293 messageToSend
= JSON
.stringify([
296 messagePayload
?.code
?? ErrorType
.GENERIC_ERROR
,
297 messagePayload
?.message
?? '',
298 messagePayload
?.details
?? { commandName
},
302 return messageToSend
;
305 private handleRequestError(
306 commandName
: RequestCommand
| IncomingRequestCommand
,
308 params
: HandleErrorParams
<EmptyObject
> = { throwError
: true }
311 this.chargingStation
.logPrefix() + ' Request command %s error: %j',
315 if (params
?.throwError
) {
320 public abstract sendMessageHandler(
321 commandName
: RequestCommand
,
322 commandParams
?: JsonType
,
324 ): Promise
<ResponseType
>;