X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2Focpp%2F1.6%2FOCPP16IncomingRequestService.ts;h=7f932cb0e19165e65899f8265f5f7763ce415821;hb=30d5941416b387d6315e3e13f6b3c49967a9e80c;hp=0cd676472a1d3adf36e901d5700a383476ed3078;hpb=5199f9fdf202eb534948f165a0994e1993675aa8;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts index 0cd67647..7f932cb0 100644 --- a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts @@ -1,10 +1,10 @@ -// Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. +// Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved. import { createWriteStream, readdirSync } from 'node:fs' import { dirname, join, resolve } from 'node:path' import { URL, fileURLToPath } from 'node:url' -import type { JSONSchemaType } from 'ajv' +import type { ValidateFunction } from 'ajv' import { Client, type FTPResponse } from 'basic-ftp' import { type Interval, @@ -113,7 +113,8 @@ import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService.js' const moduleName = 'OCPP16IncomingRequestService' export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { - protected jsonSchemas: Map> + protected payloadValidateFunctions: Map> + private readonly incomingRequestHandlers: Map< OCPP16IncomingRequestCommand, IncomingRequestHandler @@ -194,149 +195,350 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { this.handleRequestCancelReservation.bind(this) as unknown as IncomingRequestHandler ] ]) - this.jsonSchemas = new Map>([ + this.payloadValidateFunctions = new Map< + OCPP16IncomingRequestCommand, + ValidateFunction + >([ [ OCPP16IncomingRequestCommand.RESET, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/Reset.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/Reset.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.CLEAR_CACHE, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ClearCache.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/ClearCache.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/UnlockConnector.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/UnlockConnector.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.GET_CONFIGURATION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/GetConfiguration.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/GetConfiguration.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ChangeConfiguration.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/ChangeConfiguration.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/GetDiagnostics.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/GetDiagnostics.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/GetCompositeSchedule.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/GetCompositeSchedule.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/SetChargingProfile.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/SetChargingProfile.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ClearChargingProfile.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/ClearChargingProfile.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ChangeAvailability.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/ChangeAvailability.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/RemoteStartTransaction.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/RemoteStartTransaction.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/RemoteStopTransaction.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/RemoteStopTransaction.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/TriggerMessage.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/TriggerMessage.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.DATA_TRANSFER, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/DataTransfer.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/DataTransfer.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.UPDATE_FIRMWARE, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/UpdateFirmware.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/UpdateFirmware.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.RESERVE_NOW, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ReserveNow.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/ReserveNow.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.CANCEL_RESERVATION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/CancelReservation.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/CancelReservation.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ] ]) - this.validatePayload = this.validatePayload.bind(this) as ( - chargingStation: ChargingStation, - commandName: OCPP16IncomingRequestCommand, - commandPayload: JsonType - ) => boolean + this.on( + OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION, + (chargingStation: ChargingStation, connectorId: number, idTag: string) => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.getConnectorStatus(connectorId)!.transactionRemoteStarted = true + chargingStation.ocppRequestService + .requestHandler( + chargingStation, + OCPP16RequestCommand.START_TRANSACTION, + { + connectorId, + idTag + } + ) + .then(response => { + if (response.status === OCPP16AuthorizationStatus.ACCEPTED) { + logger.debug( + `${chargingStation.logPrefix()} Remote start transaction ACCEPTED on ${chargingStation.stationInfo?.chargingStationId}#${connectorId} for idTag '${idTag}'` + ) + } else { + logger.debug( + `${chargingStation.logPrefix()} Remote start transaction REJECTED on ${chargingStation.stationInfo?.chargingStationId}#${connectorId} for idTag '${idTag}'` + ) + } + }) + .catch(error => { + logger.error( + `${chargingStation.logPrefix()} ${moduleName}.constructor: Remote start transaction error:`, + error + ) + }) + } + ) + this.on( + `Trigger${OCPP16MessageTrigger.BootNotification}`, + (chargingStation: ChargingStation) => { + chargingStation.ocppRequestService + .requestHandler( + chargingStation, + OCPP16RequestCommand.BOOT_NOTIFICATION, + chargingStation.bootNotificationRequest, + { skipBufferingOnError: true, triggerMessage: true } + ) + .then(response => { + chargingStation.bootNotificationResponse = response + }) + .catch(error => { + logger.error( + `${chargingStation.logPrefix()} ${moduleName}.constructor: Trigger boot notification error:`, + error + ) + }) + } + ) + this.on(`Trigger${OCPP16MessageTrigger.Heartbeat}`, (chargingStation: ChargingStation) => { + chargingStation.ocppRequestService + .requestHandler( + chargingStation, + OCPP16RequestCommand.HEARTBEAT, + undefined, + { + triggerMessage: true + } + ) + .catch(error => { + logger.error( + `${chargingStation.logPrefix()} ${moduleName}.constructor: Trigger heartbeat error:`, + error + ) + }) + }) + this.on( + `$Trigger${OCPP16MessageTrigger.StatusNotification}`, + (chargingStation: ChargingStation, connectorId?: number) => { + const errorHandler = (error: Error): void => { + logger.error( + `${chargingStation.logPrefix()} ${moduleName}.constructor: Trigger status notification error:`, + error + ) + } + if (connectorId != null) { + chargingStation.ocppRequestService + .requestHandler( + chargingStation, + OCPP16RequestCommand.STATUS_NOTIFICATION, + { + connectorId, + errorCode: OCPP16ChargePointErrorCode.NO_ERROR, + status: chargingStation.getConnectorStatus(connectorId)?.status + }, + { + triggerMessage: true + } + ) + .catch(errorHandler) + } else if (chargingStation.hasEvses) { + for (const evseStatus of chargingStation.evses.values()) { + for (const [id, connectorStatus] of evseStatus.connectors) { + chargingStation.ocppRequestService + .requestHandler( + chargingStation, + OCPP16RequestCommand.STATUS_NOTIFICATION, + { + connectorId: id, + errorCode: OCPP16ChargePointErrorCode.NO_ERROR, + status: connectorStatus.status + }, + { + triggerMessage: true + } + ) + .catch(errorHandler) + } + } + } else { + for (const [id, connectorStatus] of chargingStation.connectors) { + chargingStation.ocppRequestService + .requestHandler( + chargingStation, + OCPP16RequestCommand.STATUS_NOTIFICATION, + { + connectorId: id, + errorCode: OCPP16ChargePointErrorCode.NO_ERROR, + status: connectorStatus.status + }, + { + triggerMessage: true + } + ) + .catch(errorHandler) + } + } + } + ) + this.validatePayload = this.validatePayload.bind(this) } public async incomingRequestHandler( @@ -427,17 +629,11 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { commandName: OCPP16IncomingRequestCommand, commandPayload: JsonType ): boolean { - if (this.jsonSchemas.has(commandName)) { - return this.validateIncomingRequestPayload( - chargingStation, - commandName, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.jsonSchemas.get(commandName)!, - commandPayload - ) + if (this.payloadValidateFunctions.has(commandName)) { + return this.validateIncomingRequestPayload(chargingStation, commandName, commandPayload) } logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation` + `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema validation function found for command '${commandName}' PDU validation` ) return false } @@ -691,10 +887,9 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { `${chargingStation.logPrefix()} Get composite schedule with a specified rate unit is not yet supported, no conversion will be done` ) } - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const connectorStatus = chargingStation.getConnectorStatus(connectorId)! + const connectorStatus = chargingStation.getConnectorStatus(connectorId) if ( - isEmptyArray(connectorStatus.chargingProfiles) && + isEmptyArray(connectorStatus?.chargingProfiles) && isEmptyArray(chargingStation.getConnectorStatus(0)?.chargingProfiles) ) { return OCPP16Constants.OCPP_RESPONSE_REJECTED @@ -712,36 +907,26 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { let previousCompositeSchedule: OCPP16ChargingSchedule | undefined let compositeSchedule: OCPP16ChargingSchedule | undefined for (const chargingProfile of chargingProfiles) { - if ( - chargingProfile.chargingSchedule.startSchedule == null && - connectorStatus.transactionStarted === true - ) { + if (chargingProfile.chargingSchedule.startSchedule == null) { logger.debug( `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${ chargingProfile.chargingProfileId } has no startSchedule defined. Trying to set it to the connector current transaction start date` ) // OCPP specifies that if startSchedule is not defined, it should be relative to start of the connector transaction - chargingProfile.chargingSchedule.startSchedule = connectorStatus.transactionStart + chargingProfile.chargingSchedule.startSchedule = connectorStatus?.transactionStart } - if ( - chargingProfile.chargingSchedule.startSchedule != null && - !isDate(chargingProfile.chargingSchedule.startSchedule) - ) { + if (!isDate(chargingProfile.chargingSchedule.startSchedule)) { logger.warn( `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${ chargingProfile.chargingProfileId } startSchedule property is not a Date instance. Trying to convert it to a Date instance` ) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion chargingProfile.chargingSchedule.startSchedule = convertToDate( chargingProfile.chargingSchedule.startSchedule - )! + ) } - if ( - chargingProfile.chargingSchedule.startSchedule != null && - chargingProfile.chargingSchedule.duration == null - ) { + if (chargingProfile.chargingSchedule.duration == null) { logger.debug( `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${ chargingProfile.chargingProfileId @@ -750,14 +935,15 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { // OCPP specifies that if duration is not defined, it should be infinite chargingProfile.chargingSchedule.duration = differenceInSeconds( maxTime, - chargingProfile.chargingSchedule.startSchedule + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingProfile.chargingSchedule.startSchedule! ) } if ( !prepareChargingProfileKind( connectorStatus, chargingProfile, - compositeScheduleInterval.start as Date, + compositeScheduleInterval.start, chargingStation.logPrefix() ) ) { @@ -766,7 +952,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { if ( !canProceedChargingProfile( chargingProfile, - compositeScheduleInterval.start as Date, + compositeScheduleInterval.start, chargingStation.logPrefix() ) ) { @@ -782,8 +968,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { if (compositeSchedule != null) { return { status: GenericStatus.Accepted, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - scheduleStart: compositeSchedule.startSchedule!, + scheduleStart: compositeSchedule.startSchedule, connectorId, chargingSchedule: compositeSchedule } @@ -815,8 +1000,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const connectorStatus = chargingStation.getConnectorStatus(connectorId!) if (connectorId != null && isNotEmptyArray(connectorStatus?.chargingProfiles)) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - connectorStatus!.chargingProfiles = [] + connectorStatus.chargingProfiles = [] logger.debug( `${chargingStation.logPrefix()} Charging profile(s) cleared on connector id ${connectorId}` ) @@ -866,7 +1050,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { ? OCPP16ChargePointStatus.Available : OCPP16ChargePointStatus.Unavailable if (connectorId === 0) { - let response: OCPP16ChangeAvailabilityResponse + let response: OCPP16ChangeAvailabilityResponse | undefined if (chargingStation.hasEvses) { for (const evseStatus of chargingStation.evses.values()) { response = await OCPP16ServiceUtils.changeAvailability( @@ -931,94 +1115,47 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { idTag ) } - const remoteStartTransactionLogMsg = ` - ${chargingStation.logPrefix()} Transaction remotely STARTED on ${chargingStation.stationInfo - ?.chargingStationId}#${transactionConnectorId} for idTag '${idTag}'` - await OCPP16ServiceUtils.sendAndSetConnectorStatus( - chargingStation, - transactionConnectorId, - OCPP16ChargePointStatus.Preparing - ) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const connectorStatus = chargingStation.getConnectorStatus(transactionConnectorId)! - // Authorization check required + // idTag authorization check required if ( chargingStation.getAuthorizeRemoteTxRequests() && - (await OCPP16ServiceUtils.isIdTagAuthorized(chargingStation, transactionConnectorId, idTag)) + !(await OCPP16ServiceUtils.isIdTagAuthorized(chargingStation, transactionConnectorId, idTag)) ) { - // Authorization successful, start transaction - if ( - (chargingProfile != null && - this.setRemoteStartTransactionChargingProfile( - chargingStation, - transactionConnectorId, - chargingProfile - )) || - chargingProfile == null - ) { - connectorStatus.transactionRemoteStarted = true - if ( - ( - await chargingStation.ocppRequestService.requestHandler< - OCPP16StartTransactionRequest, - OCPP16StartTransactionResponse - >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, { - connectorId: transactionConnectorId, - idTag - }) - ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED - ) { - logger.debug(remoteStartTransactionLogMsg) - return OCPP16Constants.OCPP_RESPONSE_ACCEPTED - } - return await this.notifyRemoteStartTransactionRejected( - chargingStation, - transactionConnectorId, - idTag - ) - } return await this.notifyRemoteStartTransactionRejected( chargingStation, transactionConnectorId, idTag ) } - // No authorization check required, start transaction + await OCPP16ServiceUtils.sendAndSetConnectorStatus( + chargingStation, + transactionConnectorId, + OCPP16ChargePointStatus.Preparing + ) if ( - (chargingProfile != null && - this.setRemoteStartTransactionChargingProfile( - chargingStation, - transactionConnectorId, - chargingProfile - )) || - chargingProfile == null + chargingProfile != null && + !this.setRemoteStartTransactionChargingProfile( + chargingStation, + transactionConnectorId, + chargingProfile + ) ) { - connectorStatus.transactionRemoteStarted = true - if ( - ( - await chargingStation.ocppRequestService.requestHandler< - OCPP16StartTransactionRequest, - OCPP16StartTransactionResponse - >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, { - connectorId: transactionConnectorId, - idTag - }) - ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED - ) { - logger.debug(remoteStartTransactionLogMsg) - return OCPP16Constants.OCPP_RESPONSE_ACCEPTED - } return await this.notifyRemoteStartTransactionRejected( chargingStation, transactionConnectorId, idTag ) } - return await this.notifyRemoteStartTransactionRejected( - chargingStation, - transactionConnectorId, - idTag - ) + Promise.resolve() + .then(() => + this.emit( + OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION, + chargingStation, + transactionConnectorId, + idTag + ) + ) + .catch(Constants.EMPTY_FUNCTION) + return OCPP16Constants.OCPP_RESPONSE_ACCEPTED } private async notifyRemoteStartTransactionRejected ( @@ -1035,7 +1172,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { ) } logger.warn( - `${chargingStation.logPrefix()} Remote starting transaction REJECTED on connector id ${connectorId}, idTag '${idTag}', availability '${connectorStatus?.availability}', status '${connectorStatus?.status}'` + `${chargingStation.logPrefix()} Remote start transaction REJECTED on connector id ${connectorId}, idTag '${idTag}', availability '${connectorStatus?.availability}', status '${connectorStatus?.status}'` ) return OCPP16Constants.OCPP_RESPONSE_REJECTED } @@ -1108,15 +1245,15 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { ) return OCPP16Constants.OCPP_RESPONSE_EMPTY } - let { retrieveDate } = commandPayload + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + commandPayload.retrieveDate = convertToDate(commandPayload.retrieveDate)! + const { retrieveDate } = commandPayload if (chargingStation.stationInfo?.firmwareStatus !== OCPP16FirmwareStatus.Installed) { logger.warn( `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: firmware update is already in progress` ) return OCPP16Constants.OCPP_RESPONSE_EMPTY } - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - retrieveDate = convertToDate(retrieveDate)! const now = Date.now() if (retrieveDate.getTime() <= now) { this.updateFirmwareSimulation(chargingStation).catch(Constants.EMPTY_FUNCTION) @@ -1299,8 +1436,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { let ftpClient: Client | undefined try { const logFiles = readdirSync(resolve(dirname(fileURLToPath(import.meta.url)), '../')) - .filter((file) => file.endsWith('.log')) - .map((file) => join('./', file)) + .filter(file => file.endsWith('.log')) + .map(file => join('./', file)) const diagnosticsArchive = `${chargingStation.stationInfo?.chargingStationId}_logs.tar.gz` create({ gzip: true }, logFiles).pipe(createWriteStream(diagnosticsArchive)) ftpClient = new Client() @@ -1312,7 +1449,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { }) let uploadResponse: FTPResponse | undefined if (accessResponse.code === 220) { - ftpClient.trackProgress((info) => { + ftpClient.trackProgress(info => { logger.info( `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: ${ info.bytes / 1024 @@ -1325,7 +1462,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, { status: OCPP16DiagnosticsStatus.Uploading }) - .catch((error) => { + .catch(error => { logger.error( `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Error while sending '${ OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION @@ -1419,97 +1556,27 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { try { switch (requestedMessage) { case OCPP16MessageTrigger.BootNotification: - setTimeout(() => { - chargingStation.ocppRequestService - .requestHandler( - chargingStation, - OCPP16RequestCommand.BOOT_NOTIFICATION, - chargingStation.bootNotificationRequest, - { skipBufferingOnError: true, triggerMessage: true } + Promise.resolve() + .then(() => + this.emit(`Trigger${OCPP16MessageTrigger.BootNotification}`, chargingStation) ) - .then((response) => { - chargingStation.bootNotificationResponse = response - }) - .catch(Constants.EMPTY_FUNCTION) - }, OCPP16Constants.OCPP_TRIGGER_MESSAGE_DELAY) + .catch(Constants.EMPTY_FUNCTION) return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED case OCPP16MessageTrigger.Heartbeat: - setTimeout(() => { - chargingStation.ocppRequestService - .requestHandler( - chargingStation, - OCPP16RequestCommand.HEARTBEAT, - undefined, - { - triggerMessage: true - } - ) - .catch(Constants.EMPTY_FUNCTION) - }, OCPP16Constants.OCPP_TRIGGER_MESSAGE_DELAY) + Promise.resolve() + .then(() => this.emit(`Trigger${OCPP16MessageTrigger.Heartbeat}`, chargingStation)) + .catch(Constants.EMPTY_FUNCTION) return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED case OCPP16MessageTrigger.StatusNotification: - setTimeout(() => { - if (connectorId != null) { - chargingStation.ocppRequestService - .requestHandler( + Promise.resolve() + .then(() => + this.emit( + `Trigger${OCPP16MessageTrigger.StatusNotification}`, chargingStation, - OCPP16RequestCommand.STATUS_NOTIFICATION, - { - connectorId, - errorCode: OCPP16ChargePointErrorCode.NO_ERROR, - status: chargingStation.getConnectorStatus(connectorId)?.status - }, - { - triggerMessage: true - } + connectorId ) - .catch(Constants.EMPTY_FUNCTION) - } else { - if (chargingStation.hasEvses) { - for (const evseStatus of chargingStation.evses.values()) { - for (const [id, connectorStatus] of evseStatus.connectors) { - chargingStation.ocppRequestService - .requestHandler< - OCPP16StatusNotificationRequest, - OCPP16StatusNotificationResponse - >( - chargingStation, - OCPP16RequestCommand.STATUS_NOTIFICATION, - { - connectorId: id, - errorCode: OCPP16ChargePointErrorCode.NO_ERROR, - status: connectorStatus.status - }, - { - triggerMessage: true - } - ) - .catch(Constants.EMPTY_FUNCTION) - } - } - } else { - for (const id of chargingStation.connectors.keys()) { - chargingStation.ocppRequestService - .requestHandler< - OCPP16StatusNotificationRequest, - OCPP16StatusNotificationResponse - >( - chargingStation, - OCPP16RequestCommand.STATUS_NOTIFICATION, - { - connectorId: id, - errorCode: OCPP16ChargePointErrorCode.NO_ERROR, - status: chargingStation.getConnectorStatus(id)?.status - }, - { - triggerMessage: true - } - ) - .catch(Constants.EMPTY_FUNCTION) - } - } - } - }, OCPP16Constants.OCPP_TRIGGER_MESSAGE_DELAY) + ) + .catch(Constants.EMPTY_FUNCTION) return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED default: return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED @@ -1559,6 +1626,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { ) { return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + commandPayload.expiryDate = convertToDate(commandPayload.expiryDate)! const { reservationId, idTag, connectorId } = commandPayload let response: OCPP16ReserveNowResponse try { @@ -1572,8 +1641,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED } await removeExpiredReservations(chargingStation) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - switch (chargingStation.getConnectorStatus(connectorId)!.status) { + switch (chargingStation.getConnectorStatus(connectorId)?.status) { case OCPP16ChargePointStatus.Faulted: response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_FAULTED break