Commit | Line | Data |
---|---|---|
c0560973 | 1 | import { AuthorizeResponse, StartTransactionResponse, StopTransactionReason, StopTransactionResponse } from '../../types/ocpp/Transaction'; |
caad9d6b | 2 | import { DiagnosticsStatus, IncomingRequestCommand, RequestCommand, SendParams } from '../../types/ocpp/Requests'; |
c0560973 | 3 | |
efa43e52 | 4 | import { BootNotificationResponse } from '../../types/ocpp/Responses'; |
c0560973 JB |
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'; | |
d1888640 | 10 | import { JsonType } from '../../types/JsonType'; |
c0560973 | 11 | import { MessageType } from '../../types/ocpp/MessageType'; |
fd0c36fa | 12 | import { MeterValue } from '../../types/ocpp/MeterValues'; |
e58068fd | 13 | import OCPPError from '../../exception/OCPPError'; |
c0560973 | 14 | import OCPPResponseService from './OCPPResponseService'; |
a6b3c6c3 | 15 | import PerformanceStatistics from '../../performance/PerformanceStatistics'; |
6d9abcc2 | 16 | import Utils from '../../utils/Utils'; |
c0560973 JB |
17 | import logger from '../../utils/Logger'; |
18 | ||
19 | export default abstract class OCPPRequestService { | |
20 | public chargingStation: ChargingStation; | |
21 | protected ocppResponseService: OCPPResponseService; | |
22 | ||
23 | constructor(chargingStation: ChargingStation, ocppResponseService: OCPPResponseService) { | |
24 | this.chargingStation = chargingStation; | |
25 | this.ocppResponseService = ocppResponseService; | |
26 | } | |
27 | ||
d1888640 | 28 | public async sendMessage(messageId: string, messageData: JsonType | OCPPError, messageType: MessageType, commandName: RequestCommand | IncomingRequestCommand, |
caad9d6b JB |
29 | params: SendParams = { |
30 | skipBufferingOnError: false, | |
31 | triggerMessage: false | |
d1888640 | 32 | }): Promise<JsonType | OCPPError | string> { |
623bbc14 | 33 | if (this.chargingStation.isInRejectedState() || (this.chargingStation.isInPendingState() && !params.triggerMessage)) { |
caad9d6b | 34 | throw new OCPPError(ErrorType.SECURITY_ERROR, 'Cannot send command payload if the charging station is not in accepted state', commandName); |
73c4266d JB |
35 | } else if ((this.chargingStation.isInUnknownState() && commandName === RequestCommand.BOOT_NOTIFICATION) |
36 | || this.chargingStation.isInAcceptedState() || (this.chargingStation.isInPendingState() && params.triggerMessage)) { | |
caad9d6b JB |
37 | // eslint-disable-next-line @typescript-eslint/no-this-alias |
38 | const self = this; | |
39 | // Send a message through wsConnection | |
40 | return Utils.promiseWithTimeout(new Promise((resolve, reject) => { | |
41 | const messageToSend = this.buildMessageToSend(messageId, messageData, messageType, commandName, responseCallback, rejectCallback); | |
42 | if (this.chargingStation.getEnableStatistics()) { | |
43 | this.chargingStation.performanceStatistics.addRequestStatistic(commandName, messageType); | |
6198eef3 | 44 | } |
caad9d6b JB |
45 | // Check if wsConnection opened |
46 | if (this.chargingStation.isWebSocketConnectionOpened()) { | |
47 | // Yes: Send Message | |
48 | const beginId = PerformanceStatistics.beginMeasure(commandName); | |
49 | // FIXME: Handle sending error | |
50 | this.chargingStation.wsConnection.send(messageToSend); | |
51 | PerformanceStatistics.endMeasure(commandName, beginId); | |
52 | } else if (!params.skipBufferingOnError) { | |
53 | // Buffer it | |
54 | this.chargingStation.bufferMessage(messageToSend); | |
d1888640 | 55 | const ocppError = new OCPPError(ErrorType.GENERIC_ERROR, `WebSocket closed for buffered message id '${messageId}' with content '${messageToSend}'`, commandName, messageData?.details as JsonType ?? {}); |
caad9d6b JB |
56 | if (messageType === MessageType.CALL_MESSAGE) { |
57 | // Reject it but keep the request in the cache | |
58 | return reject(ocppError); | |
59 | } | |
60 | return rejectCallback(ocppError, false); | |
61 | } else { | |
62 | // Reject it | |
d1888640 | 63 | return rejectCallback(new OCPPError(ErrorType.GENERIC_ERROR, `WebSocket closed for non buffered message id '${messageId}' with content '${messageToSend}'`, commandName, messageData?.details as JsonType ?? {}), false); |
c0560973 | 64 | } |
caad9d6b JB |
65 | // Response? |
66 | if (messageType !== MessageType.CALL_MESSAGE) { | |
67 | // Yes: send Ok | |
68 | return resolve(messageData); | |
a4bc2942 | 69 | } |
c0560973 | 70 | |
caad9d6b JB |
71 | /** |
72 | * Function that will receive the request's response | |
73 | * | |
74 | * @param payload | |
75 | * @param requestPayload | |
76 | */ | |
d1888640 | 77 | async function responseCallback(payload: JsonType | string, requestPayload: JsonType): Promise<void> { |
caad9d6b JB |
78 | if (self.chargingStation.getEnableStatistics()) { |
79 | self.chargingStation.performanceStatistics.addRequestStatistic(commandName, MessageType.CALL_RESULT_MESSAGE); | |
80 | } | |
81 | // Handle the request's response | |
82 | try { | |
83 | await self.ocppResponseService.handleResponse(commandName as RequestCommand, payload, requestPayload); | |
84 | resolve(payload); | |
85 | } catch (error) { | |
86 | reject(error); | |
87 | throw error; | |
88 | } finally { | |
89 | self.chargingStation.requests.delete(messageId); | |
90 | } | |
c0560973 | 91 | } |
caad9d6b JB |
92 | |
93 | /** | |
94 | * Function that will receive the request's error response | |
95 | * | |
96 | * @param error | |
97 | * @param requestStatistic | |
98 | */ | |
99 | function rejectCallback(error: OCPPError, requestStatistic = true): void { | |
100 | if (requestStatistic && self.chargingStation.getEnableStatistics()) { | |
101 | self.chargingStation.performanceStatistics.addRequestStatistic(commandName, MessageType.CALL_ERROR_MESSAGE); | |
102 | } | |
103 | logger.error(`${self.chargingStation.logPrefix()} Error %j occurred when calling command %s with message data %j`, error, commandName, messageData); | |
104 | self.chargingStation.requests.delete(messageId); | |
105 | reject(error); | |
106 | } | |
d1888640 | 107 | }), Constants.OCPP_WEBSOCKET_TIMEOUT, new OCPPError(ErrorType.GENERIC_ERROR, `Timeout for message id '${messageId}'`, commandName, messageData?.details as JsonType ?? {}), () => { |
caad9d6b JB |
108 | messageType === MessageType.CALL_MESSAGE && this.chargingStation.requests.delete(messageId); |
109 | }); | |
110 | } else { | |
5f496df6 | 111 | throw new OCPPError(ErrorType.SECURITY_ERROR, 'Cannot send command payload if the charging station is in unknown state', commandName); |
caad9d6b | 112 | } |
c0560973 JB |
113 | } |
114 | ||
c7f95d16 | 115 | protected handleRequestError(commandName: RequestCommand, error: Error): void { |
47e22477 | 116 | logger.error(this.chargingStation.logPrefix() + ' Request command ' + commandName + ' error: %j', error); |
c0560973 JB |
117 | throw error; |
118 | } | |
119 | ||
d1888640 JB |
120 | private buildMessageToSend(messageId: string, messageData: JsonType | OCPPError, messageType: MessageType, commandName: RequestCommand | IncomingRequestCommand, |
121 | responseCallback: (payload: JsonType | string, requestPayload: JsonType) => Promise<void>, | |
9239b49a | 122 | rejectCallback: (error: OCPPError, requestStatistic?: boolean) => void): string { |
e7accadb JB |
123 | let messageToSend: string; |
124 | // Type of message | |
125 | switch (messageType) { | |
126 | // Request | |
127 | case MessageType.CALL_MESSAGE: | |
128 | // Build request | |
de3dbcf5 JB |
129 | this.chargingStation.requests.set(messageId, [responseCallback, rejectCallback, commandName, messageData]); |
130 | messageToSend = JSON.stringify([messageType, messageId, commandName, messageData]); | |
e7accadb JB |
131 | break; |
132 | // Response | |
133 | case MessageType.CALL_RESULT_MESSAGE: | |
134 | // Build response | |
de3dbcf5 | 135 | messageToSend = JSON.stringify([messageType, messageId, messageData]); |
e7accadb JB |
136 | break; |
137 | // Error Message | |
138 | case MessageType.CALL_ERROR_MESSAGE: | |
139 | // Build Error Message | |
de3dbcf5 | 140 | messageToSend = JSON.stringify([messageType, messageId, messageData?.code ?? ErrorType.GENERIC_ERROR, messageData?.message ?? '', messageData?.details ?? {}]); |
e7accadb JB |
141 | break; |
142 | } | |
143 | return messageToSend; | |
144 | } | |
145 | ||
caad9d6b JB |
146 | public abstract sendHeartbeat(params?: SendParams): Promise<void>; |
147 | 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>; | |
c0560973 | 148 | public abstract sendStatusNotification(connectorId: number, status: ChargePointStatus, errorCode?: ChargePointErrorCode): Promise<void>; |
163547b1 | 149 | public abstract sendAuthorize(connectorId: number, idTag?: string): Promise<AuthorizeResponse>; |
c0560973 JB |
150 | public abstract sendStartTransaction(connectorId: number, idTag?: string): Promise<StartTransactionResponse>; |
151 | public abstract sendStopTransaction(transactionId: number, meterStop: number, idTag?: string, reason?: StopTransactionReason): Promise<StopTransactionResponse>; | |
aef1b33a | 152 | public abstract sendMeterValues(connectorId: number, transactionId: number, interval: number): Promise<void>; |
fd0c36fa JB |
153 | public abstract sendTransactionBeginMeterValues(connectorId: number, transactionId: number, beginMeterValue: MeterValue): Promise<void>; |
154 | public abstract sendTransactionEndMeterValues(connectorId: number, transactionId: number, endMeterValue: MeterValue): Promise<void>; | |
47e22477 | 155 | public abstract sendDiagnosticsStatusNotification(diagnosticsStatus: DiagnosticsStatus): Promise<void>; |
d1888640 JB |
156 | public abstract sendResult(messageId: string, resultMessageData: JsonType, commandName: RequestCommand | IncomingRequestCommand): Promise<JsonType>; |
157 | public abstract sendError(messageId: string, error: OCPPError, commandName: RequestCommand | IncomingRequestCommand): Promise<JsonType>; | |
c0560973 | 158 | } |