X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2Focpp%2F1.6%2FOCPP16IncomingRequestService.ts;h=a61dedd8edcfebfd6e0b26a17672ae7695f6d7d5;hb=65b5177e252fab95766ef8141e60e0b2434f2338;hp=1941b665f00a5f7da58756c8186850abbb24b5ec;hpb=acfa5fd10b8489f5d85654e654356e65bfd1e0d0;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 1941b665..a61dedd8 100644 --- a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts @@ -1,43 +1,37 @@ -// 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 { createWriteStream, readdirSync } from 'node:fs' +import { dirname, join, resolve } from 'node:path' +import { URL, fileURLToPath } from 'node:url' -import type { JSONSchemaType } from 'ajv'; -import { Client, type FTPResponse } from 'basic-ftp'; +import type { JSONSchemaType } from 'ajv' +import { Client, type FTPResponse } from 'basic-ftp' import { + type Interval, addSeconds, differenceInSeconds, - isAfter, - isBefore, isDate, - isWithinInterval, - max, - maxTime, - min, - minTime, - secondsToMilliseconds, -} from 'date-fns'; -import { create } from 'tar'; + secondsToMilliseconds +} from 'date-fns' +import { maxTime } from 'date-fns/constants' +import { create } from 'tar' -import { OCPP16Constants } from './OCPP16Constants'; -import { OCPP16ServiceUtils } from './OCPP16ServiceUtils'; +import { OCPP16Constants } from './OCPP16Constants.js' +import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js' import { type ChargingStation, canProceedChargingProfile, checkChargingStation, getConfigurationKey, + getConnectorChargingProfiles, prepareChargingProfileKind, removeExpiredReservations, - setConfigurationKeyValue, -} from '../../../charging-station'; -import { OCPPError } from '../../../exception'; + setConfigurationKeyValue +} from '../../../charging-station/index.js' +import { OCPPError } from '../../../exception/index.js' import { type ChangeConfigurationRequest, type ChangeConfigurationResponse, - type ClearChargingProfileRequest, - type ClearChargingProfileResponse, ErrorType, type GenericResponse, GenericStatus, @@ -46,7 +40,6 @@ import { type GetDiagnosticsRequest, type GetDiagnosticsResponse, type IncomingRequestHandler, - type JsonObject, type JsonType, OCPP16AuthorizationStatus, OCPP16AvailabilityType, @@ -59,10 +52,10 @@ import { OCPP16ChargePointStatus, type OCPP16ChargingProfile, OCPP16ChargingProfilePurposeType, - OCPP16ChargingRateUnitType, type OCPP16ChargingSchedule, - type OCPP16ChargingSchedulePeriod, type OCPP16ClearCacheRequest, + type OCPP16ClearChargingProfileRequest, + type OCPP16ClearChargingProfileResponse, type OCPP16DataTransferRequest, type OCPP16DataTransferResponse, OCPP16DataTransferVendorId, @@ -101,11 +94,10 @@ import { type SetChargingProfileRequest, type SetChargingProfileResponse, type UnlockConnectorRequest, - type UnlockConnectorResponse, -} from '../../../types'; + type UnlockConnectorResponse +} from '../../../types/index.js' import { Constants, - cloneObject, convertToDate, convertToInt, formatDurationMilliSeconds, @@ -113,250 +105,250 @@ import { isEmptyArray, isNotEmptyArray, isNotEmptyString, - isNullOrUndefined, - isUndefined, - isValidTime, logger, - sleep, -} from '../../../utils'; -import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService'; + sleep +} from '../../../utils/index.js' +import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService.js' -const moduleName = 'OCPP16IncomingRequestService'; +const moduleName = 'OCPP16IncomingRequestService' export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { - protected jsonSchemas: Map>; - private incomingRequestHandlers: Map; + protected jsonSchemas: Map> + private readonly incomingRequestHandlers: Map< + OCPP16IncomingRequestCommand, + IncomingRequestHandler + > - public constructor() { - // if (new.target?.name === moduleName) { - // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`); + public constructor () { + // if (new.target.name === moduleName) { + // throw new TypeError(`Cannot construct ${new.target.name} instances directly`) // } - super(OCPPVersion.VERSION_16); + super(OCPPVersion.VERSION_16) this.incomingRequestHandlers = new Map([ [ OCPP16IncomingRequestCommand.RESET, - this.handleRequestReset.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestReset.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.CLEAR_CACHE, - this.handleRequestClearCache.bind(this) as IncomingRequestHandler, + this.handleRequestClearCache.bind(this) as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR, - this.handleRequestUnlockConnector.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestUnlockConnector.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.GET_CONFIGURATION, - this.handleRequestGetConfiguration.bind(this) as IncomingRequestHandler, + this.handleRequestGetConfiguration.bind(this) as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION, - this.handleRequestChangeConfiguration.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestChangeConfiguration.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE, - this.handleRequestGetCompositeSchedule.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestGetCompositeSchedule.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE, - this.handleRequestSetChargingProfile.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestSetChargingProfile.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE, - this.handleRequestClearChargingProfile.bind(this) as IncomingRequestHandler, + this.handleRequestClearChargingProfile.bind(this) as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY, - this.handleRequestChangeAvailability.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestChangeAvailability.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION, - this.handleRequestRemoteStartTransaction.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestRemoteStartTransaction.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION, - this.handleRequestRemoteStopTransaction.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestRemoteStopTransaction.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, - this.handleRequestGetDiagnostics.bind(this) as IncomingRequestHandler, + this.handleRequestGetDiagnostics.bind(this) as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, - this.handleRequestTriggerMessage.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestTriggerMessage.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.DATA_TRANSFER, - this.handleRequestDataTransfer.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestDataTransfer.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.UPDATE_FIRMWARE, - this.handleRequestUpdateFirmware.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestUpdateFirmware.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.RESERVE_NOW, - this.handleRequestReserveNow.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestReserveNow.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.CANCEL_RESERVATION, - this.handleRequestCancelReservation.bind(this) as unknown as IncomingRequestHandler, - ], - ]); - this.jsonSchemas = new Map>([ + this.handleRequestCancelReservation.bind(this) as unknown as IncomingRequestHandler + ] + ]) + this.jsonSchemas = new Map>([ [ OCPP16IncomingRequestCommand.RESET, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/Reset.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.CLEAR_CACHE, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/ClearCache.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/UnlockConnector.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.GET_CONFIGURATION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/GetConfiguration.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/ChangeConfiguration.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/GetDiagnostics.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/GetCompositeSchedule.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/SetChargingProfile.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE, - OCPP16ServiceUtils.parseJsonSchemaFile( + OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/ClearChargingProfile.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/ChangeAvailability.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/RemoteStartTransaction.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/RemoteStopTransaction.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/TriggerMessage.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.DATA_TRANSFER, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/DataTransfer.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.UPDATE_FIRMWARE, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/UpdateFirmware.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.RESERVE_NOW, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/ReserveNow.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.CANCEL_RESERVATION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/CancelReservation.json', moduleName, - 'constructor', - ), - ], - ]); + 'constructor' + ) + ] + ]) this.validatePayload = this.validatePayload.bind(this) as ( chargingStation: ChargingStation, commandName: OCPP16IncomingRequestCommand, - commandPayload: JsonType, - ) => boolean; + commandPayload: JsonType + ) => boolean } public async incomingRequestHandler( chargingStation: ChargingStation, messageId: string, commandName: OCPP16IncomingRequestCommand, - commandPayload: ReqType, + commandPayload: ReqType ): Promise { - let response: ResType; + let response: ResType if ( - chargingStation.getOcppStrictCompliance() === true && - chargingStation.inPendingState() === true && + chargingStation.stationInfo?.ocppStrictCompliance === true && + chargingStation.inPendingState() && (commandName === OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION || commandName === OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION) ) { @@ -364,37 +356,37 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { ErrorType.SECURITY_ERROR, `${commandName} cannot be issued to handle request PDU ${JSON.stringify( commandPayload, - null, - 2, + undefined, + 2 )} while the charging station is in pending state on the central server`, commandName, - commandPayload, - ); + commandPayload + ) } if ( - chargingStation.isRegistered() === true || - (chargingStation.getOcppStrictCompliance() === false && - chargingStation.inUnknownState() === true) + chargingStation.isRegistered() || + (chargingStation.stationInfo?.ocppStrictCompliance === false && + chargingStation.inUnknownState()) ) { if ( - this.incomingRequestHandlers.has(commandName) === true && - OCPP16ServiceUtils.isIncomingRequestCommandSupported(chargingStation, commandName) === true + this.incomingRequestHandlers.has(commandName) && + OCPP16ServiceUtils.isIncomingRequestCommandSupported(chargingStation, commandName) ) { try { - this.validatePayload(chargingStation, commandName, commandPayload); + this.validatePayload(chargingStation, commandName, commandPayload) // Call the method to build the response + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion response = (await this.incomingRequestHandlers.get(commandName)!( chargingStation, - commandPayload, - )) as ResType; + commandPayload + )) as ResType } catch (error) { // Log logger.error( - `${chargingStation.logPrefix()} ${moduleName}.incomingRequestHandler: - Handle incoming request error:`, - error, - ); - throw error; + `${chargingStation.logPrefix()} ${moduleName}.incomingRequestHandler: Handle incoming request error:`, + error + ) + throw error } } else { // Throw exception @@ -402,173 +394,162 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { ErrorType.NOT_IMPLEMENTED, `${commandName} is not implemented to handle request PDU ${JSON.stringify( commandPayload, - null, - 2, + undefined, + 2 )}`, commandName, - commandPayload, - ); + commandPayload + ) } } else { throw new OCPPError( ErrorType.SECURITY_ERROR, `${commandName} cannot be issued to handle request PDU ${JSON.stringify( commandPayload, - null, - 2, + undefined, + 2 )} while the charging station is not registered on the central server.`, commandName, - commandPayload, - ); + commandPayload + ) } // Send the built response await chargingStation.ocppRequestService.sendResponse( chargingStation, messageId, response, - commandName, - ); + commandName + ) } - private validatePayload( + private validatePayload ( chargingStation: ChargingStation, commandName: OCPP16IncomingRequestCommand, - commandPayload: JsonType, + commandPayload: JsonType ): boolean { - if (this.jsonSchemas.has(commandName) === true) { + 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, - ); + commandPayload + ) } logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found - for command '${commandName}' PDU validation`, - ); - return false; + `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation` + ) + return false } // Simulate charging station restart - private handleRequestReset( + private handleRequestReset ( chargingStation: ChargingStation, - commandPayload: ResetRequest, + commandPayload: ResetRequest ): GenericResponse { - const { type } = commandPayload; - this.runInAsyncScope( - chargingStation.reset.bind(chargingStation) as ( - this: ChargingStation, - ...args: unknown[] - ) => Promise, - chargingStation, - `${type}Reset` as OCPP16StopTransactionReason, - ).catch(Constants.EMPTY_FUNCTION); + const { type } = commandPayload + chargingStation + .reset(`${type}Reset` as OCPP16StopTransactionReason) + .catch(Constants.EMPTY_FUNCTION) logger.info( - `${chargingStation.logPrefix()} ${type} reset command received, simulating it. The station will be - back online in ${formatDurationMilliSeconds(chargingStation.stationInfo.resetTime!)}`, - ); - return OCPP16Constants.OCPP_RESPONSE_ACCEPTED; + `${chargingStation.logPrefix()} ${type} reset command received, simulating it. The station will be back online in ${formatDurationMilliSeconds( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.stationInfo!.resetTime! + )}` + ) + return OCPP16Constants.OCPP_RESPONSE_ACCEPTED } - private async handleRequestUnlockConnector( + private async handleRequestUnlockConnector ( chargingStation: ChargingStation, - commandPayload: UnlockConnectorRequest, + commandPayload: UnlockConnectorRequest ): Promise { - const { connectorId } = commandPayload; - if (chargingStation.hasConnector(connectorId) === false) { + const { connectorId } = commandPayload + if (!chargingStation.hasConnector(connectorId)) { logger.error( - `${chargingStation.logPrefix()} Trying to unlock a non existing - connector id ${connectorId.toString()}`, - ); - return OCPP16Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED; + `${chargingStation.logPrefix()} Trying to unlock a non existing connector id ${connectorId}` + ) + return OCPP16Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED } if (connectorId === 0) { - logger.error( - `${chargingStation.logPrefix()} Trying to unlock connector id ${connectorId.toString()}`, - ); - return OCPP16Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED; + logger.error(`${chargingStation.logPrefix()} Trying to unlock connector id ${connectorId}`) + return OCPP16Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED } if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) { const stopResponse = await chargingStation.stopTransactionOnConnector( connectorId, - OCPP16StopTransactionReason.UNLOCK_COMMAND, - ); + OCPP16StopTransactionReason.UNLOCK_COMMAND + ) if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) { - return OCPP16Constants.OCPP_RESPONSE_UNLOCKED; + return OCPP16Constants.OCPP_RESPONSE_UNLOCKED } - return OCPP16Constants.OCPP_RESPONSE_UNLOCK_FAILED; + return OCPP16Constants.OCPP_RESPONSE_UNLOCK_FAILED } await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, connectorId, - OCPP16ChargePointStatus.Available, - ); - return OCPP16Constants.OCPP_RESPONSE_UNLOCKED; + OCPP16ChargePointStatus.Available + ) + return OCPP16Constants.OCPP_RESPONSE_UNLOCKED } - private handleRequestGetConfiguration( + private handleRequestGetConfiguration ( chargingStation: ChargingStation, - commandPayload: GetConfigurationRequest, + commandPayload: GetConfigurationRequest ): GetConfigurationResponse { - const { key } = commandPayload; - const configurationKey: OCPPConfigurationKey[] = []; - const unknownKey: string[] = []; - if (isUndefined(key) === true) { - for (const configuration of chargingStation.ocppConfiguration!.configurationKey!) { - if (isUndefined(configuration.visible) === true) { - configuration.visible = true; - } - if (configuration.visible === false) { - continue; + const { key } = commandPayload + const configurationKey: OCPPConfigurationKey[] = [] + const unknownKey: string[] = [] + if (key == null) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + for (const configKey of chargingStation.ocppConfiguration!.configurationKey!) { + if (!OCPP16ServiceUtils.isConfigurationKeyVisible(configKey)) { + continue } configurationKey.push({ - key: configuration.key, - readonly: configuration.readonly, - value: configuration.value, - }); + key: configKey.key, + readonly: configKey.readonly, + value: configKey.value + }) } - } else if (isNotEmptyArray(key) === true) { - for (const k of key!) { - const keyFound = getConfigurationKey(chargingStation, k, true); - if (keyFound) { - if (isUndefined(keyFound.visible) === true) { - keyFound.visible = true; - } - if (keyFound.visible === false) { - continue; + } else if (isNotEmptyArray(key)) { + for (const k of key) { + const keyFound = getConfigurationKey(chargingStation, k, true) + if (keyFound != null) { + if (!OCPP16ServiceUtils.isConfigurationKeyVisible(keyFound)) { + continue } configurationKey.push({ key: keyFound.key, readonly: keyFound.readonly, - value: keyFound.value, - }); + value: keyFound.value + }) } else { - unknownKey.push(k); + unknownKey.push(k) } } } return { configurationKey, - unknownKey, - }; + unknownKey + } } - private handleRequestChangeConfiguration( + private handleRequestChangeConfiguration ( chargingStation: ChargingStation, - commandPayload: ChangeConfigurationRequest, + commandPayload: ChangeConfigurationRequest ): ChangeConfigurationResponse { - const { key, value } = commandPayload; - const keyToChange = getConfigurationKey(chargingStation, key, true); + const { key, value } = commandPayload + const keyToChange = getConfigurationKey(chargingStation, key, true) if (keyToChange?.readonly === true) { - return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_REJECTED } else if (keyToChange?.readonly === false) { - let valueChanged = false; + let valueChanged = false if (keyToChange.value !== value) { - setConfigurationKeyValue(chargingStation, key, value, true); - valueChanged = true; + setConfigurationKeyValue(chargingStation, key, value, true) + valueChanged = true } - let triggerHeartbeatRestart = false; + let triggerHeartbeatRestart = false if ( (keyToChange.key as OCPP16StandardParametersKey) === OCPP16StandardParametersKey.HeartBeatInterval && @@ -577,9 +558,9 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { setConfigurationKeyValue( chargingStation, OCPP16StandardParametersKey.HeartbeatInterval, - value, - ); - triggerHeartbeatRestart = true; + value + ) + triggerHeartbeatRestart = true } if ( (keyToChange.key as OCPP16StandardParametersKey) === @@ -589,366 +570,256 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { setConfigurationKeyValue( chargingStation, OCPP16StandardParametersKey.HeartBeatInterval, - value, - ); - triggerHeartbeatRestart = true; + value + ) + triggerHeartbeatRestart = true } if (triggerHeartbeatRestart) { - chargingStation.restartHeartbeat(); + chargingStation.restartHeartbeat() } if ( (keyToChange.key as OCPP16StandardParametersKey) === OCPP16StandardParametersKey.WebSocketPingInterval && valueChanged ) { - chargingStation.restartWebSocketPing(); + chargingStation.restartWebSocketPing() } - if (keyToChange.reboot) { - return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_REBOOT_REQUIRED; + if (keyToChange.reboot === true) { + return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_REBOOT_REQUIRED } - return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_ACCEPTED; + return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_ACCEPTED } - return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_NOT_SUPPORTED; + return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_NOT_SUPPORTED } - private handleRequestSetChargingProfile( + private handleRequestSetChargingProfile ( chargingStation: ChargingStation, - commandPayload: SetChargingProfileRequest, + commandPayload: SetChargingProfileRequest ): SetChargingProfileResponse { if ( - OCPP16ServiceUtils.checkFeatureProfile( + !OCPP16ServiceUtils.checkFeatureProfile( chargingStation, OCPP16SupportedFeatureProfiles.SmartCharging, - OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE, - ) === false + OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE + ) ) { - return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_NOT_SUPPORTED; + return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_NOT_SUPPORTED } - const { connectorId, csChargingProfiles } = commandPayload; - if (chargingStation.hasConnector(connectorId) === false) { + const { connectorId, csChargingProfiles } = commandPayload + if (!chargingStation.hasConnector(connectorId)) { logger.error( - `${chargingStation.logPrefix()} Trying to set charging profile(s) to a - non existing connector id ${connectorId}`, - ); - return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED; + `${chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector id ${connectorId}` + ) + return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED } if ( csChargingProfiles.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.CHARGE_POINT_MAX_PROFILE && connectorId !== 0 ) { - return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED + } + if ( + csChargingProfiles.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.TX_PROFILE && + connectorId === 0 + ) { + logger.error( + `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${connectorId}` + ) + return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED + } + const connectorStatus = chargingStation.getConnectorStatus(connectorId) + if ( + csChargingProfiles.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.TX_PROFILE && + connectorId > 0 && + connectorStatus?.transactionStarted === false + ) { + logger.error( + `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${connectorId} without a started transaction` + ) + return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED } if ( csChargingProfiles.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.TX_PROFILE && - (connectorId === 0 || - chargingStation.getConnectorStatus(connectorId)?.transactionStarted === false) + connectorId > 0 && + connectorStatus?.transactionStarted === true && + csChargingProfiles.transactionId !== connectorStatus.transactionId ) { logger.error( - `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) - on connector ${connectorId} without a started transaction`, - ); - return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED; + `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${connectorId} with a different transaction id ${ + csChargingProfiles.transactionId + } than the started transaction id ${connectorStatus.transactionId}` + ) + return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED } - OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, csChargingProfiles); + OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, csChargingProfiles) logger.debug( `${chargingStation.logPrefix()} Charging profile(s) set on connector id ${connectorId}: %j`, - csChargingProfiles, - ); - return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED; + csChargingProfiles + ) + return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED } - private handleRequestGetCompositeSchedule( + private handleRequestGetCompositeSchedule ( chargingStation: ChargingStation, - commandPayload: OCPP16GetCompositeScheduleRequest, + commandPayload: OCPP16GetCompositeScheduleRequest ): OCPP16GetCompositeScheduleResponse { if ( - OCPP16ServiceUtils.checkFeatureProfile( + !OCPP16ServiceUtils.checkFeatureProfile( chargingStation, OCPP16SupportedFeatureProfiles.SmartCharging, - OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE, - ) === false + OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE + ) ) { - return OCPP16Constants.OCPP_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_RESPONSE_REJECTED } - const { connectorId, duration, chargingRateUnit } = commandPayload; - if (chargingStation.hasConnector(connectorId) === false) { + const { connectorId, duration, chargingRateUnit } = commandPayload + if (!chargingStation.hasConnector(connectorId)) { logger.error( - `${chargingStation.logPrefix()} Trying to get composite schedule to a - non existing connector id ${connectorId}`, - ); - return OCPP16Constants.OCPP_RESPONSE_REJECTED; + `${chargingStation.logPrefix()} Trying to get composite schedule to a non existing connector id ${connectorId}` + ) + return OCPP16Constants.OCPP_RESPONSE_REJECTED } if (connectorId === 0) { logger.error( - `${chargingStation.logPrefix()} Get composite schedule on connector id ${connectorId} is not yet supported`, - ); - return OCPP16Constants.OCPP_RESPONSE_REJECTED; + `${chargingStation.logPrefix()} Get composite schedule on connector id ${connectorId} is not yet supported` + ) + return OCPP16Constants.OCPP_RESPONSE_REJECTED } - if (chargingRateUnit) { + if (chargingRateUnit != null) { logger.warn( - `${chargingStation.logPrefix()} Get composite schedule with a specified rate unit is not yet supported, no conversion will be done`, - ); + `${chargingStation.logPrefix()} Get composite schedule with a specified rate unit is not yet supported, no conversion will be done` + ) } - const connectorStatus = chargingStation.getConnectorStatus(connectorId)!; + const connectorStatus = chargingStation.getConnectorStatus(connectorId) if ( - isEmptyArray( - connectorStatus?.chargingProfiles && - isEmptyArray(chargingStation.getConnectorStatus(0)?.chargingProfiles), - ) + isEmptyArray(connectorStatus?.chargingProfiles) && + isEmptyArray(chargingStation.getConnectorStatus(0)?.chargingProfiles) ) { - return OCPP16Constants.OCPP_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_RESPONSE_REJECTED } - const currentDate = new Date(); - const interval: Interval = { + const currentDate = new Date() + const compositeScheduleInterval: Interval = { start: currentDate, - end: addSeconds(currentDate, duration), - }; - const storedChargingProfiles: OCPP16ChargingProfile[] = cloneObject( - (connectorStatus?.chargingProfiles ?? []).concat( - chargingStation.getConnectorStatus(0)?.chargingProfiles ?? [], - ), - ).sort((a, b) => b.stackLevel - a.stackLevel); - const chargingProfiles: OCPP16ChargingProfile[] = []; - for (const storedChargingProfile of storedChargingProfiles) { - if ( - connectorStatus?.transactionStarted && - isNullOrUndefined(storedChargingProfile.chargingSchedule?.startSchedule) - ) { + end: addSeconds(currentDate, duration) + } + // Get charging profiles sorted by connector id then stack level + const chargingProfiles: OCPP16ChargingProfile[] = getConnectorChargingProfiles( + chargingStation, + connectorId + ) + let previousCompositeSchedule: OCPP16ChargingSchedule | undefined + let compositeSchedule: OCPP16ChargingSchedule | undefined + for (const chargingProfile of chargingProfiles) { + if (chargingProfile.chargingSchedule.startSchedule == null) { logger.debug( `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${ - storedChargingProfile.chargingProfileId - } has no startSchedule defined. Trying to set it to the connector current transaction start date`, - ); + 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 - storedChargingProfile.chargingSchedule.startSchedule = connectorStatus?.transactionStart; + chargingProfile.chargingSchedule.startSchedule = connectorStatus?.transactionStart } - if (!isDate(storedChargingProfile.chargingSchedule?.startSchedule)) { + if (!isDate(chargingProfile.chargingSchedule.startSchedule)) { logger.warn( `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${ - storedChargingProfile.chargingProfileId - } startSchedule property is not a Date object. Trying to convert it to a Date object`, - ); - storedChargingProfile.chargingSchedule.startSchedule = convertToDate( - storedChargingProfile.chargingSchedule?.startSchedule, - )!; + chargingProfile.chargingProfileId + } startSchedule property is not a Date instance. Trying to convert it to a Date instance` + ) + chargingProfile.chargingSchedule.startSchedule = convertToDate( + chargingProfile.chargingSchedule.startSchedule + ) + } + if (chargingProfile.chargingSchedule.duration == null) { + logger.debug( + `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${ + chargingProfile.chargingProfileId + } has no duration defined and will be set to the maximum time allowed` + ) + // OCPP specifies that if duration is not defined, it should be infinite + chargingProfile.chargingSchedule.duration = differenceInSeconds( + maxTime, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingProfile.chargingSchedule.startSchedule! + ) } if ( !prepareChargingProfileKind( connectorStatus, - storedChargingProfile, - interval.start as Date, - chargingStation.logPrefix(), + chargingProfile, + compositeScheduleInterval.start, + chargingStation.logPrefix() ) ) { - continue; + continue } if ( !canProceedChargingProfile( - storedChargingProfile, - interval.start as Date, - chargingStation.logPrefix(), + chargingProfile, + compositeScheduleInterval.start, + chargingStation.logPrefix() ) ) { - continue; + continue } - // Add active charging profiles into chargingProfiles array - if ( - isValidTime(storedChargingProfile.chargingSchedule?.startSchedule) && - isWithinInterval(storedChargingProfile.chargingSchedule.startSchedule!, interval) - ) { - if (isEmptyArray(chargingProfiles)) { - if ( - isAfter( - addSeconds( - storedChargingProfile.chargingSchedule.startSchedule!, - storedChargingProfile.chargingSchedule.duration!, - ), - interval.end, - ) - ) { - storedChargingProfile.chargingSchedule.chargingSchedulePeriod = - storedChargingProfile.chargingSchedule.chargingSchedulePeriod.filter( - (schedulePeriod) => - isWithinInterval( - addSeconds( - storedChargingProfile.chargingSchedule.startSchedule!, - schedulePeriod.startPeriod, - )!, - interval, - ), - ); - storedChargingProfile.chargingSchedule.duration = differenceInSeconds( - interval.end, - storedChargingProfile.chargingSchedule.startSchedule!, - ); - } - chargingProfiles.push(storedChargingProfile); - } else if (isNotEmptyArray(chargingProfiles)) { - const chargingProfilesInterval: Interval = { - start: min( - chargingProfiles.map( - (chargingProfile) => chargingProfile.chargingSchedule.startSchedule ?? maxTime, - ), - ), - end: max( - chargingProfiles.map( - (chargingProfile) => - addSeconds( - chargingProfile.chargingSchedule.startSchedule!, - chargingProfile.chargingSchedule.duration!, - ) ?? minTime, - ), - ), - }; - let addChargingProfile = false; - if ( - isBefore(interval.start, chargingProfilesInterval.start) && - isBefore( - storedChargingProfile.chargingSchedule.startSchedule!, - chargingProfilesInterval.start, - ) - ) { - // Remove charging schedule periods that are after the start of the active profiles interval - storedChargingProfile.chargingSchedule.chargingSchedulePeriod = - storedChargingProfile.chargingSchedule.chargingSchedulePeriod.filter( - (schedulePeriod) => - isWithinInterval( - addSeconds( - storedChargingProfile.chargingSchedule.startSchedule!, - schedulePeriod.startPeriod, - ), - { - start: interval.start, - end: chargingProfilesInterval.start, - }, - ), - ); - addChargingProfile = true; - } - if ( - isBefore(chargingProfilesInterval.end, interval.end) && - isAfter( - addSeconds( - storedChargingProfile.chargingSchedule.startSchedule!, - storedChargingProfile.chargingSchedule.duration!, - ), - chargingProfilesInterval.end, - ) - ) { - // Remove charging schedule periods that are before the end of the active profiles interval - // FIXME: can lead to a gap in the charging schedule: chargingProfilesInterval.end -> first matching schedulePeriod.startPeriod - storedChargingProfile.chargingSchedule.chargingSchedulePeriod = - storedChargingProfile.chargingSchedule.chargingSchedulePeriod.filter( - (schedulePeriod) => - isWithinInterval( - addSeconds( - storedChargingProfile.chargingSchedule.startSchedule!, - schedulePeriod.startPeriod, - ), - { - start: chargingProfilesInterval.end, - end: interval.end, - }, - ), - ); - addChargingProfile = true; - } - addChargingProfile && chargingProfiles.push(storedChargingProfile); - } + compositeSchedule = OCPP16ServiceUtils.composeChargingSchedules( + previousCompositeSchedule, + chargingProfile.chargingSchedule, + compositeScheduleInterval + ) + previousCompositeSchedule = compositeSchedule + } + if (compositeSchedule != null) { + return { + status: GenericStatus.Accepted, + scheduleStart: compositeSchedule.startSchedule, + connectorId, + chargingSchedule: compositeSchedule } } - const compositeScheduleStart: Date = min( - chargingProfiles.map( - (chargingProfile) => chargingProfile.chargingSchedule.startSchedule ?? maxTime, - ), - ); - const compositeScheduleDuration: number = Math.max( - ...chargingProfiles.map((chargingProfile) => - isNaN(chargingProfile.chargingSchedule.duration!) - ? -Infinity - : chargingProfile.chargingSchedule.duration!, - ), - ); - const compositeSchedulePeriods: OCPP16ChargingSchedulePeriod[] = chargingProfiles - .map((chargingProfile) => chargingProfile.chargingSchedule.chargingSchedulePeriod) - .reduce( - (accumulator, value) => - accumulator.concat(value).sort((a, b) => a.startPeriod - b.startPeriod), - [], - ); - const compositeSchedule: OCPP16ChargingSchedule = { - startSchedule: compositeScheduleStart, - duration: compositeScheduleDuration, - chargingRateUnit: chargingProfiles.every( - (chargingProfile) => - chargingProfile.chargingSchedule.chargingRateUnit === OCPP16ChargingRateUnitType.AMPERE, - ) - ? OCPP16ChargingRateUnitType.AMPERE - : chargingProfiles.every( - (chargingProfile) => - chargingProfile.chargingSchedule.chargingRateUnit === OCPP16ChargingRateUnitType.WATT, - ) - ? OCPP16ChargingRateUnitType.WATT - : OCPP16ChargingRateUnitType.AMPERE, - chargingSchedulePeriod: compositeSchedulePeriods, - minChargeRate: Math.min( - ...chargingProfiles.map((chargingProfile) => - isNaN(chargingProfile.chargingSchedule.minChargeRate!) - ? Infinity - : chargingProfile.chargingSchedule.minChargeRate!, - ), - ), - }; - return { - status: GenericStatus.Accepted, - scheduleStart: compositeSchedule.startSchedule!, - connectorId, - chargingSchedule: compositeSchedule, - }; + return OCPP16Constants.OCPP_RESPONSE_REJECTED } - private handleRequestClearChargingProfile( + private handleRequestClearChargingProfile ( chargingStation: ChargingStation, - commandPayload: ClearChargingProfileRequest, - ): ClearChargingProfileResponse { + commandPayload: OCPP16ClearChargingProfileRequest + ): OCPP16ClearChargingProfileResponse { if ( - OCPP16ServiceUtils.checkFeatureProfile( + !OCPP16ServiceUtils.checkFeatureProfile( chargingStation, OCPP16SupportedFeatureProfiles.SmartCharging, - OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE, - ) === false + OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE + ) ) { - return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN; + return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN } - const { connectorId } = commandPayload; - if (chargingStation.hasConnector(connectorId!) === false) { + const { connectorId } = commandPayload + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + if (!chargingStation.hasConnector(connectorId!)) { logger.error( - `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to - a non existing connector id ${connectorId}`, - ); - return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN; + `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector id ${connectorId}` + ) + return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN } - if ( - !isNullOrUndefined(connectorId) && - isNotEmptyArray(chargingStation.getConnectorStatus(connectorId!)?.chargingProfiles) - ) { - chargingStation.getConnectorStatus(connectorId!)!.chargingProfiles = []; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const connectorStatus = chargingStation.getConnectorStatus(connectorId!) + if (connectorId != null && isNotEmptyArray(connectorStatus?.chargingProfiles)) { + connectorStatus.chargingProfiles = [] logger.debug( - `${chargingStation.logPrefix()} Charging profile(s) cleared on connector id ${connectorId}`, - ); - return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED; + `${chargingStation.logPrefix()} Charging profile(s) cleared on connector id ${connectorId}` + ) + return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED } - if (isNullOrUndefined(connectorId)) { - let clearedCP = false; + if (connectorId == null) { + let clearedCP = false if (chargingStation.hasEvses) { for (const evseStatus of chargingStation.evses.values()) { - for (const connectorStatus of evseStatus.connectors.values()) { + for (const status of evseStatus.connectors.values()) { clearedCP = OCPP16ServiceUtils.clearChargingProfiles( chargingStation, commandPayload, - connectorStatus.chargingProfiles, - ); + status.chargingProfiles + ) } } } else { @@ -956,246 +827,240 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { clearedCP = OCPP16ServiceUtils.clearChargingProfiles( chargingStation, commandPayload, - chargingStation.getConnectorStatus(id)?.chargingProfiles, - ); + chargingStation.getConnectorStatus(id)?.chargingProfiles + ) } } if (clearedCP) { - return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED; + return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED } } - return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN; + return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN } - private async handleRequestChangeAvailability( + private async handleRequestChangeAvailability ( chargingStation: ChargingStation, - commandPayload: OCPP16ChangeAvailabilityRequest, + commandPayload: OCPP16ChangeAvailabilityRequest ): Promise { - const { connectorId, type } = commandPayload; - if (chargingStation.hasConnector(connectorId) === false) { + const { connectorId, type } = commandPayload + if (!chargingStation.hasConnector(connectorId)) { logger.error( - `${chargingStation.logPrefix()} Trying to change the availability of a - non existing connector id ${connectorId.toString()}`, - ); - return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED; + `${chargingStation.logPrefix()} Trying to change the availability of a non existing connector id ${connectorId}` + ) + return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED } const chargePointStatus: OCPP16ChargePointStatus = type === OCPP16AvailabilityType.Operative ? OCPP16ChargePointStatus.Available - : OCPP16ChargePointStatus.Unavailable; + : 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( chargingStation, [...evseStatus.connectors.keys()], chargePointStatus, - type, - ); + type + ) } } else { response = await OCPP16ServiceUtils.changeAvailability( chargingStation, [...chargingStation.connectors.keys()], chargePointStatus, - type, - ); + type + ) } - return response!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return response! } else if ( connectorId > 0 && - (chargingStation.isChargingStationAvailable() === true || - (chargingStation.isChargingStationAvailable() === false && + (chargingStation.isChargingStationAvailable() || + (!chargingStation.isChargingStationAvailable() && type === OCPP16AvailabilityType.Inoperative)) ) { if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) { - chargingStation.getConnectorStatus(connectorId)!.availability = type; - return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.getConnectorStatus(connectorId)!.availability = type + return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED } - chargingStation.getConnectorStatus(connectorId)!.availability = type; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.getConnectorStatus(connectorId)!.availability = type await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, connectorId, - chargePointStatus, - ); - return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED; + chargePointStatus + ) + return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED } - return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED } - private async handleRequestRemoteStartTransaction( + private async handleRequestRemoteStartTransaction ( chargingStation: ChargingStation, - commandPayload: RemoteStartTransactionRequest, + commandPayload: RemoteStartTransactionRequest ): Promise { - const { connectorId: transactionConnectorId, idTag, chargingProfile } = commandPayload; - if (chargingStation.hasConnector(transactionConnectorId) === false) { - return this.notifyRemoteStartTransactionRejected( + const { connectorId: transactionConnectorId, idTag, chargingProfile } = commandPayload + if (!chargingStation.hasConnector(transactionConnectorId)) { + return await this.notifyRemoteStartTransactionRejected( chargingStation, transactionConnectorId, - idTag, - ); + idTag + ) } if ( !chargingStation.isChargingStationAvailable() || !chargingStation.isConnectorAvailable(transactionConnectorId) ) { - return this.notifyRemoteStartTransactionRejected( + return await this.notifyRemoteStartTransactionRejected( chargingStation, transactionConnectorId, - idTag, - ); + idTag + ) } const remoteStartTransactionLogMsg = ` ${chargingStation.logPrefix()} Transaction remotely STARTED on ${ - chargingStation.stationInfo.chargingStationId - }#${transactionConnectorId.toString()} for idTag '${idTag}'`; + chargingStation.stationInfo?.chargingStationId + }#${transactionConnectorId} for idTag '${idTag}'` await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, transactionConnectorId, - OCPP16ChargePointStatus.Preparing, - ); - const connectorStatus = chargingStation.getConnectorStatus(transactionConnectorId)!; + OCPP16ChargePointStatus.Preparing + ) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const connectorStatus = chargingStation.getConnectorStatus(transactionConnectorId)! // Authorization check required if ( - chargingStation.getAuthorizeRemoteTxRequests() === true && + chargingStation.getAuthorizeRemoteTxRequests() && (await OCPP16ServiceUtils.isIdTagAuthorized(chargingStation, transactionConnectorId, idTag)) ) { // Authorization successful, start transaction if ( - this.setRemoteStartTransactionChargingProfile( - chargingStation, - transactionConnectorId, - chargingProfile!, - ) === true + (chargingProfile != null && + this.setRemoteStartTransactionChargingProfile( + chargingStation, + transactionConnectorId, + chargingProfile + )) || + chargingProfile == null ) { - connectorStatus.transactionRemoteStarted = true; + connectorStatus.transactionRemoteStarted = true if ( ( await chargingStation.ocppRequestService.requestHandler< - OCPP16StartTransactionRequest, - OCPP16StartTransactionResponse + OCPP16StartTransactionRequest, + OCPP16StartTransactionResponse >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, { connectorId: transactionConnectorId, - idTag, + idTag }) ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED ) { - logger.debug(remoteStartTransactionLogMsg); - return OCPP16Constants.OCPP_RESPONSE_ACCEPTED; + logger.debug(remoteStartTransactionLogMsg) + return OCPP16Constants.OCPP_RESPONSE_ACCEPTED } - return this.notifyRemoteStartTransactionRejected( + return await this.notifyRemoteStartTransactionRejected( chargingStation, transactionConnectorId, - idTag, - ); + idTag + ) } - return this.notifyRemoteStartTransactionRejected( + return await this.notifyRemoteStartTransactionRejected( chargingStation, transactionConnectorId, - idTag, - ); + idTag + ) } // No authorization check required, start transaction if ( - this.setRemoteStartTransactionChargingProfile( - chargingStation, - transactionConnectorId, - chargingProfile!, - ) === true + (chargingProfile != null && + this.setRemoteStartTransactionChargingProfile( + chargingStation, + transactionConnectorId, + chargingProfile + )) || + chargingProfile == null ) { - connectorStatus.transactionRemoteStarted = true; + connectorStatus.transactionRemoteStarted = true if ( ( await chargingStation.ocppRequestService.requestHandler< - OCPP16StartTransactionRequest, - OCPP16StartTransactionResponse + OCPP16StartTransactionRequest, + OCPP16StartTransactionResponse >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, { connectorId: transactionConnectorId, - idTag, + idTag }) ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED ) { - logger.debug(remoteStartTransactionLogMsg); - return OCPP16Constants.OCPP_RESPONSE_ACCEPTED; + logger.debug(remoteStartTransactionLogMsg) + return OCPP16Constants.OCPP_RESPONSE_ACCEPTED } - return this.notifyRemoteStartTransactionRejected( + return await this.notifyRemoteStartTransactionRejected( chargingStation, transactionConnectorId, - idTag, - ); + idTag + ) } - return this.notifyRemoteStartTransactionRejected( + return await this.notifyRemoteStartTransactionRejected( chargingStation, transactionConnectorId, - idTag, - ); + idTag + ) } - private async notifyRemoteStartTransactionRejected( + private async notifyRemoteStartTransactionRejected ( chargingStation: ChargingStation, connectorId: number, - idTag: string, + idTag: string ): Promise { - if ( - chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.Available - ) { + const connectorStatus = chargingStation.getConnectorStatus(connectorId) + if (connectorStatus?.status !== OCPP16ChargePointStatus.Available) { await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, connectorId, - OCPP16ChargePointStatus.Available, - ); + OCPP16ChargePointStatus.Available + ) } logger.warn( - `${chargingStation.logPrefix()} Remote starting transaction REJECTED on connector id - ${connectorId.toString()}, idTag '${idTag}', availability '${chargingStation.getConnectorStatus( - connectorId, - )?.availability}', status '${chargingStation.getConnectorStatus(connectorId)?.status}'`, - ); - return OCPP16Constants.OCPP_RESPONSE_REJECTED; + `${chargingStation.logPrefix()} Remote starting transaction REJECTED on connector id ${connectorId}, idTag '${idTag}', availability '${connectorStatus?.availability}', status '${connectorStatus?.status}'` + ) + return OCPP16Constants.OCPP_RESPONSE_REJECTED } - private setRemoteStartTransactionChargingProfile( + private setRemoteStartTransactionChargingProfile ( chargingStation: ChargingStation, connectorId: number, - chargingProfile: OCPP16ChargingProfile, + chargingProfile: OCPP16ChargingProfile ): boolean { - if ( - chargingProfile && - chargingProfile.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.TX_PROFILE - ) { - OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, chargingProfile); + if (chargingProfile.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.TX_PROFILE) { + OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, chargingProfile) logger.debug( - `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction - on connector id ${connectorId}: %j`, - chargingProfile, - ); - return true; - } else if ( - chargingProfile && - chargingProfile.chargingProfilePurpose !== OCPP16ChargingProfilePurposeType.TX_PROFILE - ) { - logger.warn( - `${chargingStation.logPrefix()} Not allowed to set ${ - chargingProfile.chargingProfilePurpose - } charging profile(s) at remote start transaction`, - ); - return false; + `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction on connector id ${connectorId}: %j`, + chargingProfile + ) + return true } - return true; + logger.warn( + `${chargingStation.logPrefix()} Not allowed to set ${ + chargingProfile.chargingProfilePurpose + } charging profile(s) at remote start transaction` + ) + return false } - private async handleRequestRemoteStopTransaction( + private async handleRequestRemoteStopTransaction ( chargingStation: ChargingStation, - commandPayload: RemoteStopTransactionRequest, + commandPayload: RemoteStopTransactionRequest ): Promise { - const { transactionId } = commandPayload; + const { transactionId } = commandPayload if (chargingStation.hasEvses) { for (const [evseId, evseStatus] of chargingStation.evses) { if (evseId > 0) { for (const [connectorId, connectorStatus] of evseStatus.connectors) { if (connectorStatus.transactionId === transactionId) { - return OCPP16ServiceUtils.remoteStopTransaction(chargingStation, connectorId); + return await OCPP16ServiceUtils.remoteStopTransaction(chargingStation, connectorId) } } } @@ -1206,92 +1071,70 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { connectorId > 0 && chargingStation.getConnectorStatus(connectorId)?.transactionId === transactionId ) { - return OCPP16ServiceUtils.remoteStopTransaction(chargingStation, connectorId); + return await OCPP16ServiceUtils.remoteStopTransaction(chargingStation, connectorId) } } } logger.warn( - `${chargingStation.logPrefix()} Trying to remote stop a non existing transaction with id - ${transactionId.toString()}`, - ); - return OCPP16Constants.OCPP_RESPONSE_REJECTED; + `${chargingStation.logPrefix()} Trying to remote stop a non existing transaction with id ${transactionId}` + ) + return OCPP16Constants.OCPP_RESPONSE_REJECTED } - private handleRequestUpdateFirmware( + private handleRequestUpdateFirmware ( chargingStation: ChargingStation, - commandPayload: OCPP16UpdateFirmwareRequest, + commandPayload: OCPP16UpdateFirmwareRequest ): OCPP16UpdateFirmwareResponse { if ( - OCPP16ServiceUtils.checkFeatureProfile( + !OCPP16ServiceUtils.checkFeatureProfile( chargingStation, OCPP16SupportedFeatureProfiles.FirmwareManagement, - OCPP16IncomingRequestCommand.UPDATE_FIRMWARE, - ) === false + OCPP16IncomingRequestCommand.UPDATE_FIRMWARE + ) ) { logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: - Cannot simulate firmware update: feature profile not supported`, - ); - return OCPP16Constants.OCPP_RESPONSE_EMPTY; + `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: feature profile not supported` + ) + return OCPP16Constants.OCPP_RESPONSE_EMPTY } - let { retrieveDate } = commandPayload; - if ( - !isNullOrUndefined(chargingStation.stationInfo.firmwareStatus) && - chargingStation.stationInfo.firmwareStatus !== OCPP16FirmwareStatus.Installed - ) { + // 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; + `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: firmware update is already in progress` + ) + return OCPP16Constants.OCPP_RESPONSE_EMPTY } - retrieveDate = convertToDate(retrieveDate)!; - const now = Date.now(); - if (retrieveDate?.getTime() <= now) { - this.runInAsyncScope( - this.updateFirmwareSimulation.bind(this) as ( - this: OCPP16IncomingRequestService, - ...args: unknown[] - ) => Promise, - this, - chargingStation, - ).catch(Constants.EMPTY_FUNCTION); + const now = Date.now() + if (retrieveDate.getTime() <= now) { + this.updateFirmwareSimulation(chargingStation).catch(Constants.EMPTY_FUNCTION) } else { - setTimeout( - () => { - this.runInAsyncScope( - this.updateFirmwareSimulation.bind(this) as ( - this: OCPP16IncomingRequestService, - ...args: unknown[] - ) => Promise, - this, - chargingStation, - ).catch(Constants.EMPTY_FUNCTION); - }, - retrieveDate?.getTime() - now, - ); + setTimeout(() => { + this.updateFirmwareSimulation(chargingStation).catch(Constants.EMPTY_FUNCTION) + }, retrieveDate.getTime() - now) } - return OCPP16Constants.OCPP_RESPONSE_EMPTY; + return OCPP16Constants.OCPP_RESPONSE_EMPTY } - private async updateFirmwareSimulation( + private async updateFirmwareSimulation ( chargingStation: ChargingStation, maxDelay = 30, - minDelay = 15, + minDelay = 15 ): Promise { - if (checkChargingStation(chargingStation, chargingStation.logPrefix()) === false) { - return; + if (!checkChargingStation(chargingStation, chargingStation.logPrefix())) { + return } if (chargingStation.hasEvses) { for (const [evseId, evseStatus] of chargingStation.evses) { if (evseId > 0) { for (const [connectorId, connectorStatus] of evseStatus.connectors) { - if (connectorStatus?.transactionStarted === false) { + if (connectorStatus.transactionStarted === false) { await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, connectorId, - OCPP16ChargePointStatus.Unavailable, - ); + OCPP16ChargePointStatus.Unavailable + ) } } } @@ -1305,67 +1148,68 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, connectorId, - OCPP16ChargePointStatus.Unavailable, - ); + OCPP16ChargePointStatus.Unavailable + ) } } } await chargingStation.ocppRequestService.requestHandler< - OCPP16FirmwareStatusNotificationRequest, - OCPP16FirmwareStatusNotificationResponse + OCPP16FirmwareStatusNotificationRequest, + OCPP16FirmwareStatusNotificationResponse >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, { - status: OCPP16FirmwareStatus.Downloading, - }); - chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloading; + status: OCPP16FirmwareStatus.Downloading + }) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.stationInfo!.firmwareStatus = OCPP16FirmwareStatus.Downloading if ( chargingStation.stationInfo?.firmwareUpgrade?.failureStatus === OCPP16FirmwareStatus.DownloadFailed ) { - await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay))); + await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay))) await chargingStation.ocppRequestService.requestHandler< - OCPP16FirmwareStatusNotificationRequest, - OCPP16FirmwareStatusNotificationResponse + OCPP16FirmwareStatusNotificationRequest, + OCPP16FirmwareStatusNotificationResponse >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, { - status: chargingStation.stationInfo?.firmwareUpgrade?.failureStatus, - }); + status: chargingStation.stationInfo.firmwareUpgrade.failureStatus + }) chargingStation.stationInfo.firmwareStatus = - chargingStation.stationInfo?.firmwareUpgrade?.failureStatus; - return; + chargingStation.stationInfo.firmwareUpgrade.failureStatus + return } - await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay))); + await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay))) await chargingStation.ocppRequestService.requestHandler< - OCPP16FirmwareStatusNotificationRequest, - OCPP16FirmwareStatusNotificationResponse + OCPP16FirmwareStatusNotificationRequest, + OCPP16FirmwareStatusNotificationResponse >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, { - status: OCPP16FirmwareStatus.Downloaded, - }); - chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloaded; - let wasTransactionsStarted = false; - let transactionsStarted: boolean; + status: OCPP16FirmwareStatus.Downloaded + }) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.stationInfo!.firmwareStatus = OCPP16FirmwareStatus.Downloaded + let wasTransactionsStarted = false + let transactionsStarted: boolean do { - const runningTransactions = chargingStation.getNumberOfRunningTransactions(); + const runningTransactions = chargingStation.getNumberOfRunningTransactions() if (runningTransactions > 0) { - const waitTime = secondsToMilliseconds(15); + const waitTime = secondsToMilliseconds(15) logger.debug( - `${chargingStation.logPrefix()} ${moduleName}.updateFirmwareSimulation: - ${runningTransactions} transaction(s) in progress, waiting ${formatDurationMilliSeconds( - waitTime, - )} before continuing firmware update simulation`, - ); - await sleep(waitTime); - transactionsStarted = true; - wasTransactionsStarted = true; + `${chargingStation.logPrefix()} ${moduleName}.updateFirmwareSimulation: ${runningTransactions} transaction(s) in progress, waiting ${formatDurationMilliSeconds( + waitTime + )} before continuing firmware update simulation` + ) + await sleep(waitTime) + transactionsStarted = true + wasTransactionsStarted = true } else { if (chargingStation.hasEvses) { for (const [evseId, evseStatus] of chargingStation.evses) { if (evseId > 0) { for (const [connectorId, connectorStatus] of evseStatus.connectors) { - if (connectorStatus?.status !== OCPP16ChargePointStatus.Unavailable) { + if (connectorStatus.status !== OCPP16ChargePointStatus.Unavailable) { await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, connectorId, - OCPP16ChargePointStatus.Unavailable, - ); + OCPP16ChargePointStatus.Unavailable + ) } } } @@ -1380,191 +1224,186 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, connectorId, - OCPP16ChargePointStatus.Unavailable, - ); + OCPP16ChargePointStatus.Unavailable + ) } } } - transactionsStarted = false; + transactionsStarted = false } - } while (transactionsStarted); + } while (transactionsStarted) !wasTransactionsStarted && - (await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay)))); - if (checkChargingStation(chargingStation, chargingStation.logPrefix()) === false) { - return; + (await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay)))) + if (!checkChargingStation(chargingStation, chargingStation.logPrefix())) { + return } await chargingStation.ocppRequestService.requestHandler< - OCPP16FirmwareStatusNotificationRequest, - OCPP16FirmwareStatusNotificationResponse + OCPP16FirmwareStatusNotificationRequest, + OCPP16FirmwareStatusNotificationResponse >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, { - status: OCPP16FirmwareStatus.Installing, - }); - chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Installing; + status: OCPP16FirmwareStatus.Installing + }) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.stationInfo!.firmwareStatus = OCPP16FirmwareStatus.Installing if ( chargingStation.stationInfo?.firmwareUpgrade?.failureStatus === OCPP16FirmwareStatus.InstallationFailed ) { - await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay))); + await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay))) await chargingStation.ocppRequestService.requestHandler< - OCPP16FirmwareStatusNotificationRequest, - OCPP16FirmwareStatusNotificationResponse + OCPP16FirmwareStatusNotificationRequest, + OCPP16FirmwareStatusNotificationResponse >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, { - status: chargingStation.stationInfo?.firmwareUpgrade?.failureStatus, - }); + status: chargingStation.stationInfo.firmwareUpgrade.failureStatus + }) chargingStation.stationInfo.firmwareStatus = - chargingStation.stationInfo?.firmwareUpgrade?.failureStatus; - return; + chargingStation.stationInfo.firmwareUpgrade.failureStatus + return } if (chargingStation.stationInfo?.firmwareUpgrade?.reset === true) { - await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay))); - await chargingStation.reset(OCPP16StopTransactionReason.REBOOT); + await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay))) + await chargingStation.reset(OCPP16StopTransactionReason.REBOOT) } } - private async handleRequestGetDiagnostics( + private async handleRequestGetDiagnostics ( chargingStation: ChargingStation, - commandPayload: GetDiagnosticsRequest, + commandPayload: GetDiagnosticsRequest ): Promise { if ( - OCPP16ServiceUtils.checkFeatureProfile( + !OCPP16ServiceUtils.checkFeatureProfile( chargingStation, OCPP16SupportedFeatureProfiles.FirmwareManagement, - OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, - ) === false + OCPP16IncomingRequestCommand.GET_DIAGNOSTICS + ) ) { logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: - Cannot get diagnostics: feature profile not supported`, - ); - return OCPP16Constants.OCPP_RESPONSE_EMPTY; + `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Cannot get diagnostics: feature profile not supported` + ) + return OCPP16Constants.OCPP_RESPONSE_EMPTY } - const { location } = commandPayload; - const uri = new URL(location); + const { location } = commandPayload + const uri = new URL(location) if (uri.protocol.startsWith('ftp:')) { - let ftpClient: Client | undefined; + let ftpClient: Client | undefined try { const logFiles = readdirSync(resolve(dirname(fileURLToPath(import.meta.url)), '../')) - .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(); + .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() const accessResponse = await ftpClient.access({ host: uri.host, ...(isNotEmptyString(uri.port) && { port: convertToInt(uri.port) }), ...(isNotEmptyString(uri.username) && { user: uri.username }), - ...(isNotEmptyString(uri.password) && { password: uri.password }), - }); - let uploadResponse: FTPResponse | undefined; + ...(isNotEmptyString(uri.password) && { password: uri.password }) + }) + let uploadResponse: FTPResponse | undefined if (accessResponse.code === 220) { - ftpClient.trackProgress((info) => { + ftpClient.trackProgress(info => { logger.info( `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: ${ info.bytes / 1024 - } bytes transferred from diagnostics archive ${info.name}`, - ); + } bytes transferred from diagnostics archive ${info.name}` + ) chargingStation.ocppRequestService .requestHandler< - OCPP16DiagnosticsStatusNotificationRequest, - OCPP16DiagnosticsStatusNotificationResponse - >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, { - status: OCPP16DiagnosticsStatus.Uploading, - }) - .catch((error) => { + OCPP16DiagnosticsStatusNotificationRequest, + OCPP16DiagnosticsStatusNotificationResponse + >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, { + status: OCPP16DiagnosticsStatus.Uploading + }) + .catch(error => { logger.error( - `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: - Error while sending '${OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION}'`, - error, - ); - }); - }); + `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Error while sending '${ + OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION + }'`, + error + ) + }) + }) uploadResponse = await ftpClient.uploadFrom( join(resolve(dirname(fileURLToPath(import.meta.url)), '../'), diagnosticsArchive), - `${uri.pathname}${diagnosticsArchive}`, - ); + `${uri.pathname}${diagnosticsArchive}` + ) if (uploadResponse.code === 226) { await chargingStation.ocppRequestService.requestHandler< - OCPP16DiagnosticsStatusNotificationRequest, - OCPP16DiagnosticsStatusNotificationResponse + OCPP16DiagnosticsStatusNotificationRequest, + OCPP16DiagnosticsStatusNotificationResponse >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, { - status: OCPP16DiagnosticsStatus.Uploaded, - }); - if (ftpClient) { - ftpClient.close(); - } - return { fileName: diagnosticsArchive }; + status: OCPP16DiagnosticsStatus.Uploaded + }) + ftpClient.close() + return { fileName: diagnosticsArchive } } throw new OCPPError( ErrorType.GENERIC_ERROR, - `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${ - uploadResponse?.code && `|${uploadResponse?.code.toString()}` - }`, - OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, - ); + `Diagnostics transfer failed with error code ${accessResponse.code}|${uploadResponse.code}`, + OCPP16IncomingRequestCommand.GET_DIAGNOSTICS + ) } throw new OCPPError( ErrorType.GENERIC_ERROR, - `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${ - uploadResponse?.code && `|${uploadResponse?.code.toString()}` - }`, - OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, - ); + `Diagnostics transfer failed with error code ${accessResponse.code}|${uploadResponse?.code}`, + OCPP16IncomingRequestCommand.GET_DIAGNOSTICS + ) } catch (error) { await chargingStation.ocppRequestService.requestHandler< - OCPP16DiagnosticsStatusNotificationRequest, - OCPP16DiagnosticsStatusNotificationResponse + OCPP16DiagnosticsStatusNotificationRequest, + OCPP16DiagnosticsStatusNotificationResponse >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, { - status: OCPP16DiagnosticsStatus.UploadFailed, - }); - if (ftpClient) { - ftpClient.close(); - } + status: OCPP16DiagnosticsStatus.UploadFailed + }) + ftpClient?.close() + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return this.handleIncomingRequestError( chargingStation, OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, error as Error, - { errorResponse: OCPP16Constants.OCPP_RESPONSE_EMPTY }, - )!; + { errorResponse: OCPP16Constants.OCPP_RESPONSE_EMPTY } + )! } } else { logger.error( `${chargingStation.logPrefix()} Unsupported protocol ${ uri.protocol - } to transfer the diagnostic logs archive`, - ); + } to transfer the diagnostic logs archive` + ) await chargingStation.ocppRequestService.requestHandler< - OCPP16DiagnosticsStatusNotificationRequest, - OCPP16DiagnosticsStatusNotificationResponse + OCPP16DiagnosticsStatusNotificationRequest, + OCPP16DiagnosticsStatusNotificationResponse >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, { - status: OCPP16DiagnosticsStatus.UploadFailed, - }); - return OCPP16Constants.OCPP_RESPONSE_EMPTY; + status: OCPP16DiagnosticsStatus.UploadFailed + }) + return OCPP16Constants.OCPP_RESPONSE_EMPTY } } - private handleRequestTriggerMessage( + private handleRequestTriggerMessage ( chargingStation: ChargingStation, - commandPayload: OCPP16TriggerMessageRequest, + commandPayload: OCPP16TriggerMessageRequest ): OCPP16TriggerMessageResponse { - const { requestedMessage, connectorId } = commandPayload; + const { requestedMessage, connectorId } = commandPayload if ( !OCPP16ServiceUtils.checkFeatureProfile( chargingStation, OCPP16SupportedFeatureProfiles.RemoteTrigger, - OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, + OCPP16IncomingRequestCommand.TRIGGER_MESSAGE ) || !OCPP16ServiceUtils.isMessageTriggerSupported(chargingStation, requestedMessage) ) { - return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED; + return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED } if ( !OCPP16ServiceUtils.isConnectorIdValid( chargingStation, OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, - connectorId!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + connectorId! ) ) { - return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED } try { switch (requestedMessage) { @@ -1572,234 +1411,237 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { setTimeout(() => { chargingStation.ocppRequestService .requestHandler( - chargingStation, - OCPP16RequestCommand.BOOT_NOTIFICATION, - chargingStation.bootNotificationRequest, - { skipBufferingOnError: true, triggerMessage: true }, - ) - .then((response) => { - chargingStation.bootNotificationResponse = response; + chargingStation, + OCPP16RequestCommand.BOOT_NOTIFICATION, + chargingStation.bootNotificationRequest, + { skipBufferingOnError: true, triggerMessage: true } + ) + .then(response => { + chargingStation.bootNotificationResponse = response }) - .catch(Constants.EMPTY_FUNCTION); - }, OCPP16Constants.OCPP_TRIGGER_MESSAGE_DELAY); - return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED; + .catch(Constants.EMPTY_FUNCTION) + }, OCPP16Constants.OCPP_TRIGGER_MESSAGE_DELAY) + return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED case OCPP16MessageTrigger.Heartbeat: setTimeout(() => { chargingStation.ocppRequestService .requestHandler( - chargingStation, - OCPP16RequestCommand.HEARTBEAT, - null, - { - triggerMessage: true, - }, - ) - .catch(Constants.EMPTY_FUNCTION); - }, OCPP16Constants.OCPP_TRIGGER_MESSAGE_DELAY); - return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED; + chargingStation, + OCPP16RequestCommand.HEARTBEAT, + undefined, + { + triggerMessage: true + } + ) + .catch(Constants.EMPTY_FUNCTION) + }, OCPP16Constants.OCPP_TRIGGER_MESSAGE_DELAY) + return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED case OCPP16MessageTrigger.StatusNotification: setTimeout(() => { - if (!isNullOrUndefined(connectorId)) { + if (connectorId != null) { chargingStation.ocppRequestService .requestHandler( + chargingStation, + OCPP16RequestCommand.STATUS_NOTIFICATION, + { + connectorId, + errorCode: OCPP16ChargePointErrorCode.NO_ERROR, + status: chargingStation.getConnectorStatus(connectorId)?.status + }, + { + triggerMessage: true + } + ) + .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, connectorStatus] of chargingStation.connectors) { + chargingStation.ocppRequestService + .requestHandler< + OCPP16StatusNotificationRequest, + OCPP16StatusNotificationResponse + >( chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, { - connectorId, + connectorId: id, errorCode: OCPP16ChargePointErrorCode.NO_ERROR, - status: chargingStation.getConnectorStatus(connectorId!)?.status, + status: connectorStatus.status }, { - triggerMessage: true, - }, - ) - .catch(Constants.EMPTY_FUNCTION); - } else { - // eslint-disable-next-line no-lonely-if - 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); + triggerMessage: true } - } - } 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); - } + ) + .catch(Constants.EMPTY_FUNCTION) } } - }, OCPP16Constants.OCPP_TRIGGER_MESSAGE_DELAY); - return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED; + }, OCPP16Constants.OCPP_TRIGGER_MESSAGE_DELAY) + return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED default: - return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED; + return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED } } catch (error) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return this.handleIncomingRequestError( chargingStation, OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, error as Error, - { errorResponse: OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED }, - )!; + { errorResponse: OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED } + )! } } - private handleRequestDataTransfer( + private handleRequestDataTransfer ( chargingStation: ChargingStation, - commandPayload: OCPP16DataTransferRequest, + commandPayload: OCPP16DataTransferRequest ): OCPP16DataTransferResponse { - const { vendorId } = commandPayload; + const { vendorId } = commandPayload try { if (Object.values(OCPP16DataTransferVendorId).includes(vendorId)) { - return OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_ACCEPTED; + return OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_ACCEPTED } - return OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_UNKNOWN_VENDOR_ID; + return OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_UNKNOWN_VENDOR_ID } catch (error) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return this.handleIncomingRequestError( chargingStation, OCPP16IncomingRequestCommand.DATA_TRANSFER, error as Error, - { errorResponse: OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_REJECTED }, - )!; + { errorResponse: OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_REJECTED } + )! } } - private async handleRequestReserveNow( + private async handleRequestReserveNow ( chargingStation: ChargingStation, - commandPayload: OCPP16ReserveNowRequest, + commandPayload: OCPP16ReserveNowRequest ): Promise { if ( !OCPP16ServiceUtils.checkFeatureProfile( chargingStation, OCPP16SupportedFeatureProfiles.Reservation, - OCPP16IncomingRequestCommand.RESERVE_NOW, + OCPP16IncomingRequestCommand.RESERVE_NOW ) ) { - return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED } - const { reservationId, idTag, connectorId } = commandPayload; - let response: OCPP16ReserveNowResponse; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + commandPayload.expiryDate = convertToDate(commandPayload.expiryDate)! + const { reservationId, idTag, connectorId } = commandPayload + let response: OCPP16ReserveNowResponse try { if (connectorId > 0 && !chargingStation.isConnectorAvailable(connectorId)) { - return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED } if (connectorId === 0 && !chargingStation.getReserveConnectorZeroSupported()) { - return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED } if (!(await OCPP16ServiceUtils.isIdTagAuthorized(chargingStation, connectorId, idTag))) { - return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED } - await removeExpiredReservations(chargingStation); - switch (chargingStation.getConnectorStatus(connectorId)!.status) { + await removeExpiredReservations(chargingStation) + switch (chargingStation.getConnectorStatus(connectorId)?.status) { case OCPP16ChargePointStatus.Faulted: - response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_FAULTED; - break; + response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_FAULTED + break case OCPP16ChargePointStatus.Preparing: case OCPP16ChargePointStatus.Charging: case OCPP16ChargePointStatus.SuspendedEV: case OCPP16ChargePointStatus.SuspendedEVSE: case OCPP16ChargePointStatus.Finishing: - response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED; - break; + response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED + break case OCPP16ChargePointStatus.Unavailable: - response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_UNAVAILABLE; - break; + response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_UNAVAILABLE + break case OCPP16ChargePointStatus.Reserved: if (!chargingStation.isConnectorReservable(reservationId, idTag, connectorId)) { - response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED; - break; + response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED + break } // eslint-disable-next-line no-fallthrough default: if (!chargingStation.isConnectorReservable(reservationId, idTag)) { - response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED; - break; + response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED + break } await chargingStation.addReservation({ id: commandPayload.reservationId, - ...commandPayload, - }); - response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_ACCEPTED; - break; + ...commandPayload + }) + response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_ACCEPTED + break } - return response; + return response } catch (error) { - chargingStation.getConnectorStatus(connectorId)!.status = OCPP16ChargePointStatus.Available; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.getConnectorStatus(connectorId)!.status = OCPP16ChargePointStatus.Available + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return this.handleIncomingRequestError( chargingStation, OCPP16IncomingRequestCommand.RESERVE_NOW, error as Error, - { errorResponse: OCPP16Constants.OCPP_RESERVATION_RESPONSE_FAULTED }, - )!; + { errorResponse: OCPP16Constants.OCPP_RESERVATION_RESPONSE_FAULTED } + )! } } - private async handleRequestCancelReservation( + private async handleRequestCancelReservation ( chargingStation: ChargingStation, - commandPayload: OCPP16CancelReservationRequest, + commandPayload: OCPP16CancelReservationRequest ): Promise { if ( !OCPP16ServiceUtils.checkFeatureProfile( chargingStation, OCPP16SupportedFeatureProfiles.Reservation, - OCPP16IncomingRequestCommand.CANCEL_RESERVATION, + OCPP16IncomingRequestCommand.CANCEL_RESERVATION ) ) { - return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED } try { - const { reservationId } = commandPayload; - const reservation = chargingStation.getReservationBy('reservationId', reservationId); - if (isUndefined(reservation)) { + const { reservationId } = commandPayload + const reservation = chargingStation.getReservationBy('reservationId', reservationId) + if (reservation == null) { logger.debug( - `${chargingStation.logPrefix()} Reservation with id ${reservationId} - does not exist on charging station`, - ); - return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED; + `${chargingStation.logPrefix()} Reservation with id ${reservationId} does not exist on charging station` + ) + return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED } await chargingStation.removeReservation( - reservation!, - ReservationTerminationReason.RESERVATION_CANCELED, - ); - return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_ACCEPTED; + reservation, + ReservationTerminationReason.RESERVATION_CANCELED + ) + return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_ACCEPTED } catch (error) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return this.handleIncomingRequestError( chargingStation, OCPP16IncomingRequestCommand.CANCEL_RESERVATION, error as Error, - { errorResponse: OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED }, - )!; + { errorResponse: OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED } + )! } } }