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
<
38 protected readonly chargingStation
: ChargingStation
;
39 private readonly ocppResponseService
: OCPPResponseService
;
41 protected constructor(
42 chargingStation
: ChargingStation
,
43 ocppResponseService
: OCPPResponseService
45 this.chargingStation
= chargingStation
;
46 this.ocppResponseService
= ocppResponseService
;
49 public static getInstance
<T
extends OCPPRequestService
>(
50 this: new (chargingStation
: ChargingStation
, ocppResponseService
: OCPPResponseService
) => T
,
51 chargingStation
: ChargingStation
,
52 ocppResponseService
: OCPPResponseService
54 if (!OCPPRequestService
.instances
.has(chargingStation
.id
)) {
55 OCPPRequestService
.instances
.set(
57 new this(chargingStation
, ocppResponseService
)
60 return OCPPRequestService
.instances
.get(chargingStation
.id
) as T
;
63 public async sendResult(
65 messagePayload
: JsonType
,
66 commandName
: IncomingRequestCommand
67 ): Promise
<ResponseType
> {
69 // Send result message
70 return await this.internalSendMessage(
73 MessageType
.CALL_RESULT_MESSAGE
,
77 this.handleRequestError(commandName
, error
as Error);
81 public async sendError(
84 commandName
: IncomingRequestCommand
85 ): Promise
<ResponseType
> {
88 return await this.internalSendMessage(
91 MessageType
.CALL_ERROR_MESSAGE
,
95 this.handleRequestError(commandName
, error
as Error);
99 protected async sendMessage(
101 messagePayload
: JsonType
,
102 commandName
: RequestCommand
,
103 params
: SendParams
= {
104 skipBufferingOnError
: false,
105 triggerMessage
: false,
107 ): Promise
<ResponseType
> {
109 return await this.internalSendMessage(
112 MessageType
.CALL_MESSAGE
,
117 this.handleRequestError(commandName
, error
as Error, { throwError
: false });
121 private async internalSendMessage(
123 messagePayload
: JsonType
| OCPPError
,
124 messageType
: MessageType
,
125 commandName
?: RequestCommand
| IncomingRequestCommand
,
126 params
: SendParams
= {
127 skipBufferingOnError
: false,
128 triggerMessage
: false,
130 ): Promise
<ResponseType
> {
132 (this.chargingStation
.isInUnknownState() &&
133 commandName
=== RequestCommand
.BOOT_NOTIFICATION
) ||
134 (!this.chargingStation
.getOcppStrictCompliance() &&
135 this.chargingStation
.isInUnknownState()) ||
136 this.chargingStation
.isInAcceptedState() ||
137 (this.chargingStation
.isInPendingState() && params
.triggerMessage
)
139 // eslint-disable-next-line @typescript-eslint/no-this-alias
141 // Send a message through wsConnection
142 return Utils
.promiseWithTimeout(
143 new Promise((resolve
, reject
) => {
144 const messageToSend
= this.buildMessageToSend(
152 if (this.chargingStation
.getEnableStatistics()) {
153 this.chargingStation
.performanceStatistics
.addRequestStatistic(
158 // Check if wsConnection opened
159 if (this.chargingStation
.isWebSocketConnectionOpened()) {
161 const beginId
= PerformanceStatistics
.beginMeasure(commandName
);
162 // FIXME: Handle sending error
163 this.chargingStation
.wsConnection
.send(messageToSend
);
164 PerformanceStatistics
.endMeasure(commandName
, beginId
);
165 } else if (!params
.skipBufferingOnError
) {
167 this.chargingStation
.bufferMessage(messageToSend
);
168 const ocppError
= new OCPPError(
169 ErrorType
.GENERIC_ERROR
,
170 `WebSocket closed for buffered message id '${messageId}' with content '${messageToSend}'`,
172 (messagePayload
?.details
as JsonType
) ?? {}
174 if (messageType
=== MessageType
.CALL_MESSAGE
) {
175 // Reject it but keep the request in the cache
176 return reject(ocppError
);
178 return rejectCallback(ocppError
, false);
181 return rejectCallback(
183 ErrorType
.GENERIC_ERROR
,
184 `WebSocket closed for non buffered message id '${messageId}' with content '${messageToSend}'`,
186 (messagePayload
?.details
as JsonType
) ?? {}
192 if (messageType
!== MessageType
.CALL_MESSAGE
) {
194 return resolve(messagePayload
);
198 * Function that will receive the request's response
201 * @param requestPayload
203 async function responseCallback(
204 payload
: JsonType
| string,
205 requestPayload
: JsonType
207 if (self.chargingStation
.getEnableStatistics()) {
208 self.chargingStation
.performanceStatistics
.addRequestStatistic(
210 MessageType
.CALL_RESULT_MESSAGE
213 // Handle the request's response
215 await self.ocppResponseService
.handleResponse(
216 commandName
as RequestCommand
,
225 self.chargingStation
.requests
.delete(messageId
);
230 * Function that will receive the request's error response
233 * @param requestStatistic
235 function rejectCallback(error
: OCPPError
, requestStatistic
= true): void {
236 if (requestStatistic
&& self.chargingStation
.getEnableStatistics()) {
237 self.chargingStation
.performanceStatistics
.addRequestStatistic(
239 MessageType
.CALL_ERROR_MESSAGE
243 `${self.chargingStation.logPrefix()} Error %j occurred when calling command %s with message data %j`,
248 self.chargingStation
.requests
.delete(messageId
);
252 Constants
.OCPP_WEBSOCKET_TIMEOUT
,
254 ErrorType
.GENERIC_ERROR
,
255 `Timeout for message id '${messageId}'`,
257 (messagePayload
?.details
as JsonType
) ?? {}
260 messageType
=== MessageType
.CALL_MESSAGE
&&
261 this.chargingStation
.requests
.delete(messageId
);
266 ErrorType
.SECURITY_ERROR
,
267 `Cannot send command ${commandName} payload when the charging station is in ${this.chargingStation.getRegistrationStatus()} state on the central server`,
272 private buildMessageToSend(
274 messagePayload
: JsonType
| OCPPError
,
275 messageType
: MessageType
,
276 commandName
?: RequestCommand
| IncomingRequestCommand
,
277 responseCallback
?: (payload
: JsonType
| string, requestPayload
: JsonType
) => Promise
<void>,
278 rejectCallback
?: (error
: OCPPError
, requestStatistic
?: boolean) => void
280 let messageToSend
: string;
282 switch (messageType
) {
284 case MessageType
.CALL_MESSAGE
:
286 this.chargingStation
.requests
.set(messageId
, [
292 messageToSend
= JSON
.stringify([messageType
, messageId
, commandName
, messagePayload
]);
295 case MessageType
.CALL_RESULT_MESSAGE
:
297 messageToSend
= JSON
.stringify([messageType
, messageId
, messagePayload
]);
300 case MessageType
.CALL_ERROR_MESSAGE
:
301 // Build Error Message
302 messageToSend
= JSON
.stringify([
305 messagePayload
?.code
?? ErrorType
.GENERIC_ERROR
,
306 messagePayload
?.message
?? '',
307 messagePayload
?.details
?? { commandName
},
311 return messageToSend
;
314 private handleRequestError(
315 commandName
: RequestCommand
| IncomingRequestCommand
,
317 params
: HandleErrorParams
<EmptyObject
> = { throwError
: true }
320 this.chargingStation
.logPrefix() + ' Request command %s error: %j',
324 if (params
?.throwError
) {
329 public abstract sendHeartbeat(params
?: SendParams
): Promise
<void>;
330 public abstract sendBootNotification(
331 chargePointModel
: string,
332 chargePointVendor
: string,
333 chargeBoxSerialNumber
?: string,
334 firmwareVersion
?: string,
335 chargePointSerialNumber
?: string,
338 meterSerialNumber
?: string,
341 ): Promise
<BootNotificationResponse
>;
343 public abstract sendStatusNotification(
345 status: ChargePointStatus
,
346 errorCode
?: ChargePointErrorCode
349 public abstract sendAuthorize(connectorId
: number, idTag
?: string): Promise
<AuthorizeResponse
>;
350 public abstract sendStartTransaction(
353 ): Promise
<StartTransactionResponse
>;
355 public abstract sendStopTransaction(
356 transactionId
: number,
359 reason
?: StopTransactionReason
360 ): Promise
<StopTransactionResponse
>;
362 public abstract sendMeterValues(
364 transactionId
: number,
368 public abstract sendTransactionBeginMeterValues(
370 transactionId
: number,
371 beginMeterValue
: MeterValue
374 public abstract sendTransactionEndMeterValues(
376 transactionId
: number,
377 endMeterValue
: MeterValue
380 public abstract sendDiagnosticsStatusNotification(
381 diagnosticsStatus
: DiagnosticsStatus