fix(simulator): fix empty array detection helper semantic
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16ServiceUtils.ts
index 741db1d32a948a910ae5a968dc24a7316014dd97..eceb21488f6e4e320a19d9d1df52bd6c43b98c31 100644 (file)
@@ -1,7 +1,13 @@
-// Partial Copyright Jerome Benoit. 2021. All Rights Reserved.
+// Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
+
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+import type { JSONSchemaType } from 'ajv';
 
 import OCPPError from '../../../exception/OCPPError';
 import { CurrentType, Voltage } from '../../../types/ChargingStationTemplate';
+import type { JsonType } from '../../../types/JsonType';
 import type {
   MeasurandPerPhaseSampledValueTemplates,
   SampledValueTemplate,
@@ -16,16 +22,17 @@ import {
   MeterValueContext,
   MeterValueLocation,
   MeterValueUnit,
-  OCPP16MeterValue,
+  type OCPP16MeterValue,
   OCPP16MeterValueMeasurand,
   OCPP16MeterValuePhase,
-  OCPP16SampledValue,
+  type OCPP16SampledValue,
 } from '../../../types/ocpp/1.6/MeterValues';
 import {
-  OCPP16IncomingRequestCommand,
+  type OCPP16IncomingRequestCommand,
   OCPP16RequestCommand,
 } from '../../../types/ocpp/1.6/Requests';
 import { ErrorType } from '../../../types/ocpp/ErrorType';
+import { OCPPVersion } from '../../../types/ocpp/OCPPVersion';
 import Constants from '../../../utils/Constants';
 import { ACElectricUtils, DCElectricUtils } from '../../../utils/ElectricUtils';
 import logger from '../../../utils/Logger';
@@ -58,7 +65,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
     debug = false
   ): OCPP16MeterValue {
     const meterValue: OCPP16MeterValue = {
-      timestamp: new Date().toISOString(),
+      timestamp: new Date(),
       sampledValue: [],
     };
     const connector = chargingStation.getConnectorStatus(connectorId);
@@ -84,7 +91,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
           `${chargingStation.logPrefix()} MeterValues measurand ${
             meterValue.sampledValue[sampledValuesIndex].measurand ??
             OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
-          }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
+          }: connectorId ${connectorId}, transaction ${connector?.transactionId}, value: ${
             meterValue.sampledValue[sampledValuesIndex].value
           }/100`
         );
@@ -145,7 +152,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
           OCPP16ServiceUtils.buildSampledValue(
             voltagePhaseLineToNeutralSampledValueTemplate ?? voltageSampledValueTemplate,
             voltagePhaseLineToNeutralMeasurandValue ?? voltageMeasurandValue,
-            null,
+            undefined,
             phaseLineToNeutralValue as OCPP16MeterValuePhase
           )
         );
@@ -184,7 +191,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
             OCPP16ServiceUtils.buildSampledValue(
               voltagePhaseLineToLineSampledValueTemplate ?? voltageSampledValueTemplate,
               voltagePhaseLineToLineMeasurandValue ?? defaultVoltagePhaseLineToLineMeasurandValue,
-              null,
+              undefined,
               phaseLineToLineValue as OCPP16MeterValuePhase
             )
           );
@@ -355,7 +362,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
           `${chargingStation.logPrefix()} MeterValues measurand ${
             meterValue.sampledValue[sampledValuesIndex].measurand ??
             OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
-          }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
+          }: connectorId ${connectorId}, transaction ${connector?.transactionId}, value: ${
             meterValue.sampledValue[sampledValuesIndex].value
           }/${connectorMaximumPowerRounded}`
         );
@@ -371,7 +378,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
             (powerPerPhaseSampledValueTemplates[`L${phase}`] as SampledValueTemplate) ??
               powerSampledValueTemplate,
             powerMeasurandValues[`L${phase}`] as number,
-            null,
+            undefined,
             phaseValue as OCPP16MeterValuePhase
           )
         );
@@ -391,7 +398,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
               OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
             }: phase ${
               meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
-            }, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
+            }, connectorId ${connectorId}, transaction ${connector?.transactionId}, value: ${
               meterValue.sampledValue[sampledValuesPerPhaseIndex].value
             }/${connectorMaximumPowerPerPhaseRounded}`
           );
@@ -567,7 +574,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
           `${chargingStation.logPrefix()} MeterValues measurand ${
             meterValue.sampledValue[sampledValuesIndex].measurand ??
             OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
-          }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
+          }: connectorId ${connectorId}, transaction ${connector?.transactionId}, value: ${
             meterValue.sampledValue[sampledValuesIndex].value
           }/${connectorMaximumAmperage}`
         );
@@ -583,7 +590,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
             (currentPerPhaseSampledValueTemplates[phaseValue] as SampledValueTemplate) ??
               currentSampledValueTemplate,
             currentMeasurandValues[phaseValue] as number,
-            null,
+            undefined,
             phaseValue as OCPP16MeterValuePhase
           )
         );
@@ -599,7 +606,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
               OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
             }: phase ${
               meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
-            }, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
+            }, connectorId ${connectorId}, transaction ${connector?.transactionId}, value: ${
               meterValue.sampledValue[sampledValuesPerPhaseIndex].value
             }/${connectorMaximumAmperage}`
           );
@@ -641,9 +648,9 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
       // Persist previous value on connector
       if (
         connector &&
-        !Utils.isNullOrUndefined(connector.energyActiveImportRegisterValue) &&
+        Utils.isNullOrUndefined(connector.energyActiveImportRegisterValue) === false &&
         connector.energyActiveImportRegisterValue >= 0 &&
-        !Utils.isNullOrUndefined(connector.transactionEnergyActiveImportRegisterValue) &&
+        Utils.isNullOrUndefined(connector.transactionEnergyActiveImportRegisterValue) === false &&
         connector.transactionEnergyActiveImportRegisterValue >= 0
       ) {
         connector.energyActiveImportRegisterValue += energyValueRounded;
@@ -669,7 +676,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
             meterValue.sampledValue[sampledValuesIndex].measurand ??
             OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
           }: connectorId ${connectorId}, transaction ${
-            connector.transactionId
+            connector?.transactionId
           }, value: ${energyValueRounded}/${connectorMaximumEnergyRounded}, duration: ${Utils.roundTo(
             interval / (3600 * 1000),
             4
@@ -686,7 +693,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
     meterStart: number
   ): OCPP16MeterValue {
     const meterValue: OCPP16MeterValue = {
-      timestamp: new Date().toISOString(),
+      timestamp: new Date(),
       sampledValue: [],
     };
     // Energy.Active.Import.Register measurand (default)
@@ -698,7 +705,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
     meterValue.sampledValue.push(
       OCPP16ServiceUtils.buildSampledValue(
         sampledValueTemplate,
-        Utils.roundTo(meterStart / unitDivider, 4),
+        Utils.roundTo((meterStart ?? 0) / unitDivider, 4),
         MeterValueContext.TRANSACTION_BEGIN
       )
     );
@@ -711,7 +718,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
     meterStop: number
   ): OCPP16MeterValue {
     const meterValue: OCPP16MeterValue = {
-      timestamp: new Date().toISOString(),
+      timestamp: new Date(),
       sampledValue: [],
     };
     // Energy.Active.Import.Register measurand (default)
@@ -723,7 +730,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
     meterValue.sampledValue.push(
       OCPP16ServiceUtils.buildSampledValue(
         sampledValueTemplate,
-        Utils.roundTo(meterStop / unitDivider, 4),
+        Utils.roundTo((meterStop ?? 0) / unitDivider, 4),
         MeterValueContext.TRANSACTION_END
       )
     );
@@ -745,23 +752,27 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
     connectorId: number,
     cp: OCPP16ChargingProfile
   ): void {
-    if (Utils.isNullOrUndefined(chargingStation.getConnectorStatus(connectorId).chargingProfiles)) {
+    if (
+      Utils.isNullOrUndefined(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles)
+    ) {
       logger.error(
         `${chargingStation.logPrefix()} Trying to set a charging profile on connectorId ${connectorId} with an uninitialized charging profiles array attribute, applying deferred initialization`
       );
       chargingStation.getConnectorStatus(connectorId).chargingProfiles = [];
     }
-    if (Array.isArray(chargingStation.getConnectorStatus(connectorId).chargingProfiles) === false) {
+    if (
+      Array.isArray(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles) === false
+    ) {
       logger.error(
         `${chargingStation.logPrefix()} Trying to set a charging profile on connectorId ${connectorId} with an improper attribute type for the charging profiles array, applying proper type initialization`
       );
       chargingStation.getConnectorStatus(connectorId).chargingProfiles = [];
     }
     let cpReplaced = false;
-    if (!Utils.isEmptyArray(chargingStation.getConnectorStatus(connectorId).chargingProfiles)) {
+    if (Utils.isNotEmptyArray(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles)) {
       chargingStation
         .getConnectorStatus(connectorId)
-        .chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => {
+        ?.chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => {
           if (
             chargingProfile.chargingProfileId === cp.chargingProfileId ||
             (chargingProfile.stackLevel === cp.stackLevel &&
@@ -772,7 +783,20 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
           }
         });
     }
-    !cpReplaced && chargingStation.getConnectorStatus(connectorId).chargingProfiles?.push(cp);
+    !cpReplaced && chargingStation.getConnectorStatus(connectorId)?.chargingProfiles?.push(cp);
+  }
+
+  public static parseJsonSchemaFile<T extends JsonType>(
+    relativePath: string,
+    moduleName?: string,
+    methodName?: string
+  ): JSONSchemaType<T> {
+    return super.parseJsonSchemaFile<T>(
+      path.resolve(path.dirname(fileURLToPath(import.meta.url)), relativePath),
+      OCPPVersion.VERSION_16,
+      moduleName,
+      methodName
+    );
   }
 
   private static buildSampledValue(