X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;ds=sidebyside;f=src%2Fcharging-station%2Focpp%2F1.6%2FOCPP16IncomingRequestService.ts;h=0215414862ae253d0a4bfe7455d04f218063ef27;hb=ef9e3b334b977bd79231adb7e59c3875313d32dc;hp=81f7540f74cf382c07482347a23adfead9a117a6;hpb=d372f6da34cd27ce947ea2457dc37646a7edb472;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 81f7540f..02154148 100644 --- a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts @@ -6,15 +6,18 @@ import { URL, fileURLToPath } from 'node:url'; import type { JSONSchemaType } from 'ajv'; import { Client, type FTPResponse } from 'basic-ftp'; -import { addSeconds, isWithinInterval, max, 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'; @@ -668,75 +671,107 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { return OCPP16Constants.OCPP_RESPONSE_REJECTED; } if (chargingRateUnit) { - logger.error( - `${chargingStation.logPrefix()} Get composite schedule with a specified rate unit is not yet supported`, + logger.warn( + `${chargingStation.logPrefix()} Get composite schedule with a specified rate unit is not yet supported, no conversion will be done`, ); - return OCPP16Constants.OCPP_RESPONSE_REJECTED; } - if (isEmptyArray(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles)) { + const connectorStatus = chargingStation.getConnectorStatus(connectorId)!; + if ( + isEmptyArray( + connectorStatus?.chargingProfiles && + isEmptyArray(chargingStation.getConnectorStatus(0)?.chargingProfiles), + ) + ) { return OCPP16Constants.OCPP_RESPONSE_REJECTED; } - const startDate = new Date(); - const interval: Interval = { - start: startDate, - end: addSeconds(startDate, 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(connectorId)! - .chargingProfiles!) { + for (const chargingProfile of chargingProfiles) { if ( - compositeSchedule?.chargingRateUnit && - compositeSchedule.chargingRateUnit !== chargingProfile.chargingSchedule.chargingRateUnit + isNullOrUndefined(chargingProfile.chargingSchedule?.startSchedule) && + connectorStatus?.transactionStarted ) { - logger.error( - `${chargingStation.logPrefix()} Building composite schedule with different charging rate units is not yet supported, skipping charging profile id ${ + 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 ( + !isNullOrUndefined(chargingProfile.chargingSchedule?.startSchedule) && + !isDate(chargingProfile.chargingSchedule?.startSchedule) + ) { + logger.warn( + `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${ + chargingProfile.chargingProfileId + } startSchedule property is not a Date instance. Trying to convert it to a Date instance`, + ); + 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 ( - isWithinInterval(chargingProfile.chargingSchedule.startSchedule!, interval) && - isWithinInterval( - addSeconds( - chargingProfile.chargingSchedule.startSchedule!, - chargingProfile.chargingSchedule.duration!, - ), - interval, + !canProceedChargingProfile( + chargingProfile, + compositeScheduleInterval.start as Date, + chargingStation.logPrefix(), ) ) { - compositeSchedule = { - startSchedule: max([ - compositeSchedule?.startSchedule ?? interval.start, - chargingProfile.chargingSchedule.startSchedule!, - ]), - duration: Math.max( - compositeSchedule?.duration ?? -Infinity, - chargingProfile.chargingSchedule.duration!, - ), - chargingRateUnit: chargingProfile.chargingSchedule.chargingRateUnit, - ...(compositeSchedule?.chargingSchedulePeriod === undefined - ? { chargingSchedulePeriod: [] } - : { - chargingSchedulePeriod: compositeSchedule.chargingSchedulePeriod.concat( - ...chargingProfile.chargingSchedule.chargingSchedulePeriod, - ), - }), - ...(chargingProfile.chargingSchedule.minChargeRate && { - minChargeRate: Math.min( - compositeSchedule?.minChargeRate ?? Infinity, - chargingProfile.chargingSchedule.minChargeRate, - ), - }), - }; + continue; } + compositeSchedule = OCPP16ServiceUtils.composeChargingSchedules( + previousCompositeSchedule, + chargingProfile.chargingSchedule, + compositeScheduleInterval, + ); + previousCompositeSchedule = compositeSchedule; } - return { - status: GenericStatus.Accepted, - scheduleStart: compositeSchedule?.startSchedule, - connectorId, - chargingSchedule: compositeSchedule, - }; + if (compositeSchedule) { + return { + status: GenericStatus.Accepted, + scheduleStart: compositeSchedule.startSchedule!, + connectorId, + chargingSchedule: compositeSchedule, + }; + } + return OCPP16Constants.OCPP_RESPONSE_REJECTED; } private handleRequestClearChargingProfile(