X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;ds=sidebyside;f=src%2Fcharging-station%2FChargingStationWorkerBroadcastChannel.ts;h=154e1afb14bbb8631fdb570c3a95c2888e4516f2;hb=1984f1944eeb7468c2f3adbf03b83fbc98910dff;hp=ebc100990c943fae4abeb804760292258b8ee409;hpb=a9ed42b2eaac1fb26700d6d86952d0d2cee4d857;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ChargingStationWorkerBroadcastChannel.ts b/src/charging-station/ChargingStationWorkerBroadcastChannel.ts index ebc10099..154e1afb 100644 --- a/src/charging-station/ChargingStationWorkerBroadcastChannel.ts +++ b/src/charging-station/ChargingStationWorkerBroadcastChannel.ts @@ -1,9 +1,15 @@ import BaseError from '../exception/BaseError'; import type OCPPError from '../exception/OCPPError'; -import { RequestCommand, type StatusNotificationRequest } from '../types/ocpp/Requests'; -import type { StatusNotificationResponse } from '../types/ocpp/Responses'; +import { + HeartbeatRequest, + RequestCommand, + type StatusNotificationRequest, +} from '../types/ocpp/Requests'; +import type { HeartbeatResponse, StatusNotificationResponse } from '../types/ocpp/Responses'; import { AuthorizationStatus, + AuthorizeRequest, + AuthorizeResponse, StartTransactionRequest, StartTransactionResponse, StopTransactionRequest, @@ -27,59 +33,148 @@ const moduleName = 'ChargingStationWorkerBroadcastChannel'; type CommandResponse = | StartTransactionResponse | StopTransactionResponse - | StatusNotificationResponse; + | AuthorizeResponse + | StatusNotificationResponse + | HeartbeatResponse; + +type CommandHandler = ( + requestPayload?: BroadcastChannelRequestPayload +) => Promise | void; export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChannel { + private readonly commandHandlers: Map; + private readonly chargingStation: ChargingStation; constructor(chargingStation: ChargingStation) { super(); + this.commandHandlers = new Map([ + [BroadcastChannelProcedureName.START_CHARGING_STATION, () => this.chargingStation.start()], + [ + BroadcastChannelProcedureName.STOP_CHARGING_STATION, + async () => this.chargingStation.stop(), + ], + [ + BroadcastChannelProcedureName.OPEN_CONNECTION, + () => this.chargingStation.openWSConnection(), + ], + [ + BroadcastChannelProcedureName.CLOSE_CONNECTION, + () => this.chargingStation.closeWSConnection(), + ], + [ + BroadcastChannelProcedureName.START_TRANSACTION, + async (requestPayload?: BroadcastChannelRequestPayload) => + this.chargingStation.ocppRequestService.requestHandler< + StartTransactionRequest, + StartTransactionResponse + >(this.chargingStation, RequestCommand.START_TRANSACTION, requestPayload), + ], + [ + BroadcastChannelProcedureName.STOP_TRANSACTION, + async (requestPayload?: BroadcastChannelRequestPayload) => + this.chargingStation.ocppRequestService.requestHandler< + StopTransactionRequest, + StartTransactionResponse + >(this.chargingStation, RequestCommand.STOP_TRANSACTION, { + ...requestPayload, + meterStop: this.chargingStation.getEnergyActiveImportRegisterByTransactionId( + requestPayload.transactionId, + true + ), + }), + ], + [ + BroadcastChannelProcedureName.START_AUTOMATIC_TRANSACTION_GENERATOR, + (requestPayload?: BroadcastChannelRequestPayload) => + this.chargingStation.startAutomaticTransactionGenerator(requestPayload.connectorIds), + ], + [ + BroadcastChannelProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR, + (requestPayload?: BroadcastChannelRequestPayload) => + this.chargingStation.stopAutomaticTransactionGenerator(requestPayload.connectorIds), + ], + [ + BroadcastChannelProcedureName.AUTHORIZE, + async (requestPayload?: BroadcastChannelRequestPayload) => + this.chargingStation.ocppRequestService.requestHandler< + AuthorizeRequest, + AuthorizeResponse + >(this.chargingStation, RequestCommand.AUTHORIZE, requestPayload), + ], + [ + BroadcastChannelProcedureName.STATUS_NOTIFICATION, + async (requestPayload?: BroadcastChannelRequestPayload) => + this.chargingStation.ocppRequestService.requestHandler< + StatusNotificationRequest, + StatusNotificationResponse + >(this.chargingStation, RequestCommand.STATUS_NOTIFICATION, requestPayload), + ], + [ + BroadcastChannelProcedureName.HEARTBEAT, + async (requestPayload?: BroadcastChannelRequestPayload) => + this.chargingStation.ocppRequestService.requestHandler< + HeartbeatRequest, + HeartbeatResponse + >(this.chargingStation, RequestCommand.HEARTBEAT, requestPayload), + ], + ]); this.chargingStation = chargingStation; this.onmessage = this.requestHandler.bind(this) as (message: MessageEvent) => void; this.onmessageerror = this.messageErrorHandler.bind(this) as (message: MessageEvent) => void; } private async requestHandler(messageEvent: MessageEvent): Promise { - if (this.isResponse(messageEvent.data) === true) { + const validatedMessageEvent = this.validateMessageEvent(messageEvent); + if (validatedMessageEvent === false) { return; } - const [uuid, command, requestPayload] = this.validateMessageEvent(messageEvent) - .data as BroadcastChannelRequest; + if (this.isResponse(validatedMessageEvent.data) === true) { + return; + } + const [uuid, command, requestPayload] = validatedMessageEvent.data as BroadcastChannelRequest; - if (requestPayload?.hashIds !== undefined || requestPayload?.hashId !== undefined) { - if ( - requestPayload?.hashId === undefined && - requestPayload?.hashIds?.includes(this.chargingStation.stationInfo.hashId) === false - ) { - return; - } - if ( - requestPayload?.hashIds === undefined && - requestPayload?.hashId !== this.chargingStation.stationInfo.hashId - ) { - return; - } - if (requestPayload?.hashId !== undefined) { - logger.warn( - `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'hashId' field usage in PDU is deprecated, use 'hashIds' instead` - ); - } + if ( + requestPayload?.hashIds !== undefined && + requestPayload?.hashIds?.includes(this.chargingStation.stationInfo.hashId) === false + ) { + return; + } + if (requestPayload?.hashId !== undefined) { + logger.error( + `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'hashId' field usage in PDU is deprecated, use 'hashIds' instead` + ); + return; } let responsePayload: BroadcastChannelResponsePayload; - let commandResponse: CommandResponse; + let commandResponse: CommandResponse | void; try { commandResponse = await this.commandHandler(command, requestPayload); - if (commandResponse === undefined) { + if (commandResponse === undefined || commandResponse === null) { responsePayload = { hashId: this.chargingStation.stationInfo.hashId, status: ResponseStatus.SUCCESS, }; } else { - responsePayload = { - hashId: this.chargingStation.stationInfo.hashId, - status: this.commandResponseToResponseStatus(commandResponse), - }; + const responseStatus = this.commandResponseToResponseStatus( + command, + commandResponse as CommandResponse + ); + if (responseStatus === ResponseStatus.SUCCESS) { + responsePayload = { + hashId: this.chargingStation.stationInfo.hashId, + status: responseStatus, + }; + } else { + responsePayload = { + hashId: this.chargingStation.stationInfo.hashId, + status: responseStatus, + command, + requestPayload, + commandResponse: commandResponse as CommandResponse, + }; + } } } catch (error) { logger.error( @@ -91,7 +186,7 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca status: ResponseStatus.FAILURE, command, requestPayload, - commandResponse, + commandResponse: commandResponse as CommandResponse, errorMessage: (error as Error).message, errorStack: (error as Error).stack, errorDetails: (error as OCPPError).details, @@ -110,75 +205,57 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca private async commandHandler( command: BroadcastChannelProcedureName, requestPayload: BroadcastChannelRequestPayload - ): Promise { + ): Promise { + if (this.commandHandlers.has(command) === true) { + this.cleanRequestPayload(command, requestPayload); + return this.commandHandlers.get(command)(requestPayload); + } + throw new BaseError(`Unknown worker broadcast channel command: ${command}`); + } + + private cleanRequestPayload( + command: BroadcastChannelProcedureName, + requestPayload: BroadcastChannelRequestPayload + ): void { + delete requestPayload.hashId; + delete requestPayload.hashIds; + [ + BroadcastChannelProcedureName.START_AUTOMATIC_TRANSACTION_GENERATOR, + BroadcastChannelProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR, + ].includes(command) === false && delete requestPayload.connectorIds; + } + + private commandResponseToResponseStatus( + command: BroadcastChannelProcedureName, + commandResponse: CommandResponse + ): ResponseStatus { switch (command) { - case BroadcastChannelProcedureName.START_CHARGING_STATION: - this.chargingStation.start(); - break; - case BroadcastChannelProcedureName.STOP_CHARGING_STATION: - await this.chargingStation.stop(); - break; - case BroadcastChannelProcedureName.OPEN_CONNECTION: - this.chargingStation.openWSConnection(); - break; - case BroadcastChannelProcedureName.CLOSE_CONNECTION: - this.chargingStation.closeWSConnection(); - break; case BroadcastChannelProcedureName.START_TRANSACTION: - return this.chargingStation.ocppRequestService.requestHandler< - StartTransactionRequest, - StartTransactionResponse - >(this.chargingStation, RequestCommand.START_TRANSACTION, { - connectorId: requestPayload.connectorId, - idTag: requestPayload.idTag, - }); case BroadcastChannelProcedureName.STOP_TRANSACTION: - return this.chargingStation.ocppRequestService.requestHandler< - StopTransactionRequest, - StopTransactionResponse - >(this.chargingStation, RequestCommand.STOP_TRANSACTION, { - transactionId: requestPayload.transactionId, - meterStop: this.chargingStation.getEnergyActiveImportRegisterByTransactionId( - requestPayload.transactionId, - true - ), - idTag: this.chargingStation.getTransactionIdTag(requestPayload.transactionId), - ...(requestPayload.reason && { reason: requestPayload.reason }), - }); - case BroadcastChannelProcedureName.START_AUTOMATIC_TRANSACTION_GENERATOR: - this.chargingStation.startAutomaticTransactionGenerator(requestPayload.connectorIds); - break; - case BroadcastChannelProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR: - this.chargingStation.stopAutomaticTransactionGenerator(requestPayload.connectorIds); - break; + case BroadcastChannelProcedureName.AUTHORIZE: + if ( + ( + commandResponse as + | StartTransactionResponse + | StopTransactionResponse + | AuthorizeResponse + )?.idTagInfo?.status === AuthorizationStatus.ACCEPTED + ) { + return ResponseStatus.SUCCESS; + } + return ResponseStatus.FAILURE; case BroadcastChannelProcedureName.STATUS_NOTIFICATION: - return this.chargingStation.ocppRequestService.requestHandler< - StatusNotificationRequest, - StatusNotificationResponse - >(this.chargingStation, RequestCommand.STATUS_NOTIFICATION, { - connectorId: requestPayload.connectorId, - errorCode: requestPayload.errorCode, - status: requestPayload.status, - ...(requestPayload.info && { info: requestPayload.info }), - ...(requestPayload.timestamp && { timestamp: requestPayload.timestamp }), - ...(requestPayload.vendorId && { vendorId: requestPayload.vendorId }), - ...(requestPayload.vendorErrorCode && { - vendorErrorCode: requestPayload.vendorErrorCode, - }), - }); + if (Utils.isEmptyObject(commandResponse) === true) { + return ResponseStatus.SUCCESS; + } + return ResponseStatus.FAILURE; + case BroadcastChannelProcedureName.HEARTBEAT: + if ('currentTime' in commandResponse) { + return ResponseStatus.SUCCESS; + } + return ResponseStatus.FAILURE; default: - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - throw new BaseError(`Unknown worker broadcast channel command: ${command}`); - } - } - - private commandResponseToResponseStatus(commandResponse: CommandResponse): ResponseStatus { - if ( - Utils.isEmptyObject(commandResponse) || - commandResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED - ) { - return ResponseStatus.SUCCESS; + return ResponseStatus.FAILURE; } - return ResponseStatus.FAILURE; } }