X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2Focpp%2F1.6%2FOCPP16IncomingRequestService.ts;h=0215414862ae253d0a4bfe7455d04f218063ef27;hb=ef9e3b334b977bd79231adb7e59c3875313d32dc;hp=f5b568dc41eb39ade5eeb28cbfb7fff9459c81c7;hpb=611d5cd2c94317acc7959c930bf962847b059e86;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 f5b568dc..02154148 100644 --- a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts @@ -6,26 +6,27 @@ import { URL, fileURLToPath } from 'node:url'; import type { JSONSchemaType } from 'ajv'; import { Client, type FTPResponse } from 'basic-ftp'; -import { secondsToMilliseconds } from 'date-fns'; +import { addSeconds, differenceInSeconds, isDate, maxTime, secondsToMilliseconds } from 'date-fns'; import { create } from 'tar'; import { OCPP16Constants } from './OCPP16Constants'; import { OCPP16ServiceUtils } from './OCPP16ServiceUtils'; import { type ChargingStation, + canProceedChargingProfile, checkChargingStation, getConfigurationKey, + getConnectorChargingProfiles, + prepareChargingProfileKind, + removeExpiredReservations, setConfigurationKeyValue, } from '../../../charging-station'; import { OCPPError } from '../../../exception'; import { - type ChangeAvailabilityRequest, - type ChangeAvailabilityResponse, type ChangeConfigurationRequest, type ChangeConfigurationResponse, type ClearChargingProfileRequest, type ClearChargingProfileResponse, - type ConnectorStatus, ErrorType, type GenericResponse, GenericStatus, @@ -41,6 +42,8 @@ import { type OCPP16BootNotificationRequest, type OCPP16BootNotificationResponse, type OCPP16CancelReservationRequest, + type OCPP16ChangeAvailabilityRequest, + type OCPP16ChangeAvailabilityResponse, OCPP16ChargePointErrorCode, OCPP16ChargePointStatus, type OCPP16ChargingProfile, @@ -259,7 +262,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { ], [ OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY, - OCPP16ServiceUtils.parseJsonSchemaFile( + OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/ChangeAvailability.json', moduleName, 'constructor', @@ -437,18 +440,17 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { chargingStation: ChargingStation, commandPayload: ResetRequest, ): GenericResponse { + const { type } = commandPayload; this.runInAsyncScope( chargingStation.reset.bind(chargingStation) as ( this: ChargingStation, ...args: unknown[] ) => Promise, chargingStation, - `${commandPayload.type}Reset` as OCPP16StopTransactionReason, + `${type}Reset` as OCPP16StopTransactionReason, ).catch(Constants.EMPTY_FUNCTION); logger.info( - `${chargingStation.logPrefix()} ${ - commandPayload.type - } reset command received, simulating it. The station will be + `${chargingStation.logPrefix()} ${type} reset command received, simulating it. The station will be back online in ${formatDurationMilliSeconds(chargingStation.stationInfo.resetTime!)}`, ); return OCPP16Constants.OCPP_RESPONSE_ACCEPTED; @@ -458,7 +460,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { chargingStation: ChargingStation, commandPayload: UnlockConnectorRequest, ): Promise { - const connectorId = commandPayload.connectorId; + const { connectorId } = commandPayload; if (chargingStation.hasConnector(connectorId) === false) { logger.error( `${chargingStation.logPrefix()} Trying to unlock a non existing @@ -494,9 +496,10 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { chargingStation: ChargingStation, commandPayload: GetConfigurationRequest, ): GetConfigurationResponse { + const { key } = commandPayload; const configurationKey: OCPPConfigurationKey[] = []; const unknownKey: string[] = []; - if (isUndefined(commandPayload.key) === true) { + if (isUndefined(key) === true) { for (const configuration of chargingStation.ocppConfiguration!.configurationKey!) { if (isUndefined(configuration.visible) === true) { configuration.visible = true; @@ -510,9 +513,9 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { value: configuration.value, }); } - } else if (isNotEmptyArray(commandPayload.key) === true) { - for (const key of commandPayload.key!) { - const keyFound = getConfigurationKey(chargingStation, key, true); + } 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; @@ -526,7 +529,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { value: keyFound.value, }); } else { - unknownKey.push(key); + unknownKey.push(k); } } } @@ -540,13 +543,14 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { chargingStation: ChargingStation, commandPayload: ChangeConfigurationRequest, ): ChangeConfigurationResponse { - const keyToChange = getConfigurationKey(chargingStation, commandPayload.key, true); + const { key, value } = commandPayload; + const keyToChange = getConfigurationKey(chargingStation, key, true); if (keyToChange?.readonly === true) { return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_REJECTED; } else if (keyToChange?.readonly === false) { let valueChanged = false; - if (keyToChange.value !== commandPayload.value) { - setConfigurationKeyValue(chargingStation, commandPayload.key, commandPayload.value, true); + if (keyToChange.value !== value) { + setConfigurationKeyValue(chargingStation, key, value, true); valueChanged = true; } let triggerHeartbeatRestart = false; @@ -558,7 +562,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { setConfigurationKeyValue( chargingStation, OCPP16StandardParametersKey.HeartbeatInterval, - commandPayload.value, + value, ); triggerHeartbeatRestart = true; } @@ -570,7 +574,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { setConfigurationKeyValue( chargingStation, OCPP16StandardParametersKey.HeartBeatInterval, - commandPayload.value, + value, ); triggerHeartbeatRestart = true; } @@ -605,43 +609,36 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { ) { return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_NOT_SUPPORTED; } - if (chargingStation.hasConnector(commandPayload.connectorId) === false) { + const { connectorId, csChargingProfiles } = commandPayload; + if (chargingStation.hasConnector(connectorId) === false) { logger.error( `${chargingStation.logPrefix()} Trying to set charging profile(s) to a - non existing connector id ${commandPayload.connectorId}`, + non existing connector id ${connectorId}`, ); return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED; } if ( - commandPayload.csChargingProfiles.chargingProfilePurpose === + csChargingProfiles.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.CHARGE_POINT_MAX_PROFILE && - commandPayload.connectorId !== 0 + connectorId !== 0 ) { return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED; } if ( - commandPayload.csChargingProfiles.chargingProfilePurpose === - OCPP16ChargingProfilePurposeType.TX_PROFILE && - (commandPayload.connectorId === 0 || - chargingStation.getConnectorStatus(commandPayload.connectorId)?.transactionStarted === - false) + csChargingProfiles.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.TX_PROFILE && + (connectorId === 0 || + chargingStation.getConnectorStatus(connectorId)?.transactionStarted === false) ) { logger.error( `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) - on connector ${commandPayload.connectorId} without a started transaction`, + on connector ${connectorId} without a started transaction`, ); return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED; } - OCPP16ServiceUtils.setChargingProfile( - chargingStation, - commandPayload.connectorId, - commandPayload.csChargingProfiles, - ); + OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, csChargingProfiles); logger.debug( - `${chargingStation.logPrefix()} Charging profile(s) set on connector id ${ - commandPayload.connectorId - }: %j`, - commandPayload.csChargingProfiles, + `${chargingStation.logPrefix()} Charging profile(s) set on connector id ${connectorId}: %j`, + csChargingProfiles, ); return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED; } @@ -659,38 +656,122 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { ) { return OCPP16Constants.OCPP_RESPONSE_REJECTED; } - if (chargingStation.hasConnector(commandPayload.connectorId) === false) { + const { connectorId, duration, chargingRateUnit } = commandPayload; + if (chargingStation.hasConnector(connectorId) === false) { logger.error( `${chargingStation.logPrefix()} Trying to get composite schedule to a - non existing connector id ${commandPayload.connectorId}`, + 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; + } + if (chargingRateUnit) { + logger.warn( + `${chargingStation.logPrefix()} Get composite schedule with a specified rate unit is not yet supported, no conversion will be done`, + ); + } + const connectorStatus = chargingStation.getConnectorStatus(connectorId)!; if ( - isEmptyArray(chargingStation.getConnectorStatus(commandPayload.connectorId)?.chargingProfiles) + isEmptyArray( + connectorStatus?.chargingProfiles && + isEmptyArray(chargingStation.getConnectorStatus(0)?.chargingProfiles), + ) ) { return OCPP16Constants.OCPP_RESPONSE_REJECTED; } - const startDate = new Date(); - const endDate = new Date(startDate.getTime() + secondsToMilliseconds(commandPayload.duration)); + const currentDate = new Date(); + const compositeScheduleInterval: Interval = { + start: currentDate, + 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 chargingStation.getConnectorStatus(commandPayload.connectorId)! - .chargingProfiles!) { - // FIXME: build the composite schedule including the local power limit, the stack level, the charging rate unit, etc. + for (const chargingProfile of chargingProfiles) { + if ( + isNullOrUndefined(chargingProfile.chargingSchedule?.startSchedule) && + connectorStatus?.transactionStarted + ) { + 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; + } if ( - chargingProfile.chargingSchedule.startSchedule! >= startDate && - chargingProfile.chargingSchedule.startSchedule! <= endDate + !isNullOrUndefined(chargingProfile.chargingSchedule?.startSchedule) && + !isDate(chargingProfile.chargingSchedule?.startSchedule) ) { - compositeSchedule = chargingProfile.chargingSchedule; - break; + 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`, + ); + chargingProfile.chargingSchedule.startSchedule = convertToDate( + chargingProfile.chargingSchedule?.startSchedule, + )!; } + if ( + !isNullOrUndefined(chargingProfile.chargingSchedule?.startSchedule) && + isNullOrUndefined(chargingProfile.chargingSchedule?.duration) + ) { + 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, + chargingProfile.chargingSchedule.startSchedule!, + ); + } + if ( + !prepareChargingProfileKind( + connectorStatus, + chargingProfile, + compositeScheduleInterval.start as Date, + chargingStation.logPrefix(), + ) + ) { + continue; + } + if ( + !canProceedChargingProfile( + chargingProfile, + compositeScheduleInterval.start as Date, + chargingStation.logPrefix(), + ) + ) { + continue; + } + compositeSchedule = OCPP16ServiceUtils.composeChargingSchedules( + previousCompositeSchedule, + chargingProfile.chargingSchedule, + compositeScheduleInterval, + ); + previousCompositeSchedule = compositeSchedule; } - return { - status: GenericStatus.Accepted, - scheduleStart: compositeSchedule?.startSchedule, - connectorId: commandPayload.connectorId, - chargingSchedule: compositeSchedule, - }; + if (compositeSchedule) { + return { + status: GenericStatus.Accepted, + scheduleStart: compositeSchedule.startSchedule!, + connectorId, + chargingSchedule: compositeSchedule, + }; + } + return OCPP16Constants.OCPP_RESPONSE_REJECTED; } private handleRequestClearChargingProfile( @@ -706,28 +787,25 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { ) { return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN; } - if (chargingStation.hasConnector(commandPayload.connectorId!) === false) { + const { connectorId } = commandPayload; + if (chargingStation.hasConnector(connectorId!) === false) { logger.error( `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to - a non existing connector id ${commandPayload.connectorId}`, + a non existing connector id ${connectorId}`, ); return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN; } if ( - !isNullOrUndefined(commandPayload.connectorId) && - isNotEmptyArray( - chargingStation.getConnectorStatus(commandPayload.connectorId!)?.chargingProfiles, - ) + !isNullOrUndefined(connectorId) && + isNotEmptyArray(chargingStation.getConnectorStatus(connectorId!)?.chargingProfiles) ) { - chargingStation.getConnectorStatus(commandPayload.connectorId!)!.chargingProfiles = []; + chargingStation.getConnectorStatus(connectorId!)!.chargingProfiles = []; logger.debug( - `${chargingStation.logPrefix()} Charging profile(s) cleared on connector id ${ - commandPayload.connectorId - }`, + `${chargingStation.logPrefix()} Charging profile(s) cleared on connector id ${connectorId}`, ); return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED; } - if (isNullOrUndefined(commandPayload.connectorId)) { + if (isNullOrUndefined(connectorId)) { let clearedCP = false; if (chargingStation.hasEvses) { for (const evseStatus of chargingStation.evses.values()) { @@ -740,11 +818,11 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { } } } else { - for (const connectorId of chargingStation.connectors.keys()) { + for (const id of chargingStation.connectors.keys()) { clearedCP = OCPP16ServiceUtils.clearChargingProfiles( chargingStation, commandPayload, - chargingStation.getConnectorStatus(connectorId)?.chargingProfiles, + chargingStation.getConnectorStatus(id)?.chargingProfiles, ); } } @@ -757,9 +835,9 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { private async handleRequestChangeAvailability( chargingStation: ChargingStation, - commandPayload: ChangeAvailabilityRequest, - ): Promise { - const connectorId: number = commandPayload.connectorId; + commandPayload: OCPP16ChangeAvailabilityRequest, + ): Promise { + const { connectorId, type } = commandPayload; if (chargingStation.hasConnector(connectorId) === false) { logger.error( `${chargingStation.logPrefix()} Trying to change the availability of a @@ -768,48 +846,40 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED; } const chargePointStatus: OCPP16ChargePointStatus = - commandPayload.type === OCPP16AvailabilityType.Operative + type === OCPP16AvailabilityType.Operative ? OCPP16ChargePointStatus.Available : OCPP16ChargePointStatus.Unavailable; if (connectorId === 0) { - let response: ChangeAvailabilityResponse = - OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED; - const changeAvailability = async (id: number, connectorStatus: ConnectorStatus) => { - if (connectorStatus?.transactionStarted === true) { - response = OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED; - } - connectorStatus.availability = commandPayload.type; - if (response === OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED) { - await OCPP16ServiceUtils.sendAndSetConnectorStatus( + let response: OCPP16ChangeAvailabilityResponse; + if (chargingStation.hasEvses) { + for (const evseStatus of chargingStation.evses.values()) { + response = await OCPP16ServiceUtils.changeAvailability( chargingStation, - id, + [...evseStatus.connectors.keys()], chargePointStatus, + type, ); } - }; - if (chargingStation.hasEvses) { - for (const evseStatus of chargingStation.evses.values()) { - for (const [id, connectorStatus] of evseStatus.connectors) { - await changeAvailability(id, connectorStatus); - } - } } else { - for (const id of chargingStation.connectors.keys()) { - await changeAvailability(id, chargingStation.getConnectorStatus(id)!); - } + response = await OCPP16ServiceUtils.changeAvailability( + chargingStation, + [...chargingStation.connectors.keys()], + chargePointStatus, + type, + ); } - return response; + return response!; } else if ( connectorId > 0 && (chargingStation.isChargingStationAvailable() === true || (chargingStation.isChargingStationAvailable() === false && - commandPayload.type === OCPP16AvailabilityType.Inoperative)) + type === OCPP16AvailabilityType.Inoperative)) ) { if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) { - chargingStation.getConnectorStatus(connectorId)!.availability = commandPayload.type; + chargingStation.getConnectorStatus(connectorId)!.availability = type; return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED; } - chargingStation.getConnectorStatus(connectorId)!.availability = commandPayload.type; + chargingStation.getConnectorStatus(connectorId)!.availability = type; await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, connectorId, @@ -825,15 +895,6 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { commandPayload: RemoteStartTransactionRequest, ): Promise { const { connectorId: transactionConnectorId, idTag, chargingProfile } = commandPayload; - if ( - (chargingStation.getConnectorStatus(transactionConnectorId)?.status === - OCPP16ChargePointStatus.Reserved && - chargingStation.getReservationBy('connectorId', transactionConnectorId)?.idTag !== idTag) || - (chargingStation.getConnectorStatus(0)?.status === OCPP16ChargePointStatus.Reserved && - chargingStation.getReservationBy('connectorId', 0)?.idTag !== idTag) - ) { - return OCPP16Constants.OCPP_RESPONSE_REJECTED; - } if (chargingStation.hasConnector(transactionConnectorId) === false) { return this.notifyRemoteStartTransactionRejected( chargingStation, @@ -861,20 +922,9 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { OCPP16ChargePointStatus.Preparing, ); const connectorStatus = chargingStation.getConnectorStatus(transactionConnectorId)!; - if ( - chargingStation.getAuthorizeRemoteTxRequests() && - !chargingStation.getLocalAuthListEnabled() && - !chargingStation.getMustAuthorizeAtRemoteStart() - ) { - logger.warn( - `${chargingStation.logPrefix()} The charging station configuration expects authorize at remote start transaction - but local authorization or must authorize at remote start isn't enabled`, - ); - } // Authorization check required if ( chargingStation.getAuthorizeRemoteTxRequests() === true && - chargingStation.getMustAuthorizeAtRemoteStart() === true && (await OCPP16ServiceUtils.isIdTagAuthorized(chargingStation, transactionConnectorId, idTag)) ) { // Authorization successful, start transaction @@ -894,12 +944,6 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, { connectorId: transactionConnectorId, idTag, - reservationId: chargingStation.getReservationBy( - 'connectorId', - chargingStation.getConnectorStatus(0)?.status === OCPP16ChargePointStatus.Reserved - ? 0 - : transactionConnectorId, - )!, }) ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED ) { @@ -935,12 +979,6 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, { connectorId: transactionConnectorId, idTag, - reservationId: chargingStation.getReservationBy( - 'connectorId', - chargingStation.getConnectorStatus(0)?.status === OCPP16ChargePointStatus.Reserved - ? 0 - : transactionConnectorId, - )!, }) ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED ) { @@ -1017,28 +1055,13 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { chargingStation: ChargingStation, commandPayload: RemoteStopTransactionRequest, ): Promise { - const transactionId = commandPayload.transactionId; - const remoteStopTransaction = async (connectorId: number): Promise => { - await OCPP16ServiceUtils.sendAndSetConnectorStatus( - chargingStation, - connectorId, - OCPP16ChargePointStatus.Finishing, - ); - const stopResponse = await chargingStation.stopTransactionOnConnector( - connectorId, - OCPP16StopTransactionReason.REMOTE, - ); - if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) { - return OCPP16Constants.OCPP_RESPONSE_ACCEPTED; - } - return OCPP16Constants.OCPP_RESPONSE_REJECTED; - }; + 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 remoteStopTransaction(connectorId); + return OCPP16ServiceUtils.remoteStopTransaction(chargingStation, connectorId); } } } @@ -1049,12 +1072,12 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { connectorId > 0 && chargingStation.getConnectorStatus(connectorId)?.transactionId === transactionId ) { - return remoteStopTransaction(connectorId); + return OCPP16ServiceUtils.remoteStopTransaction(chargingStation, connectorId); } } } logger.warn( - `${chargingStation.logPrefix()} Trying to remote stop a non existing transaction with id: + `${chargingStation.logPrefix()} Trying to remote stop a non existing transaction with id ${transactionId.toString()}`, ); return OCPP16Constants.OCPP_RESPONSE_REJECTED; @@ -1077,6 +1100,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { ); return OCPP16Constants.OCPP_RESPONSE_EMPTY; } + let { retrieveDate } = commandPayload; if ( !isNullOrUndefined(chargingStation.stationInfo.firmwareStatus) && chargingStation.stationInfo.firmwareStatus !== OCPP16FirmwareStatus.Installed @@ -1087,7 +1111,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { ); return OCPP16Constants.OCPP_RESPONSE_EMPTY; } - const retrieveDate = convertToDate(commandPayload.retrieveDate)!; + retrieveDate = convertToDate(retrieveDate)!; const now = Date.now(); if (retrieveDate?.getTime() <= now) { this.runInAsyncScope( @@ -1280,7 +1304,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { ); return OCPP16Constants.OCPP_RESPONSE_EMPTY; } - const uri = new URL(commandPayload.location); + const { location } = commandPayload; + const uri = new URL(location); if (uri.protocol.startsWith('ftp:')) { let ftpClient: Client | undefined; try { @@ -1300,7 +1325,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { if (accessResponse.code === 220) { ftpClient.trackProgress((info) => { logger.info( - `${chargingStation.logPrefix()} ${ + `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: ${ info.bytes / 1024 } bytes transferred from diagnostics archive ${info.name}`, ); @@ -1387,16 +1412,14 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { chargingStation: ChargingStation, commandPayload: OCPP16TriggerMessageRequest, ): OCPP16TriggerMessageResponse { + const { requestedMessage, connectorId } = commandPayload; if ( !OCPP16ServiceUtils.checkFeatureProfile( chargingStation, OCPP16SupportedFeatureProfiles.RemoteTrigger, OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, ) || - !OCPP16ServiceUtils.isMessageTriggerSupported( - chargingStation, - commandPayload.requestedMessage, - ) + !OCPP16ServiceUtils.isMessageTriggerSupported(chargingStation, requestedMessage) ) { return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED; } @@ -1404,13 +1427,13 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { !OCPP16ServiceUtils.isConnectorIdValid( chargingStation, OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, - commandPayload.connectorId!, + connectorId!, ) ) { return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED; } try { - switch (commandPayload.requestedMessage) { + switch (requestedMessage) { case OCPP16MessageTrigger.BootNotification: setTimeout(() => { chargingStation.ocppRequestService @@ -1442,15 +1465,15 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED; case OCPP16MessageTrigger.StatusNotification: setTimeout(() => { - if (!isNullOrUndefined(commandPayload?.connectorId)) { + if (!isNullOrUndefined(connectorId)) { chargingStation.ocppRequestService .requestHandler( chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, { - connectorId: commandPayload.connectorId, + connectorId, errorCode: OCPP16ChargePointErrorCode.NO_ERROR, - status: chargingStation.getConnectorStatus(commandPayload.connectorId!)?.status, + status: chargingStation.getConnectorStatus(connectorId!)?.status, }, { triggerMessage: true, @@ -1461,7 +1484,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { // eslint-disable-next-line no-lonely-if if (chargingStation.hasEvses) { for (const evseStatus of chargingStation.evses.values()) { - for (const [connectorId, connectorStatus] of evseStatus.connectors) { + for (const [id, connectorStatus] of evseStatus.connectors) { chargingStation.ocppRequestService .requestHandler< OCPP16StatusNotificationRequest, @@ -1470,7 +1493,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, { - connectorId, + connectorId: id, errorCode: OCPP16ChargePointErrorCode.NO_ERROR, status: connectorStatus.status, }, @@ -1482,7 +1505,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { } } } else { - for (const connectorId of chargingStation.connectors.keys()) { + for (const id of chargingStation.connectors.keys()) { chargingStation.ocppRequestService .requestHandler< OCPP16StatusNotificationRequest, @@ -1491,9 +1514,9 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, { - connectorId, + connectorId: id, errorCode: OCPP16ChargePointErrorCode.NO_ERROR, - status: chargingStation.getConnectorStatus(connectorId)?.status, + status: chargingStation.getConnectorStatus(id)?.status, }, { triggerMessage: true, @@ -1522,8 +1545,9 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { chargingStation: ChargingStation, commandPayload: OCPP16DataTransferRequest, ): OCPP16DataTransferResponse { + const { vendorId } = commandPayload; try { - if (Object.values(OCPP16DataTransferVendorId).includes(commandPayload.vendorId)) { + if (Object.values(OCPP16DataTransferVendorId).includes(vendorId)) { return OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_ACCEPTED; } return OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_UNKNOWN_VENDOR_ID; @@ -1562,6 +1586,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { if (!(await OCPP16ServiceUtils.isIdTagAuthorized(chargingStation, connectorId, idTag))) { return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED; } + await removeExpiredReservations(chargingStation); switch (chargingStation.getConnectorStatus(connectorId)!.status) { case OCPP16ChargePointStatus.Faulted: response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_FAULTED; @@ -1623,8 +1648,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { const { reservationId } = commandPayload; const reservation = chargingStation.getReservationBy('reservationId', reservationId); if (isUndefined(reservation)) { - logger.error( - `${chargingStation.logPrefix()} Reservation with ID ${reservationId} + logger.debug( + `${chargingStation.logPrefix()} Reservation with id ${reservationId} does not exist on charging station`, ); return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED;