From bbb55ee4d90133cdba3669ef407175b4062a9300 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Tue, 1 Aug 2023 22:37:45 +0200 Subject: [PATCH] fix: reduce the number of overlaping charging profiles in get composite schedule MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- src/charging-station/Helpers.ts | 20 +++--- .../ocpp/1.6/OCPP16IncomingRequestService.ts | 62 +++++++++++++------ .../ocpp/1.6/OCPP16ServiceUtils.ts | 2 +- 3 files changed, 54 insertions(+), 30 deletions(-) diff --git a/src/charging-station/Helpers.ts b/src/charging-station/Helpers.ts index 1632f28b..1856007e 100644 --- a/src/charging-station/Helpers.ts +++ b/src/charging-station/Helpers.ts @@ -518,7 +518,7 @@ export const getChargingStationConnectorChargingProfilesPowerLimit = ( chargingStation: ChargingStation, connectorId: number, ): number | undefined => { - let limit: number | undefined, matchingChargingProfile: ChargingProfile | undefined; + let limit: number | undefined, chargingProfile: ChargingProfile | undefined; // Get charging profiles for connector id and sort by stack level const chargingProfiles = cloneObject( (chargingStation.getConnectorStatus(connectorId)?.chargingProfiles ?? []).concat( @@ -534,12 +534,11 @@ export const getChargingStationConnectorChargingProfilesPowerLimit = ( ); if (!isNullOrUndefined(result)) { limit = result?.limit; - matchingChargingProfile = result?.matchingChargingProfile; + chargingProfile = result?.chargingProfile; switch (chargingStation.getCurrentOutType()) { case CurrentType.AC: limit = - matchingChargingProfile?.chargingSchedule?.chargingRateUnit === - ChargingRateUnitType.WATT + chargingProfile?.chargingSchedule?.chargingRateUnit === ChargingRateUnitType.WATT ? limit : ACElectricUtils.powerTotal( chargingStation.getNumberOfPhases(), @@ -549,8 +548,7 @@ export const getChargingStationConnectorChargingProfilesPowerLimit = ( break; case CurrentType.DC: limit = - matchingChargingProfile?.chargingSchedule?.chargingRateUnit === - ChargingRateUnitType.WATT + chargingProfile?.chargingSchedule?.chargingRateUnit === ChargingRateUnitType.WATT ? limit : DCElectricUtils.power(chargingStation.getVoltageOut(), limit!); } @@ -558,7 +556,7 @@ export const getChargingStationConnectorChargingProfilesPowerLimit = ( chargingStation.getMaximumPower() / chargingStation.powerDivider; if (limit! > connectorMaximumPower) { logger.error( - `${chargingStation.logPrefix()} ${moduleName}.getChargingStationConnectorChargingProfilesPowerLimit: Charging profile id ${matchingChargingProfile?.chargingProfileId} limit ${limit} is greater than connector id ${connectorId} maximum ${connectorMaximumPower}: %j`, + `${chargingStation.logPrefix()} ${moduleName}.getChargingStationConnectorChargingProfilesPowerLimit: Charging profile id ${chargingProfile?.chargingProfileId} limit ${limit} is greater than connector id ${connectorId} maximum ${connectorMaximumPower}: %j`, result, ); limit = connectorMaximumPower; @@ -713,7 +711,7 @@ const convertDeprecatedTemplateKey = ( interface ChargingProfilesLimit { limit: number; - matchingChargingProfile: ChargingProfile; + chargingProfile: ChargingProfile; } /** @@ -796,7 +794,7 @@ const getLimitFromChargingProfiles = ( if (chargingSchedule.chargingSchedulePeriod.length === 1) { const result: ChargingProfilesLimit = { limit: chargingSchedule.chargingSchedulePeriod[0].limit, - matchingChargingProfile: chargingProfile, + chargingProfile, }; logger.debug(debugLogMsg, result); return result; @@ -817,7 +815,7 @@ const getLimitFromChargingProfiles = ( // Found the schedule period: previous is the correct one const result: ChargingProfilesLimit = { limit: previousChargingSchedulePeriod!.limit, - matchingChargingProfile: chargingProfile, + chargingProfile: chargingProfile, }; logger.debug(debugLogMsg, result); return result; @@ -839,7 +837,7 @@ const getLimitFromChargingProfiles = ( ) { const result: ChargingProfilesLimit = { limit: previousChargingSchedulePeriod.limit, - matchingChargingProfile: chargingProfile, + chargingProfile: chargingProfile, }; logger.debug(debugLogMsg, result); return result; diff --git a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts index f4c0e6ad..6912f765 100644 --- a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts @@ -8,10 +8,14 @@ import type { JSONSchemaType } from 'ajv'; import { Client, type FTPResponse } from 'basic-ftp'; import { addSeconds, + isAfter, + isBefore, isDate, isWithinInterval, + max, maxTime, min, + minTime, secondsToMilliseconds, } from 'date-fns'; import { create } from 'tar'; @@ -681,10 +685,9 @@ 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; } const connectorStatus = chargingStation.getConnectorStatus(connectorId)!; if ( @@ -700,38 +703,39 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { start: currentDate, end: addSeconds(currentDate, duration), }; - const chargingProfiles: OCPP16ChargingProfile[] = []; - for (const chargingProfile of cloneObject( + const storedChargingProfiles: OCPP16ChargingProfile[] = cloneObject( (connectorStatus?.chargingProfiles ?? []).concat( chargingStation.getConnectorStatus(0)?.chargingProfiles ?? [], ), - ).sort((a, b) => b.stackLevel - a.stackLevel)) { + ).sort((a, b) => b.stackLevel - a.stackLevel); + const chargingProfiles: OCPP16ChargingProfile[] = []; + for (const storedChargingProfile of storedChargingProfiles) { if ( connectorStatus?.transactionStarted && - isNullOrUndefined(chargingProfile.chargingSchedule?.startSchedule) + isNullOrUndefined(storedChargingProfile.chargingSchedule?.startSchedule) ) { logger.debug( `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${ - chargingProfile.chargingProfileId + storedChargingProfile.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; + storedChargingProfile.chargingSchedule.startSchedule = connectorStatus?.transactionStart; } - if (!isDate(chargingProfile.chargingSchedule?.startSchedule)) { + if (!isDate(storedChargingProfile.chargingSchedule?.startSchedule)) { logger.warn( `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${ - chargingProfile.chargingProfileId + storedChargingProfile.chargingProfileId } startSchedule property is not a Date object. Trying to convert it to a Date object`, ); - chargingProfile.chargingSchedule.startSchedule = convertToDate( - chargingProfile.chargingSchedule?.startSchedule, + storedChargingProfile.chargingSchedule.startSchedule = convertToDate( + storedChargingProfile.chargingSchedule?.startSchedule, )!; } if ( !prepareChargingProfileKind( connectorStatus, - chargingProfile, + storedChargingProfile, interval.start as Date, chargingStation.logPrefix(), ) @@ -740,7 +744,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { } if ( !canProceedChargingProfile( - chargingProfile, + storedChargingProfile, interval.start as Date, chargingStation.logPrefix(), ) @@ -749,10 +753,32 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { } // Add active charging profiles into chargingProfiles array if ( - isValidTime(chargingProfile.chargingSchedule?.startSchedule) && - isWithinInterval(chargingProfile.chargingSchedule.startSchedule!, interval) + isValidTime(storedChargingProfile.chargingSchedule?.startSchedule) && + isWithinInterval(storedChargingProfile.chargingSchedule.startSchedule!, interval) && + (isEmptyArray(chargingProfiles) || + (isNotEmptyArray(chargingProfiles) && + (isBefore( + storedChargingProfile.chargingSchedule.startSchedule!, + min( + chargingProfiles.map( + (chargingProfile) => chargingProfile.chargingSchedule.startSchedule ?? maxTime, + ), + ), + ) || + isAfter( + storedChargingProfile.chargingSchedule.startSchedule!, + max( + chargingProfiles.map( + (chargingProfile) => + addSeconds( + chargingProfile.chargingSchedule.startSchedule!, + chargingProfile.chargingSchedule.duration!, + ) ?? minTime, + ), + ), + )))) ) { - chargingProfiles.push(chargingProfile); + chargingProfiles.push(storedChargingProfile); } } const compositeScheduleStart: Date = min( diff --git a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts index cb459dbd..222406c6 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts @@ -863,7 +863,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { Array.isArray(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles) === false ) { logger.error( - `${chargingStation.logPrefix()} Trying to set a charging profile on connector id ${connectorId} with an improper attribute type for the charging profiles array, applying proper type initialization`, + `${chargingStation.logPrefix()} Trying to set a charging profile on connector id ${connectorId} with an improper attribute type for the charging profiles array, applying proper type deferred initialization`, ); chargingStation.getConnectorStatus(connectorId)!.chargingProfiles = []; } -- 2.34.1