1 import { AuthorizeResponse
, StartTransactionResponse
, StopTransactionReason
, StopTransactionResponse
} from
'../../types/ocpp/Transaction';
2 import { DiagnosticsStatus
, IncomingRequestCommand
, RequestCommand
, SendParams
} from
'../../types/ocpp/Requests';
4 import { BootNotificationResponse
} from
'../../types/ocpp/Responses';
5 import { ChargePointErrorCode
} from
'../../types/ocpp/ChargePointErrorCode';
6 import { ChargePointStatus
} from
'../../types/ocpp/ChargePointStatus';
7 import ChargingStation from
'../ChargingStation';
8 import Constants from
'../../utils/Constants';
9 import { ErrorType
} from
'../../types/ocpp/ErrorType';
10 import { JsonType
} from
'../../types/JsonType';
11 import { MessageType
} from
'../../types/ocpp/MessageType';
12 import { MeterValue
} from
'../../types/ocpp/MeterValues';
13 import OCPPError from
'../../exception/OCPPError';
14 import OCPPResponseService from
'./OCPPResponseService';
15 import PerformanceStatistics from
'../../performance/PerformanceStatistics';
16 import Utils from
'../../utils/Utils';
17 import logger from
'../../utils/Logger';
19 export default abstract class OCPPRequestService
{
20 public chargingStation
: ChargingStation
;
21 protected ocppResponseService
: OCPPResponseService
;
23 constructor(chargingStation
: ChargingStation
, ocppResponseService
: OCPPResponseService
) {
24 this.chargingStation
= chargingStation
;
25 this.ocppResponseService
= ocppResponseService
;
28 public async sendMessage(messageId
: string, messageData
: JsonType
| OCPPError
, messageType
: MessageType
, commandName
: RequestCommand
| IncomingRequestCommand
,
29 params
: SendParams
= {
30 skipBufferingOnError
: false,
32 }): Promise
<JsonType
| OCPPError
| string> {
33 if (this.chargingStation
.isInRejectedState() || (this.chargingStation
.isInPendingState() && !params
.triggerMessage
)) {
34 throw new OCPPError(ErrorType
.SECURITY_ERROR
, 'Cannot send command payload if the charging station is not in accepted state', commandName
);
35 // FIXME: Add template tunable for accepting incoming ChangeConfiguration request while in unknown state
36 } else if ((this.chargingStation
.isInUnknownState() && (commandName
=== RequestCommand
.BOOT_NOTIFICATION
|| commandName
=== IncomingRequestCommand
.CHANGE_CONFIGURATION
))
37 || this.chargingStation
.isInAcceptedState() || (this.chargingStation
.isInPendingState() && params
.triggerMessage
)) {
38 // eslint-disable-next-line @typescript-eslint/no-this-alias
40 // Send a message through wsConnection
41 return Utils
.promiseWithTimeout(new Promise((resolve
, reject
) => {
42 const messageToSend
= this.buildMessageToSend(messageId
, messageData
, messageType
, commandName
, responseCallback
, rejectCallback
);
43 if (this.chargingStation
.getEnableStatistics()) {
44 this.chargingStation
.performanceStatistics
.addRequestStatistic(commandName
, messageType
);
46 // Check if wsConnection opened
47 if (this.chargingStation
.isWebSocketConnectionOpened()) {
49 const beginId
= PerformanceStatistics
.beginMeasure(commandName
);
50 // FIXME: Handle sending error
51 this.chargingStation
.wsConnection
.send(messageToSend
);
52 PerformanceStatistics
.endMeasure(commandName
, beginId
);
53 } else if (!params
.skipBufferingOnError
) {
55 this.chargingStation
.bufferMessage(messageToSend
);
56 const ocppError
= new OCPPError(ErrorType
.GENERIC_ERROR
, `WebSocket closed for buffered message id '${messageId}' with content '${messageToSend}'`, commandName
, messageData
?.details
as JsonType
?? {});
57 if (messageType
=== MessageType
.CALL_MESSAGE
) {
58 // Reject it but keep the request in the cache
59 return reject(ocppError
);
61 return rejectCallback(ocppError
, false);
64 return rejectCallback(new OCPPError(ErrorType
.GENERIC_ERROR
, `WebSocket closed for non buffered message id '${messageId}' with content '${messageToSend}'`, commandName
, messageData
?.details
as JsonType
?? {}), false);
67 if (messageType
!== MessageType
.CALL_MESSAGE
) {
69 return resolve(messageData
);
73 * Function that will receive the request's response
76 * @param requestPayload
78 async function responseCallback(payload
: JsonType
| string, requestPayload
: JsonType
): Promise
<void> {
79 if (self.chargingStation
.getEnableStatistics()) {
80 self.chargingStation
.performanceStatistics
.addRequestStatistic(commandName
, MessageType
.CALL_RESULT_MESSAGE
);
82 // Handle the request's response
84 await self.ocppResponseService
.handleResponse(commandName
as RequestCommand
, payload
, requestPayload
);
90 self.chargingStation
.requests
.delete(messageId
);
95 * Function that will receive the request's error response
98 * @param requestStatistic
100 function rejectCallback(error
: OCPPError
, requestStatistic
= true): void {
101 if (requestStatistic
&& self.chargingStation
.getEnableStatistics()) {
102 self.chargingStation
.performanceStatistics
.addRequestStatistic(commandName
, MessageType
.CALL_ERROR_MESSAGE
);
104 logger
.error(`${self.chargingStation.logPrefix()} Error %j occurred when calling command %s with message data %j`, error
, commandName
, messageData
);
105 self.chargingStation
.requests
.delete(messageId
);
108 }), Constants
.OCPP_WEBSOCKET_TIMEOUT
, new OCPPError(ErrorType
.GENERIC_ERROR
, `Timeout for message id '${messageId}'`, commandName
, messageData
?.details
as JsonType
?? {}), () => {
109 messageType
=== MessageType
.CALL_MESSAGE
&& this.chargingStation
.requests
.delete(messageId
);
112 throw new OCPPError(ErrorType
.SECURITY_ERROR
, 'Cannot send command payload if the charging station is in unknown state', commandName
);
116 protected handleRequestError(commandName
: RequestCommand
, error
: Error): void {
117 logger
.error(this.chargingStation
.logPrefix() + ' Request command ' + commandName
+ ' error: %j', error
);
121 private buildMessageToSend(messageId
: string, messageData
: JsonType
| OCPPError
, messageType
: MessageType
, commandName
: RequestCommand
| IncomingRequestCommand
,
122 responseCallback
: (payload
: JsonType
| string, requestPayload
: JsonType
) => Promise
<void>,
123 rejectCallback
: (error
: OCPPError
, requestStatistic
?: boolean) => void): string {
124 let messageToSend
: string;
126 switch (messageType
) {
128 case MessageType
.CALL_MESSAGE
:
130 this.chargingStation
.requests
.set(messageId
, [responseCallback
, rejectCallback
, commandName
, messageData
]);
131 messageToSend
= JSON
.stringify([messageType
, messageId
, commandName
, messageData
]);
134 case MessageType
.CALL_RESULT_MESSAGE
:
136 messageToSend
= JSON
.stringify([messageType
, messageId
, messageData
]);
139 case MessageType
.CALL_ERROR_MESSAGE
:
140 // Build Error Message
141 messageToSend
= JSON
.stringify([messageType
, messageId
, messageData
?.code
?? ErrorType
.GENERIC_ERROR
, messageData
?.message
?? '', messageData
?.details
?? {}]);
144 return messageToSend
;
147 public abstract sendHeartbeat(params
?: SendParams
): Promise
<void>;
148 public abstract sendBootNotification(chargePointModel
: string, chargePointVendor
: string, chargeBoxSerialNumber
?: string, firmwareVersion
?: string, chargePointSerialNumber
?: string, iccid
?: string, imsi
?: string, meterSerialNumber
?: string, meterType
?: string, params
?: SendParams
): Promise
<BootNotificationResponse
>;
149 public abstract sendStatusNotification(connectorId
: number, status: ChargePointStatus
, errorCode
?: ChargePointErrorCode
): Promise
<void>;
150 public abstract sendAuthorize(connectorId
: number, idTag
?: string): Promise
<AuthorizeResponse
>;
151 public abstract sendStartTransaction(connectorId
: number, idTag
?: string): Promise
<StartTransactionResponse
>;
152 public abstract sendStopTransaction(transactionId
: number, meterStop
: number, idTag
?: string, reason
?: StopTransactionReason
): Promise
<StopTransactionResponse
>;
153 public abstract sendMeterValues(connectorId
: number, transactionId
: number, interval
: number): Promise
<void>;
154 public abstract sendTransactionBeginMeterValues(connectorId
: number, transactionId
: number, beginMeterValue
: MeterValue
): Promise
<void>;
155 public abstract sendTransactionEndMeterValues(connectorId
: number, transactionId
: number, endMeterValue
: MeterValue
): Promise
<void>;
156 public abstract sendDiagnosticsStatusNotification(diagnosticsStatus
: DiagnosticsStatus
): Promise
<void>;
157 public abstract sendResult(messageId
: string, resultMessageData
: JsonType
, commandName
: RequestCommand
| IncomingRequestCommand
): Promise
<JsonType
>;
158 public abstract sendError(messageId
: string, error
: OCPPError
, commandName
: RequestCommand
| IncomingRequestCommand
): Promise
<JsonType
>;