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