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