3 StartTransactionResponse
,
5 StopTransactionResponse
,
6 } from
'../../types/ocpp/Transaction';
9 IncomingRequestCommand
,
13 } from
'../../types/ocpp/Requests';
15 import { BootNotificationResponse
} from
'../../types/ocpp/Responses';
16 import { ChargePointErrorCode
} from
'../../types/ocpp/ChargePointErrorCode';
17 import { ChargePointStatus
} from
'../../types/ocpp/ChargePointStatus';
18 import type ChargingStation from
'../ChargingStation';
19 import Constants from
'../../utils/Constants';
20 import { EmptyObject
} from
'../../types/EmptyObject';
21 import { ErrorType
} from
'../../types/ocpp/ErrorType';
22 import { HandleErrorParams
} from
'../../types/Error';
23 import { JsonType
} from
'../../types/JsonType';
24 import { MessageType
} from
'../../types/ocpp/MessageType';
25 import { MeterValue
} from
'../../types/ocpp/MeterValues';
26 import OCPPError from
'../../exception/OCPPError';
27 import type OCPPResponseService from
'./OCPPResponseService';
28 import PerformanceStatistics from
'../../performance/PerformanceStatistics';
29 import Utils from
'../../utils/Utils';
30 import logger from
'../../utils/Logger';
32 export default abstract class OCPPRequestService
{
33 private static readonly instances
: Map
<string, OCPPRequestService
> = new Map
<
37 protected readonly chargingStation
: ChargingStation
;
38 private readonly ocppResponseService
: OCPPResponseService
;
40 protected constructor(
41 chargingStation
: ChargingStation
,
42 ocppResponseService
: OCPPResponseService
44 this.chargingStation
= chargingStation
;
45 this.ocppResponseService
= ocppResponseService
;
48 public static getInstance
<T
extends OCPPRequestService
>(
49 this: new (chargingStation
: ChargingStation
, ocppResponseService
: OCPPResponseService
) => T
,
50 chargingStation
: ChargingStation
,
51 ocppResponseService
: OCPPResponseService
53 if (!OCPPRequestService
.instances
.has(chargingStation
.id
)) {
54 OCPPRequestService
.instances
.set(
56 new this(chargingStation
, ocppResponseService
)
59 return OCPPRequestService
.instances
.get(chargingStation
.id
) as T
;
62 public async sendResult(
64 messagePayload
: JsonType
,
65 commandName
: IncomingRequestCommand
66 ): Promise
<ResponseType
> {
68 // Send result message
69 return await this.internalSendMessage(
72 MessageType
.CALL_RESULT_MESSAGE
,
76 this.handleRequestError(commandName
, error
as Error);
80 public async sendError(
83 commandName
: IncomingRequestCommand
84 ): Promise
<ResponseType
> {
87 return await this.internalSendMessage(
90 MessageType
.CALL_ERROR_MESSAGE
,
94 this.handleRequestError(commandName
, error
as Error);
98 protected async sendMessage(
100 messagePayload
: JsonType
,
101 commandName
: RequestCommand
,
102 params
: SendParams
= {
103 skipBufferingOnError
: false,
104 triggerMessage
: false,
106 ): Promise
<ResponseType
> {
108 return await this.internalSendMessage(
111 MessageType
.CALL_MESSAGE
,
116 this.handleRequestError(commandName
, error
as Error, { throwError
: false });
120 private async internalSendMessage(
122 messagePayload
: JsonType
| OCPPError
,
123 messageType
: MessageType
,
124 commandName
?: RequestCommand
| IncomingRequestCommand
,
125 params
: SendParams
= {
126 skipBufferingOnError
: false,
127 triggerMessage
: false,
129 ): Promise
<ResponseType
> {
131 (this.chargingStation
.isInUnknownState() &&
132 commandName
=== RequestCommand
.BOOT_NOTIFICATION
) ||
133 (!this.chargingStation
.getOcppStrictCompliance() &&
134 this.chargingStation
.isInUnknownState()) ||
135 this.chargingStation
.isInAcceptedState() ||
136 (this.chargingStation
.isInPendingState() && params
.triggerMessage
)
138 // eslint-disable-next-line @typescript-eslint/no-this-alias
140 // Send a message through wsConnection
141 return Utils
.promiseWithTimeout(
142 new Promise((resolve
, reject
) => {
143 const messageToSend
= this.buildMessageToSend(
151 if (this.chargingStation
.getEnableStatistics()) {
152 this.chargingStation
.performanceStatistics
.addRequestStatistic(
157 // Check if wsConnection opened
158 if (this.chargingStation
.isWebSocketConnectionOpened()) {
160 const beginId
= PerformanceStatistics
.beginMeasure(commandName
);
161 // FIXME: Handle sending error
162 this.chargingStation
.wsConnection
.send(messageToSend
);
163 PerformanceStatistics
.endMeasure(commandName
, beginId
);
164 } else if (!params
.skipBufferingOnError
) {
166 this.chargingStation
.bufferMessage(messageToSend
);
167 const ocppError
= new OCPPError(
168 ErrorType
.GENERIC_ERROR
,
169 `WebSocket closed for buffered message id '${messageId}' with content '${messageToSend}'`,
171 (messagePayload
?.details
as JsonType
) ?? {}
173 if (messageType
=== MessageType
.CALL_MESSAGE
) {
174 // Reject it but keep the request in the cache
175 return reject(ocppError
);
177 return rejectCallback(ocppError
, false);
180 return rejectCallback(
182 ErrorType
.GENERIC_ERROR
,
183 `WebSocket closed for non buffered message id '${messageId}' with content '${messageToSend}'`,
185 (messagePayload
?.details
as JsonType
) ?? {}
191 if (messageType
!== MessageType
.CALL_MESSAGE
) {
193 return resolve(messagePayload
);
197 * Function that will receive the request's response
200 * @param requestPayload
202 async function responseCallback(
203 payload
: JsonType
| string,
204 requestPayload
: JsonType
206 if (self.chargingStation
.getEnableStatistics()) {
207 self.chargingStation
.performanceStatistics
.addRequestStatistic(
209 MessageType
.CALL_RESULT_MESSAGE
212 // Handle the request's response
214 await self.ocppResponseService
.handleResponse(
215 commandName
as RequestCommand
,
224 self.chargingStation
.requests
.delete(messageId
);
229 * Function that will receive the request's error response
232 * @param requestStatistic
234 function rejectCallback(error
: OCPPError
, requestStatistic
= true): void {
235 if (requestStatistic
&& self.chargingStation
.getEnableStatistics()) {
236 self.chargingStation
.performanceStatistics
.addRequestStatistic(
238 MessageType
.CALL_ERROR_MESSAGE
242 `${self.chargingStation.logPrefix()} Error %j occurred when calling command %s with message data %j`,
247 self.chargingStation
.requests
.delete(messageId
);
251 Constants
.OCPP_WEBSOCKET_TIMEOUT
,
253 ErrorType
.GENERIC_ERROR
,
254 `Timeout for message id '${messageId}'`,
256 (messagePayload
?.details
as JsonType
) ?? {}
259 messageType
=== MessageType
.CALL_MESSAGE
&&
260 this.chargingStation
.requests
.delete(messageId
);
265 ErrorType
.SECURITY_ERROR
,
266 `Cannot send command ${commandName} payload when the charging station is in ${this.chargingStation.getRegistrationStatus()} state on the central server`,
271 private buildMessageToSend(
273 messagePayload
: JsonType
| OCPPError
,
274 messageType
: MessageType
,
275 commandName
?: RequestCommand
| IncomingRequestCommand
,
276 responseCallback
?: (payload
: JsonType
| string, requestPayload
: JsonType
) => Promise
<void>,
277 rejectCallback
?: (error
: OCPPError
, requestStatistic
?: boolean) => void
279 let messageToSend
: string;
281 switch (messageType
) {
283 case MessageType
.CALL_MESSAGE
:
285 this.chargingStation
.requests
.set(messageId
, [
291 messageToSend
= JSON
.stringify([messageType
, messageId
, commandName
, messagePayload
]);
294 case MessageType
.CALL_RESULT_MESSAGE
:
296 messageToSend
= JSON
.stringify([messageType
, messageId
, messagePayload
]);
299 case MessageType
.CALL_ERROR_MESSAGE
:
300 // Build Error Message
301 messageToSend
= JSON
.stringify([
304 messagePayload
?.code
?? ErrorType
.GENERIC_ERROR
,
305 messagePayload
?.message
?? '',
306 messagePayload
?.details
?? { commandName
},
310 return messageToSend
;
313 private handleRequestError(
314 commandName
: RequestCommand
| IncomingRequestCommand
,
316 params
: HandleErrorParams
<EmptyObject
> = { throwError
: true }
319 this.chargingStation
.logPrefix() + ' Request command %s error: %j',
323 if (params
?.throwError
) {
328 public abstract sendHeartbeat(params
?: SendParams
): Promise
<void>;
329 public abstract sendBootNotification(
330 chargePointModel
: string,
331 chargePointVendor
: string,
332 chargeBoxSerialNumber
?: string,
333 firmwareVersion
?: string,
334 chargePointSerialNumber
?: string,
337 meterSerialNumber
?: string,
340 ): Promise
<BootNotificationResponse
>;
341 public abstract sendStatusNotification(
343 status: ChargePointStatus
,
344 errorCode
?: ChargePointErrorCode
346 public abstract sendAuthorize(connectorId
: number, idTag
?: string): Promise
<AuthorizeResponse
>;
347 public abstract sendStartTransaction(
350 ): Promise
<StartTransactionResponse
>;
351 public abstract sendStopTransaction(
352 transactionId
: number,
355 reason
?: StopTransactionReason
356 ): Promise
<StopTransactionResponse
>;
357 public abstract sendMeterValues(
359 transactionId
: number,
362 public abstract sendTransactionBeginMeterValues(
364 transactionId
: number,
365 beginMeterValue
: MeterValue
367 public abstract sendTransactionEndMeterValues(
369 transactionId
: number,
370 endMeterValue
: MeterValue
372 public abstract sendDiagnosticsStatusNotification(
373 diagnosticsStatus
: DiagnosticsStatus