refactor: remove uneeded unknown intermediate type cast
[e-mobility-charging-stations-simulator.git] / src / charging-station / ChargingStationUtils.ts
index 0eeb205189456c4d0ea44ec96d3cfdf434722075..6c2732460c8cbd84c388ce58958f2d8f7f231657 100644 (file)
@@ -13,6 +13,7 @@ import {
   differenceInWeeks,
   isAfter,
   isBefore,
+  isDate,
   isWithinInterval,
   toDate,
 } from 'date-fns';
@@ -56,7 +57,7 @@ import {
   isNotEmptyString,
   isNullOrUndefined,
   isUndefined,
-  isValidDate,
+  isValidTime,
   logger,
   secureRandom,
 } from '../utils';
@@ -399,7 +400,7 @@ export const stationTemplateToStationInfo = (
   delete stationTemplate.chargeBoxSerialNumberPrefix;
   delete stationTemplate.chargePointSerialNumberPrefix;
   delete stationTemplate.meterSerialNumberPrefix;
-  return stationTemplate as unknown as ChargingStationInfo;
+  return stationTemplate as ChargingStationInfo;
 };
 
 export const createSerialNumber = (
@@ -672,8 +673,10 @@ interface ChargingProfilesLimit {
 }
 
 /**
- * Charging profiles should already be sorted by connector id and stack level (highest stack level has priority)
+ * Charging profiles shall already be sorted by connector id and stack level (highest stack level has priority)
  *
+ * @param chargingStation -
+ * @param connectorId -
  * @param chargingProfiles -
  * @param logPrefix -
  * @returns ChargingProfilesLimit
@@ -688,58 +691,38 @@ const getLimitFromChargingProfiles = (
   const currentDate = new Date();
   const connectorStatus = chargingStation.getConnectorStatus(connectorId);
   for (const chargingProfile of chargingProfiles) {
-    if (
-      (isValidDate(chargingProfile.validFrom) &&
-        isBefore(currentDate, chargingProfile.validFrom!)) ||
-      (isValidDate(chargingProfile.validTo) && isAfter(currentDate, chargingProfile.validTo!))
-    ) {
-      logger.debug(
-        `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Charging profile id ${
-          chargingProfile.chargingProfileId
-        } is not valid for the current date ${currentDate.toISOString()}`,
-      );
-      continue;
-    }
     const chargingSchedule = chargingProfile.chargingSchedule;
-    if (connectorStatus?.transactionStarted && !chargingSchedule?.startSchedule) {
+    if (connectorStatus?.transactionStarted && isNullOrUndefined(chargingSchedule?.startSchedule)) {
       logger.debug(
-        `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: startSchedule is not defined in charging profile id ${chargingProfile.chargingProfileId}. Trying to set it to the connector transaction start date`,
+        `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: 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
       chargingSchedule.startSchedule = connectorStatus?.transactionStart;
     }
-    if (!(chargingSchedule?.startSchedule instanceof Date)) {
+    if (!isDate(chargingSchedule?.startSchedule)) {
       logger.warn(
-        `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: startSchedule is not a Date object in charging profile id ${chargingProfile.chargingProfileId}. Trying to convert it to a Date object`,
+        `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Charging profile id ${chargingProfile.chargingProfileId} startSchedule property is not a Date object. Trying to convert it to a Date object`,
       );
       chargingSchedule.startSchedule = convertToDate(chargingSchedule?.startSchedule)!;
     }
-    if (
-      chargingProfile.chargingProfileKind === ChargingProfileKindType.RECURRING &&
-      isNullOrUndefined(chargingProfile.recurrencyKind)
-    ) {
-      logger.error(
-        `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Recurring charging profile id ${chargingProfile.chargingProfileId} has no recurrencyKind defined`,
-      );
-      continue;
-    }
-    if (chargingProfile.chargingProfileKind === ChargingProfileKindType.RECURRING) {
-      prepareRecurringChargingProfile(chargingProfile, currentDate, logPrefix);
-    } else if (
-      chargingProfile.chargingProfileKind === ChargingProfileKindType.RELATIVE &&
-      connectorStatus?.transactionStarted
-    ) {
-      chargingSchedule.startSchedule = connectorStatus?.transactionStart;
+    switch (chargingProfile.chargingProfileKind) {
+      case ChargingProfileKindType.RECURRING:
+        if (!canProceedRecurringChargingProfile(chargingProfile, logPrefix)) {
+          continue;
+        }
+        prepareRecurringChargingProfile(chargingProfile, currentDate, logPrefix);
+        break;
+      case ChargingProfileKindType.RELATIVE:
+        connectorStatus?.transactionStarted &&
+          (chargingSchedule.startSchedule = connectorStatus?.transactionStart);
+        break;
     }
-    if (isNullOrUndefined(chargingSchedule?.duration)) {
-      logger.error(
-        `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Charging profile id ${chargingProfile.chargingProfileId} has no duration defined, not yet supported`,
-      );
+    if (!canProceedChargingProfile(chargingProfile, currentDate, logPrefix)) {
       continue;
     }
     // Check if the charging profile is active
     if (
-      isValidDate(chargingSchedule?.startSchedule) &&
+      isValidTime(chargingSchedule?.startSchedule) &&
       isWithinInterval(currentDate, {
         start: chargingSchedule.startSchedule!,
         end: addSeconds(chargingSchedule.startSchedule!, chargingSchedule.duration!),
@@ -777,26 +760,29 @@ const getLimitFromChargingProfiles = (
           logger.debug(debugLogMsg, result);
           return result;
         }
-        let lastButOneSchedule: ChargingSchedulePeriod | undefined;
+        let previousChargingSchedulePeriod: ChargingSchedulePeriod | undefined;
         // Search for the right schedule period
-        for (const [index, schedulePeriod] of chargingSchedule.chargingSchedulePeriod.entries()) {
+        for (const [
+          index,
+          chargingSchedulePeriod,
+        ] of chargingSchedule.chargingSchedulePeriod.entries()) {
           // Find the right schedule period
           if (
             isAfter(
-              addSeconds(chargingSchedule.startSchedule!, schedulePeriod.startPeriod),
+              addSeconds(chargingSchedule.startSchedule!, chargingSchedulePeriod.startPeriod),
               currentDate,
             )
           ) {
-            // Found the schedule period: last but one is the correct one
+            // Found the schedule period: previous is the correct one
             const result: ChargingProfilesLimit = {
-              limit: lastButOneSchedule!.limit,
+              limit: previousChargingSchedulePeriod!.limit,
               matchingChargingProfile: chargingProfile,
             };
             logger.debug(debugLogMsg, result);
             return result;
           }
-          // Keep it
-          lastButOneSchedule = schedulePeriod;
+          // Keep a reference to previous one
+          previousChargingSchedulePeriod = chargingSchedulePeriod;
           // Handle the last schedule period within the charging profile duration
           if (
             index === chargingSchedule.chargingSchedulePeriod.length - 1 ||
@@ -811,7 +797,7 @@ const getLimitFromChargingProfiles = (
                 ))
           ) {
             const result: ChargingProfilesLimit = {
-              limit: lastButOneSchedule.limit,
+              limit: previousChargingSchedulePeriod.limit,
               matchingChargingProfile: chargingProfile,
             };
             logger.debug(debugLogMsg, result);
@@ -823,8 +809,56 @@ const getLimitFromChargingProfiles = (
   }
 };
 
+const canProceedChargingProfile = (
+  chargingProfile: ChargingProfile,
+  currentDate: Date,
+  logPrefix: string,
+): boolean => {
+  if (
+    (isValidTime(chargingProfile.validFrom) && isBefore(currentDate, chargingProfile.validFrom!)) ||
+    (isValidTime(chargingProfile.validTo) && isAfter(currentDate, chargingProfile.validTo!))
+  ) {
+    logger.debug(
+      `${logPrefix} ${moduleName}.canProceedChargingProfile: Charging profile id ${
+        chargingProfile.chargingProfileId
+      } is not valid for the current date ${currentDate.toISOString()}`,
+    );
+    return false;
+  }
+  const chargingSchedule = chargingProfile.chargingSchedule;
+  if (isNullOrUndefined(chargingSchedule?.startSchedule)) {
+    logger.error(
+      `${logPrefix} ${moduleName}.canProceedChargingProfile: Charging profile id ${chargingProfile.chargingProfileId} has (still) no startSchedule defined`,
+    );
+    return false;
+  }
+  if (isNullOrUndefined(chargingSchedule?.duration)) {
+    logger.error(
+      `${logPrefix} ${moduleName}.canProceedChargingProfile: Charging profile id ${chargingProfile.chargingProfileId} has no duration defined, not yet supported`,
+    );
+    return false;
+  }
+  return true;
+};
+
+const canProceedRecurringChargingProfile = (
+  chargingProfile: ChargingProfile,
+  logPrefix: string,
+): boolean => {
+  if (
+    chargingProfile.chargingProfileKind === ChargingProfileKindType.RECURRING &&
+    isNullOrUndefined(chargingProfile.recurrencyKind)
+  ) {
+    logger.error(
+      `${logPrefix} ${moduleName}.canProceedRecurringChargingProfile: Recurring charging profile id ${chargingProfile.chargingProfileId} has no recurrencyKind defined`,
+    );
+    return false;
+  }
+  return true;
+};
+
 /**
- *  Adjust recurring charging profile startSchedule to the current recurrency time interval if needed
+ * Adjust recurring charging profile startSchedule to the current recurrency time interval if needed
  *
  * @param chargingProfile -
  * @param currentDate -
@@ -834,8 +868,9 @@ const prepareRecurringChargingProfile = (
   chargingProfile: ChargingProfile,
   currentDate: Date,
   logPrefix: string,
-) => {
+): boolean => {
   const chargingSchedule = chargingProfile.chargingSchedule;
+  let recurringIntervalTranslated = false;
   let recurringInterval: Interval;
   switch (chargingProfile.recurrencyKind) {
     case RecurrencyKindType.DAILY:
@@ -856,6 +891,7 @@ const prepareRecurringChargingProfile = (
           start: chargingSchedule.startSchedule,
           end: addDays(chargingSchedule.startSchedule, 1),
         };
+        recurringIntervalTranslated = true;
       }
       break;
     case RecurrencyKindType.WEEKLY:
@@ -876,10 +912,15 @@ const prepareRecurringChargingProfile = (
           start: chargingSchedule.startSchedule,
           end: addWeeks(chargingSchedule.startSchedule, 1),
         };
+        recurringIntervalTranslated = true;
       }
       break;
+    default:
+      logger.error(
+        `${logPrefix} ${moduleName}.prepareRecurringChargingProfile: Recurring charging profile id ${chargingProfile.chargingProfileId} recurrency kind ${chargingProfile.recurrencyKind} is not supported`,
+      );
   }
-  if (!isWithinInterval(currentDate, recurringInterval!)) {
+  if (recurringIntervalTranslated && !isWithinInterval(currentDate, recurringInterval!)) {
     logger.error(
       `${logPrefix} ${moduleName}.prepareRecurringChargingProfile: Recurring ${
         chargingProfile.recurrencyKind
@@ -887,16 +928,17 @@ const prepareRecurringChargingProfile = (
         recurringInterval!.start,
       ).toISOString()}, ${toDate(
         recurringInterval!.end,
-      ).toISOString()}] is not properly translated to current date ${currentDate.toISOString()} `,
+      ).toISOString()}] has not been properly translated to current date ${currentDate.toISOString()} `,
     );
   }
+  return recurringIntervalTranslated;
 };
 
 const checkRecurringChargingProfileDuration = (
   chargingProfile: ChargingProfile,
   interval: Interval,
   logPrefix: string,
-) => {
+): void => {
   if (isNullOrUndefined(chargingProfile.chargingSchedule.duration)) {
     logger.warn(
       `${logPrefix} ${moduleName}.checkRecurringChargingProfileDuration: Recurring ${