ATG: add support for idTag distribution algorithms
[e-mobility-charging-stations-simulator.git] / src / charging-station / ChargingStationUtils.ts
index ad145f0b0f8b2cd3be8eb07ae087044484a9a7f1..24556120b4b9db9862dfae478ce3fcb468e2a309 100644 (file)
@@ -5,31 +5,33 @@ import { fileURLToPath } from 'url';
 import moment from 'moment';
 
 import BaseError from '../exception/BaseError';
-import ChargingStationInfo from '../types/ChargingStationInfo';
-import ChargingStationTemplate, {
+import type { ChargingStationInfo } from '../types/ChargingStationInfo';
+import {
   AmpereUnits,
+  type ChargingStationTemplate,
   CurrentType,
   Voltage,
 } from '../types/ChargingStationTemplate';
-import { SampledValueTemplate } from '../types/MeasurandPerPhaseSampledValueTemplates';
+import type { SampledValueTemplate } from '../types/MeasurandPerPhaseSampledValueTemplates';
 import { ChargingProfileKindType, RecurrencyKindType } from '../types/ocpp/1.6/ChargingProfile';
-import { ChargingProfile, ChargingSchedulePeriod } from '../types/ocpp/ChargingProfile';
+import type { ChargingProfile, ChargingSchedulePeriod } from '../types/ocpp/ChargingProfile';
 import { StandardParametersKey } from '../types/ocpp/Configuration';
 import { MeterValueMeasurand, MeterValuePhase } from '../types/ocpp/MeterValues';
 import {
-  BootNotificationRequest,
+  type BootNotificationRequest,
   IncomingRequestCommand,
   RequestCommand,
 } from '../types/ocpp/Requests';
-import { WebSocketCloseEventStatusString } from '../types/WebSocket';
 import { WorkerProcessType } from '../types/Worker';
 import Configuration from '../utils/Configuration';
 import Constants from '../utils/Constants';
 import logger from '../utils/Logger';
 import Utils from '../utils/Utils';
-import ChargingStation from './ChargingStation';
+import type ChargingStation from './ChargingStation';
 import { ChargingStationConfigurationUtils } from './ChargingStationConfigurationUtils';
 
+const moduleName = 'ChargingStationUtils';
+
 export class ChargingStationUtils {
   private constructor() {
     // This is intentional
@@ -107,16 +109,13 @@ export class ChargingStationUtils {
     }
   }
 
-  public static getConfiguredNumberOfConnectors(
-    index: number,
-    stationTemplate: ChargingStationTemplate
-  ): number {
+  public static getConfiguredNumberOfConnectors(stationTemplate: ChargingStationTemplate): number {
     let configuredMaxConnectors: number;
-    if (!Utils.isEmptyArray(stationTemplate.numberOfConnectors)) {
+    if (Utils.isEmptyArray(stationTemplate.numberOfConnectors) === false) {
       const numberOfConnectors = stationTemplate.numberOfConnectors as number[];
-      // Distribute evenly the number of connectors
-      configuredMaxConnectors = numberOfConnectors[(index - 1) % numberOfConnectors.length];
-    } else if (!Utils.isUndefined(stationTemplate.numberOfConnectors)) {
+      configuredMaxConnectors =
+        numberOfConnectors[Math.floor(Utils.secureRandom() * numberOfConnectors.length)];
+    } else if (Utils.isUndefined(stationTemplate.numberOfConnectors) === false) {
       configuredMaxConnectors = stationTemplate.numberOfConnectors as number;
     } else {
       configuredMaxConnectors = stationTemplate?.Connectors[0]
@@ -174,32 +173,6 @@ export class ChargingStationUtils {
     return Configuration.getWorker().processType === WorkerProcessType.DYNAMIC_POOL;
   }
 
-  /**
-   * Convert websocket error code to human readable string message
-   *
-   * @param code websocket error code
-   * @returns human readable string message
-   */
-  public static getWebSocketCloseEventStatusString(code: number): string {
-    if (code >= 0 && code <= 999) {
-      return '(Unused)';
-    } else if (code >= 1016) {
-      if (code <= 1999) {
-        return '(For WebSocket standard)';
-      } else if (code <= 2999) {
-        return '(For WebSocket extensions)';
-      } else if (code <= 3999) {
-        return '(For libraries and frameworks)';
-      } else if (code <= 4999) {
-        return '(For applications)';
-      }
-    }
-    if (!Utils.isUndefined(WebSocketCloseEventStatusString[code])) {
-      return WebSocketCloseEventStatusString[code] as string;
-    }
-    return '(Unknown)';
-  }
-
   public static warnDeprecatedTemplateKey(
     template: ChargingStationTemplate,
     key: string,
@@ -238,7 +211,7 @@ export class ChargingStationUtils {
     delete stationTemplate.chargeBoxSerialNumberPrefix;
     delete stationTemplate.chargePointSerialNumberPrefix;
     delete stationTemplate.meterSerialNumberPrefix;
-    return stationTemplate;
+    return stationTemplate as unknown as ChargingStationInfo;
   }
 
   public static createStationInfoHash(stationInfo: ChargingStationInfo): void {
@@ -330,6 +303,7 @@ export class ChargingStationUtils {
     limit: number;
     matchingChargingProfile: ChargingProfile;
   } | null {
+    const debugLogMsg = `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Matching charging profile found for power limitation: %j`;
     for (const chargingProfile of chargingProfiles) {
       // Set helpers
       const currentMoment = moment();
@@ -373,10 +347,7 @@ export class ChargingStationUtils {
               limit: schedulePeriod.limit,
               matchingChargingProfile: chargingProfile,
             };
-            logger.debug(
-              `${logPrefix} Matching charging profile found for power limitation: %j`,
-              result
-            );
+            logger.debug(debugLogMsg, result);
             return result;
           }
           // Find the right schedule period
@@ -390,10 +361,7 @@ export class ChargingStationUtils {
               limit: lastButOneSchedule.limit,
               matchingChargingProfile: chargingProfile,
             };
-            logger.debug(
-              `${logPrefix} Matching charging profile found for power limitation: %j`,
-              result
-            );
+            logger.debug(debugLogMsg, result);
             return result;
           }
           // Keep it
@@ -409,10 +377,7 @@ export class ChargingStationUtils {
               limit: lastButOneSchedule.limit,
               matchingChargingProfile: chargingProfile,
             };
-            logger.debug(
-              `${logPrefix} Matching charging profile found for power limitation: %j`,
-              result
-            );
+            logger.debug(debugLogMsg, result);
             return result;
           }
         }
@@ -426,7 +391,7 @@ export class ChargingStationUtils {
     templateFile: string,
     logPrefix: string
   ): Voltage {
-    const errMsg = `${logPrefix} Unknown ${currentType} currentOutType in template file ${templateFile}, cannot define default voltage out`;
+    const errMsg = `Unknown ${currentType} currentOutType in template file ${templateFile}, cannot define default voltage out`;
     let defaultVoltageOut: number;
     switch (currentType) {
       case CurrentType.AC:
@@ -436,8 +401,8 @@ export class ChargingStationUtils {
         defaultVoltageOut = Voltage.VOLTAGE_400;
         break;
       default:
-        logger.error(errMsg);
-        throw new Error(errMsg);
+        logger.error(`${logPrefix} ${errMsg}`);
+        throw new BaseError(errMsg);
     }
     return defaultVoltageOut;
   }
@@ -449,7 +414,7 @@ export class ChargingStationUtils {
     phase?: MeterValuePhase
   ): SampledValueTemplate | undefined {
     const onPhaseStr = phase ? `on phase ${phase} ` : '';
-    if (!Constants.SUPPORTED_MEASURANDS.includes(measurand)) {
+    if (Constants.SUPPORTED_MEASURANDS.includes(measurand) === false) {
       logger.warn(
         `${chargingStation.logPrefix()} Trying to get unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId}`
       );
@@ -477,10 +442,10 @@ export class ChargingStationUtils {
       index++
     ) {
       if (
-        !Constants.SUPPORTED_MEASURANDS.includes(
+        Constants.SUPPORTED_MEASURANDS.includes(
           sampledValueTemplates[index]?.measurand ??
             MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
-        )
+        ) === false
       ) {
         logger.warn(
           `${chargingStation.logPrefix()} Unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId}`
@@ -492,7 +457,7 @@ export class ChargingStationUtils {
         ChargingStationConfigurationUtils.getConfigurationKey(
           chargingStation,
           StandardParametersKey.MeterValuesSampledData
-        )?.value.includes(measurand)
+        )?.value.includes(measurand) === true
       ) {
         return sampledValueTemplates[index];
       } else if (
@@ -502,7 +467,7 @@ export class ChargingStationUtils {
         ChargingStationConfigurationUtils.getConfigurationKey(
           chargingStation,
           StandardParametersKey.MeterValuesSampledData
-        )?.value.includes(measurand)
+        )?.value.includes(measurand) === true
       ) {
         return sampledValueTemplates[index];
       } else if (
@@ -514,9 +479,9 @@ export class ChargingStationUtils {
       }
     }
     if (measurand === MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER) {
-      const errorMsg = `${chargingStation.logPrefix()} Missing MeterValues for default measurand '${measurand}' in template on connectorId ${connectorId}`;
-      logger.error(errorMsg);
-      throw new Error(errorMsg);
+      const errorMsg = `Missing MeterValues for default measurand '${measurand}' in template on connectorId ${connectorId}`;
+      logger.error(`${chargingStation.logPrefix()} ${errorMsg}`);
+      throw new BaseError(errorMsg);
     }
     logger.debug(
       `${chargingStation.logPrefix()} No MeterValues for measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId}`
@@ -534,39 +499,43 @@ export class ChargingStationUtils {
     );
   }
 
-  public static isCommandSupported(
-    command: RequestCommand | IncomingRequestCommand,
+  public static isRequestCommandSupported(
+    command: RequestCommand,
     chargingStation: ChargingStation
   ): boolean {
-    const isIncomingRequestCommand = Object.values(IncomingRequestCommand).includes(
-      command as IncomingRequestCommand
-    );
-    const isRequestCommand = Object.values(RequestCommand).includes(command as RequestCommand);
+    const isRequestCommand = Object.values(RequestCommand).includes(command);
     if (
-      isIncomingRequestCommand &&
-      !chargingStation.stationInfo?.commandsSupport?.incomingCommands
+      isRequestCommand === true &&
+      !chargingStation.stationInfo?.commandsSupport?.outgoingCommands
     ) {
       return true;
     } else if (
-      isIncomingRequestCommand &&
-      chargingStation.stationInfo?.commandsSupport?.incomingCommands
+      isRequestCommand === true &&
+      chargingStation.stationInfo?.commandsSupport?.outgoingCommands
     ) {
-      return (
-        (chargingStation.stationInfo?.commandsSupport?.incomingCommands[command] as boolean) ??
-        false
-      );
-    } else if (
-      isRequestCommand &&
-      !chargingStation.stationInfo?.commandsSupport?.outgoingCommands
+      return chargingStation.stationInfo?.commandsSupport?.outgoingCommands[command] ?? false;
+    }
+    logger.error(`${chargingStation.logPrefix()} Unknown outgoing OCPP command '${command}'`);
+    return false;
+  }
+
+  public static isIncomingRequestCommandSupported(
+    command: IncomingRequestCommand,
+    chargingStation: ChargingStation
+  ): boolean {
+    const isIncomingRequestCommand = Object.values(IncomingRequestCommand).includes(command);
+    if (
+      isIncomingRequestCommand === true &&
+      !chargingStation.stationInfo?.commandsSupport?.incomingCommands
     ) {
       return true;
-    } else if (isRequestCommand && chargingStation.stationInfo?.commandsSupport?.outgoingCommands) {
-      return (
-        (chargingStation.stationInfo?.commandsSupport?.outgoingCommands[command] as boolean) ??
-        false
-      );
+    } else if (
+      isIncomingRequestCommand === true &&
+      chargingStation.stationInfo?.commandsSupport?.incomingCommands
+    ) {
+      return chargingStation.stationInfo?.commandsSupport?.incomingCommands[command] ?? false;
     }
-    logger.error(`${chargingStation.logPrefix()} Unknown OCPP command '${command}'`);
+    logger.error(`${chargingStation.logPrefix()} Unknown incoming OCPP command '${command}'`);
     return false;
   }