fix: reduce the number of overlaping charging profiles in get composite
authorJérôme Benoit <jerome.benoit@sap.com>
Tue, 1 Aug 2023 20:37:45 +0000 (22:37 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Tue, 1 Aug 2023 20:37:45 +0000 (22:37 +0200)
schedule

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
src/charging-station/Helpers.ts
src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts
src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts

index 1632f28b13514f18798ebf4992d5af23360922c2..1856007e223ad80bcbb48a365e315276d657f45a 100644 (file)
@@ -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<ChargingProfile[]>(
     (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;
index f4c0e6ad657423ab86e563907850f5a9a6276813..6912f765742efd86b9970066e92240aa45cc5b9c 100644 (file)
@@ -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<OCPP16ChargingProfile[]>(
+    const storedChargingProfiles: OCPP16ChargingProfile[] = cloneObject<OCPP16ChargingProfile[]>(
       (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(
index cb459dbd0d2018388ff61104d5ea9ee03e74d4bf..222406c6ce84090f5b82fc1af5e5b7d3d783c2d1 100644 (file)
@@ -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 = [];
     }