| 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 '../../utils/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): Promise<any> { |
| 27 | // eslint-disable-next-line @typescript-eslint/no-this-alias |
| 28 | const self = this; |
| 29 | // Send a message through wsConnection |
| 30 | return new Promise((resolve: (value?: any | PromiseLike<any>) => void, reject: (reason?: any) => void) => { |
| 31 | let messageToSend: string; |
| 32 | // Type of message |
| 33 | switch (messageType) { |
| 34 | // Request |
| 35 | case MessageType.CALL_MESSAGE: |
| 36 | // Build request |
| 37 | this.chargingStation.requests[messageId] = [responseCallback, rejectCallback, commandParams as Record<string, unknown>]; |
| 38 | messageToSend = JSON.stringify([messageType, messageId, commandName, commandParams]); |
| 39 | break; |
| 40 | // Response |
| 41 | case MessageType.CALL_RESULT_MESSAGE: |
| 42 | // Build response |
| 43 | messageToSend = JSON.stringify([messageType, messageId, commandParams]); |
| 44 | break; |
| 45 | // Error Message |
| 46 | case MessageType.CALL_ERROR_MESSAGE: |
| 47 | // Build Error Message |
| 48 | messageToSend = JSON.stringify([messageType, messageId, commandParams.code ? commandParams.code : ErrorType.GENERIC_ERROR, commandParams.message ? commandParams.message : '', commandParams.details ? commandParams.details : {}]); |
| 49 | break; |
| 50 | } |
| 51 | // Check if wsConnection opened and charging station registered |
| 52 | if (this.chargingStation.isWebSocketOpen() && (this.chargingStation.isRegistered() || commandName === RequestCommand.BOOT_NOTIFICATION)) { |
| 53 | if (this.chargingStation.getEnableStatistics()) { |
| 54 | this.chargingStation.performanceStatistics.addRequestStatistic(commandName, messageType); |
| 55 | } |
| 56 | // Yes: Send Message |
| 57 | const beginId = PerformanceStatistics.beginMeasure(commandName); |
| 58 | this.chargingStation.wsConnection.send(messageToSend); |
| 59 | PerformanceStatistics.endMeasure(commandName, beginId); |
| 60 | } else if (commandName !== RequestCommand.BOOT_NOTIFICATION) { |
| 61 | // Buffer it |
| 62 | this.chargingStation.addToMessageQueue(messageToSend); |
| 63 | // Reject it |
| 64 | return rejectCallback(new OCPPError(commandParams.code ? commandParams.code : ErrorType.GENERIC_ERROR, commandParams.message ? commandParams.message : `WebSocket closed for message id '${messageId}' with content '${messageToSend}', message buffered`, commandParams.details ? commandParams.details : {})); |
| 65 | } |
| 66 | // Response? |
| 67 | if (messageType === MessageType.CALL_RESULT_MESSAGE) { |
| 68 | // Yes: send Ok |
| 69 | resolve(); |
| 70 | } else if (messageType === MessageType.CALL_ERROR_MESSAGE) { |
| 71 | // Send timeout |
| 72 | setTimeout(() => rejectCallback(new OCPPError(commandParams.code ? commandParams.code : ErrorType.GENERIC_ERROR, commandParams.message ? commandParams.message : `Timeout for message id '${messageId}' with content '${messageToSend}'`, commandParams.details ? commandParams.details : {})), Constants.OCPP_ERROR_TIMEOUT); |
| 73 | } |
| 74 | |
| 75 | /** |
| 76 | * Function that will receive the request's response |
| 77 | * |
| 78 | * @param {Record<string, unknown> | string} payload |
| 79 | * @param {Record<string, unknown>} requestPayload |
| 80 | */ |
| 81 | async function responseCallback(payload: Record<string, unknown> | string, requestPayload: Record<string, unknown>): Promise<void> { |
| 82 | if (self.chargingStation.getEnableStatistics()) { |
| 83 | self.chargingStation.performanceStatistics.addRequestStatistic(commandName, MessageType.CALL_RESULT_MESSAGE); |
| 84 | } |
| 85 | // Send the response |
| 86 | await self.ocppResponseService.handleResponse(commandName as RequestCommand, payload, requestPayload); |
| 87 | resolve(payload); |
| 88 | } |
| 89 | |
| 90 | /** |
| 91 | * Function that will receive the request's rejection |
| 92 | * |
| 93 | * @param {OCPPError} error |
| 94 | */ |
| 95 | function rejectCallback(error: OCPPError): void { |
| 96 | if (self.chargingStation.getEnableStatistics()) { |
| 97 | self.chargingStation.performanceStatistics.addRequestStatistic(commandName, MessageType.CALL_ERROR_MESSAGE); |
| 98 | } |
| 99 | logger.debug(`${self.chargingStation.logPrefix()} Error: %j occurred when calling command %s with parameters: %j`, error, commandName, commandParams); |
| 100 | // Build Exception |
| 101 | // eslint-disable-next-line no-empty-function |
| 102 | self.chargingStation.requests[messageId] = [() => { }, () => { }, {}]; |
| 103 | // Send error |
| 104 | reject(error); |
| 105 | } |
| 106 | }); |
| 107 | } |
| 108 | |
| 109 | public handleRequestError(commandName: RequestCommand, error: Error): void { |
| 110 | logger.error(this.chargingStation.logPrefix() + ' Request command ' + commandName + ' error: %j', error); |
| 111 | throw error; |
| 112 | } |
| 113 | |
| 114 | public abstract sendHeartbeat(): Promise<void>; |
| 115 | public abstract sendBootNotification(chargePointModel: string, chargePointVendor: string, chargeBoxSerialNumber?: string, firmwareVersion?: string, chargePointSerialNumber?: string, iccid?: string, imsi?: string, meterSerialNumber?: string, meterType?: string): Promise<BootNotificationResponse>; |
| 116 | public abstract sendStatusNotification(connectorId: number, status: ChargePointStatus, errorCode?: ChargePointErrorCode): Promise<void>; |
| 117 | public abstract sendAuthorize(connectorId: number, idTag?: string): Promise<AuthorizeResponse>; |
| 118 | public abstract sendStartTransaction(connectorId: number, idTag?: string): Promise<StartTransactionResponse>; |
| 119 | public abstract sendStopTransaction(transactionId: number, meterStop: number, idTag?: string, reason?: StopTransactionReason): Promise<StopTransactionResponse>; |
| 120 | public abstract sendMeterValues(connectorId: number, transactionId: number, interval: number): Promise<void>; |
| 121 | public abstract sendTransactionBeginMeterValues(connectorId: number, transactionId: number, beginMeterValue: MeterValue): Promise<void>; |
| 122 | public abstract sendTransactionEndMeterValues(connectorId: number, transactionId: number, endMeterValue: MeterValue): Promise<void>; |
| 123 | public abstract sendDiagnosticsStatusNotification(diagnosticsStatus: DiagnosticsStatus): Promise<void>; |
| 124 | public abstract sendError(messageId: string, error: OCPPError, commandName: RequestCommand | IncomingRequestCommand): Promise<unknown>; |
| 125 | } |