build(ci): fix linter errors
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16ServiceUtils.ts
index 537449dfbcc55a3149f5c25792ac3982b1640c3c..b194e08e8d5a58db9641a337bd58b7d27aa5799f 100644 (file)
@@ -2,6 +2,7 @@
 
 import type { JSONSchemaType } from 'ajv';
 import {
+  type Interval,
   addSeconds,
   areIntervalsOverlapping,
   differenceInSeconds,
@@ -45,7 +46,6 @@ import {
   type OCPP16SupportedFeatureProfiles,
   OCPPVersion,
   type SampledValueTemplate,
-  Voltage,
 } from '../../../types';
 import {
   ACElectricUtils,
@@ -57,6 +57,7 @@ import {
   getRandomFloatRounded,
   getRandomInteger,
   isNotEmptyArray,
+  isNotEmptyString,
   isNullOrUndefined,
   isUndefined,
   logger,
@@ -102,7 +103,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
     if (socSampledValueTemplate) {
       const socMaximumValue = 100;
       const socMinimumValue = socSampledValueTemplate.minimumValue ?? 0;
-      const socSampledValueTemplateValue = socSampledValueTemplate.value
+      const socSampledValueTemplateValue = isNotEmptyString(socSampledValueTemplate.value)
         ? getRandomFloatFluctuatedRounded(
             parseInt(socSampledValueTemplate.value),
             socSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT,
@@ -123,7 +124,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
             OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
           }: connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${socMinimumValue}/${
             meterValue.sampledValue[sampledValuesIndex].value
-          }/${socMaximumValue}}`,
+          }/${socMaximumValue}`,
         );
       }
     }
@@ -134,9 +135,9 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
       OCPP16MeterValueMeasurand.VOLTAGE,
     );
     if (voltageSampledValueTemplate) {
-      const voltageSampledValueTemplateValue = voltageSampledValueTemplate.value
+      const voltageSampledValueTemplateValue = isNotEmptyString(voltageSampledValueTemplate.value)
         ? parseInt(voltageSampledValueTemplate.value)
-        : chargingStation.getVoltageOut();
+        : chargingStation.stationInfo.voltageOut!;
       const fluctuationPercent =
         voltageSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT;
       const voltageMeasurandValue = getRandomFloatFluctuatedRounded(
@@ -145,7 +146,8 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
       );
       if (
         chargingStation.getNumberOfPhases() !== 3 ||
-        (chargingStation.getNumberOfPhases() === 3 && chargingStation.getMainVoltageMeterValues())
+        (chargingStation.getNumberOfPhases() === 3 &&
+          chargingStation.stationInfo?.mainVoltageMeterValues)
       ) {
         meterValue.sampledValue.push(
           OCPP16ServiceUtils.buildSampledValue(voltageSampledValueTemplate, voltageMeasurandValue),
@@ -166,10 +168,11 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
           );
         let voltagePhaseLineToNeutralMeasurandValue: number | undefined;
         if (voltagePhaseLineToNeutralSampledValueTemplate) {
-          const voltagePhaseLineToNeutralSampledValueTemplateValue =
-            voltagePhaseLineToNeutralSampledValueTemplate.value
-              ? parseInt(voltagePhaseLineToNeutralSampledValueTemplate.value)
-              : chargingStation.getVoltageOut();
+          const voltagePhaseLineToNeutralSampledValueTemplateValue = isNotEmptyString(
+            voltagePhaseLineToNeutralSampledValueTemplate.value,
+          )
+            ? parseInt(voltagePhaseLineToNeutralSampledValueTemplate.value)
+            : chargingStation.stationInfo.voltageOut!;
           const fluctuationPhaseToNeutralPercent =
             voltagePhaseLineToNeutralSampledValueTemplate.fluctuationPercent ??
             Constants.DEFAULT_FLUCTUATION_PERCENT;
@@ -186,12 +189,17 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
             phaseLineToNeutralValue as OCPP16MeterValuePhase,
           ),
         );
-        if (chargingStation.getPhaseLineToLineVoltageMeterValues()) {
+        if (chargingStation.stationInfo?.phaseLineToLineVoltageMeterValues) {
           const phaseLineToLineValue = `L${phase}-L${
             (phase + 1) % chargingStation.getNumberOfPhases() !== 0
               ? (phase + 1) % chargingStation.getNumberOfPhases()
               : chargingStation.getNumberOfPhases()
           }`;
+          const voltagePhaseLineToLineValueRounded = roundTo(
+            Math.sqrt(chargingStation.getNumberOfPhases()) *
+              chargingStation.stationInfo.voltageOut!,
+            2,
+          );
           const voltagePhaseLineToLineSampledValueTemplate =
             OCPP16ServiceUtils.getSampledValueTemplate(
               chargingStation,
@@ -201,10 +209,11 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
             );
           let voltagePhaseLineToLineMeasurandValue: number | undefined;
           if (voltagePhaseLineToLineSampledValueTemplate) {
-            const voltagePhaseLineToLineSampledValueTemplateValue =
-              voltagePhaseLineToLineSampledValueTemplate.value
-                ? parseInt(voltagePhaseLineToLineSampledValueTemplate.value)
-                : Voltage.VOLTAGE_400;
+            const voltagePhaseLineToLineSampledValueTemplateValue = isNotEmptyString(
+              voltagePhaseLineToLineSampledValueTemplate.value,
+            )
+              ? parseInt(voltagePhaseLineToLineSampledValueTemplate.value)
+              : voltagePhaseLineToLineValueRounded;
             const fluctuationPhaseLineToLinePercent =
               voltagePhaseLineToLineSampledValueTemplate.fluctuationPercent ??
               Constants.DEFAULT_FLUCTUATION_PERCENT;
@@ -214,7 +223,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
             );
           }
           const defaultVoltagePhaseLineToLineMeasurandValue = getRandomFloatFluctuatedRounded(
-            Voltage.VOLTAGE_400,
+            voltagePhaseLineToLineValueRounded,
             fluctuationPercent,
           );
           meterValue.sampledValue.push(
@@ -265,7 +274,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
       const errMsg = `MeterValues measurand ${
         powerSampledValueTemplate.measurand ??
         OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
-      }: Unknown ${chargingStation.getCurrentOutType()} currentOutType in template file ${
+      }: Unknown ${chargingStation.stationInfo?.currentOutType} currentOutType in template file ${
         chargingStation.templateFile
       }, cannot calculate ${
         powerSampledValueTemplate.measurand ??
@@ -279,85 +288,116 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
       const connectorMaximumPowerPerPhase = Math.round(
         connectorMaximumAvailablePower / chargingStation.getNumberOfPhases(),
       );
-      const connectorMinimumPower = Math.round(powerSampledValueTemplate.minimumValue!) ?? 0;
+      const connectorMinimumPower = Math.round(powerSampledValueTemplate.minimumValue ?? 0);
       const connectorMinimumPowerPerPhase = Math.round(
         connectorMinimumPower / chargingStation.getNumberOfPhases(),
       );
-      switch (chargingStation.getCurrentOutType()) {
+      switch (chargingStation.stationInfo?.currentOutType) {
         case CurrentType.AC:
           if (chargingStation.getNumberOfPhases() === 3) {
-            const defaultFluctuatedPowerPerPhase =
-              powerSampledValueTemplate.value &&
-              getRandomFloatFluctuatedRounded(
-                OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
-                  powerSampledValueTemplate.value,
-                  connectorMaximumPower / unitDivider,
-                  { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
-                ) / chargingStation.getNumberOfPhases(),
-                powerSampledValueTemplate.fluctuationPercent ??
-                  Constants.DEFAULT_FLUCTUATION_PERCENT,
-              );
-            const phase1FluctuatedValue =
-              powerPerPhaseSampledValueTemplates.L1?.value &&
-              getRandomFloatFluctuatedRounded(
-                OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
-                  powerPerPhaseSampledValueTemplates.L1.value,
-                  connectorMaximumPowerPerPhase / unitDivider,
-                  { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
-                ),
-                powerPerPhaseSampledValueTemplates.L1.fluctuationPercent ??
-                  Constants.DEFAULT_FLUCTUATION_PERCENT,
-              );
-            const phase2FluctuatedValue =
-              powerPerPhaseSampledValueTemplates.L2?.value &&
-              getRandomFloatFluctuatedRounded(
-                OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
-                  powerPerPhaseSampledValueTemplates.L2.value,
-                  connectorMaximumPowerPerPhase / unitDivider,
-                  { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
-                ),
-                powerPerPhaseSampledValueTemplates.L2.fluctuationPercent ??
-                  Constants.DEFAULT_FLUCTUATION_PERCENT,
-              );
-            const phase3FluctuatedValue =
-              powerPerPhaseSampledValueTemplates.L3?.value &&
-              getRandomFloatFluctuatedRounded(
-                OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
-                  powerPerPhaseSampledValueTemplates.L3.value,
-                  connectorMaximumPowerPerPhase / unitDivider,
-                  { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
-                ),
-                powerPerPhaseSampledValueTemplates.L3.fluctuationPercent ??
-                  Constants.DEFAULT_FLUCTUATION_PERCENT,
-              );
+            const defaultFluctuatedPowerPerPhase = isNotEmptyString(powerSampledValueTemplate.value)
+              ? getRandomFloatFluctuatedRounded(
+                  OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+                    powerSampledValueTemplate.value,
+                    connectorMaximumPower / unitDivider,
+                    connectorMinimumPower / unitDivider,
+                    {
+                      limitationEnabled:
+                        chargingStation.stationInfo?.customValueLimitationMeterValues,
+                      fallbackValue: connectorMinimumPower / unitDivider,
+                    },
+                  ) / chargingStation.getNumberOfPhases(),
+                  powerSampledValueTemplate.fluctuationPercent ??
+                    Constants.DEFAULT_FLUCTUATION_PERCENT,
+                )
+              : undefined;
+            const phase1FluctuatedValue = isNotEmptyString(
+              powerPerPhaseSampledValueTemplates.L1?.value,
+            )
+              ? getRandomFloatFluctuatedRounded(
+                  OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+                    powerPerPhaseSampledValueTemplates.L1?.value,
+                    connectorMaximumPowerPerPhase / unitDivider,
+                    connectorMinimumPowerPerPhase / unitDivider,
+                    {
+                      limitationEnabled:
+                        chargingStation.stationInfo?.customValueLimitationMeterValues,
+                      fallbackValue: connectorMinimumPowerPerPhase / unitDivider,
+                    },
+                  ),
+                  powerPerPhaseSampledValueTemplates.L1?.fluctuationPercent ??
+                    Constants.DEFAULT_FLUCTUATION_PERCENT,
+                )
+              : undefined;
+            const phase2FluctuatedValue = isNotEmptyString(
+              powerPerPhaseSampledValueTemplates.L2?.value,
+            )
+              ? getRandomFloatFluctuatedRounded(
+                  OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+                    powerPerPhaseSampledValueTemplates.L2?.value,
+                    connectorMaximumPowerPerPhase / unitDivider,
+                    connectorMinimumPowerPerPhase / unitDivider,
+                    {
+                      limitationEnabled:
+                        chargingStation.stationInfo?.customValueLimitationMeterValues,
+                      fallbackValue: connectorMinimumPowerPerPhase / unitDivider,
+                    },
+                  ),
+                  powerPerPhaseSampledValueTemplates.L2?.fluctuationPercent ??
+                    Constants.DEFAULT_FLUCTUATION_PERCENT,
+                )
+              : undefined;
+            const phase3FluctuatedValue = isNotEmptyString(
+              powerPerPhaseSampledValueTemplates.L3?.value,
+            )
+              ? getRandomFloatFluctuatedRounded(
+                  OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+                    powerPerPhaseSampledValueTemplates.L3?.value,
+                    connectorMaximumPowerPerPhase / unitDivider,
+                    connectorMinimumPowerPerPhase / unitDivider,
+                    {
+                      limitationEnabled:
+                        chargingStation.stationInfo?.customValueLimitationMeterValues,
+                      fallbackValue: connectorMinimumPowerPerPhase / unitDivider,
+                    },
+                  ),
+                  powerPerPhaseSampledValueTemplates.L3?.fluctuationPercent ??
+                    Constants.DEFAULT_FLUCTUATION_PERCENT,
+                )
+              : undefined;
             powerMeasurandValues.L1 =
-              (phase1FluctuatedValue as number) ??
-              (defaultFluctuatedPowerPerPhase as number) ??
+              phase1FluctuatedValue ??
+              defaultFluctuatedPowerPerPhase ??
               getRandomFloatRounded(
                 connectorMaximumPowerPerPhase / unitDivider,
                 connectorMinimumPowerPerPhase / unitDivider,
               );
             powerMeasurandValues.L2 =
-              (phase2FluctuatedValue as number) ??
-              (defaultFluctuatedPowerPerPhase as number) ??
+              phase2FluctuatedValue ??
+              defaultFluctuatedPowerPerPhase ??
               getRandomFloatRounded(
                 connectorMaximumPowerPerPhase / unitDivider,
                 connectorMinimumPowerPerPhase / unitDivider,
               );
             powerMeasurandValues.L3 =
-              (phase3FluctuatedValue as number) ??
-              (defaultFluctuatedPowerPerPhase as number) ??
+              phase3FluctuatedValue ??
+              defaultFluctuatedPowerPerPhase ??
               getRandomFloatRounded(
                 connectorMaximumPowerPerPhase / unitDivider,
                 connectorMinimumPowerPerPhase / unitDivider,
               );
           } else {
-            powerMeasurandValues.L1 = powerSampledValueTemplate.value
+            powerMeasurandValues.L1 = isNotEmptyString(powerSampledValueTemplate.value)
               ? getRandomFloatFluctuatedRounded(
                   OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
                     powerSampledValueTemplate.value,
                     connectorMaximumPower / unitDivider,
-                    { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
+                    connectorMinimumPower / unitDivider,
+                    {
+                      limitationEnabled:
+                        chargingStation.stationInfo?.customValueLimitationMeterValues,
+                      fallbackValue: connectorMinimumPower / unitDivider,
+                    },
                   ),
                   powerSampledValueTemplate.fluctuationPercent ??
                     Constants.DEFAULT_FLUCTUATION_PERCENT,
@@ -375,12 +415,17 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
           );
           break;
         case CurrentType.DC:
-          powerMeasurandValues.allPhases = powerSampledValueTemplate.value
+          powerMeasurandValues.allPhases = isNotEmptyString(powerSampledValueTemplate.value)
             ? getRandomFloatFluctuatedRounded(
                 OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
                   powerSampledValueTemplate.value,
                   connectorMaximumPower / unitDivider,
-                  { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
+                  connectorMinimumPower / unitDivider,
+                  {
+                    limitationEnabled:
+                      chargingStation.stationInfo?.customValueLimitationMeterValues,
+                    fallbackValue: connectorMinimumPower / unitDivider,
+                  },
                 ),
                 powerSampledValueTemplate.fluctuationPercent ??
                   Constants.DEFAULT_FLUCTUATION_PERCENT,
@@ -429,7 +474,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
           OCPP16ServiceUtils.buildSampledValue(
             powerPerPhaseSampledValueTemplates[
               `L${phase}` as keyof MeasurandPerPhaseSampledValueTemplates
-            ]! ?? powerSampledValueTemplate,
+            ] ?? powerSampledValueTemplate,
             powerMeasurandValues[`L${phase}` as keyof MeasurandPerPhaseSampledValueTemplates],
             undefined,
             phaseValue as OCPP16MeterValuePhase,
@@ -501,7 +546,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
       const errMsg = `MeterValues measurand ${
         currentSampledValueTemplate.measurand ??
         OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
-      }: Unknown ${chargingStation.getCurrentOutType()} currentOutType in template file ${
+      }: Unknown ${chargingStation.stationInfo?.currentOutType} currentOutType in template file ${
         chargingStation.templateFile
       }, cannot calculate ${
         currentSampledValueTemplate.measurand ??
@@ -512,77 +557,110 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
         chargingStation.getConnectorMaximumAvailablePower(connectorId);
       const connectorMinimumAmperage = currentSampledValueTemplate.minimumValue ?? 0;
       let connectorMaximumAmperage: number;
-      switch (chargingStation.getCurrentOutType()) {
+      switch (chargingStation.stationInfo?.currentOutType) {
         case CurrentType.AC:
           connectorMaximumAmperage = ACElectricUtils.amperagePerPhaseFromPower(
             chargingStation.getNumberOfPhases(),
             connectorMaximumAvailablePower,
-            chargingStation.getVoltageOut(),
+            chargingStation.stationInfo.voltageOut!,
           );
           if (chargingStation.getNumberOfPhases() === 3) {
-            const defaultFluctuatedAmperagePerPhase =
-              currentSampledValueTemplate.value &&
-              getRandomFloatFluctuatedRounded(
-                OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
-                  currentSampledValueTemplate.value,
-                  connectorMaximumAmperage,
-                  { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
-                ),
-                currentSampledValueTemplate.fluctuationPercent ??
-                  Constants.DEFAULT_FLUCTUATION_PERCENT,
-              );
-            const phase1FluctuatedValue =
-              currentPerPhaseSampledValueTemplates.L1?.value &&
-              getRandomFloatFluctuatedRounded(
-                OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
-                  currentPerPhaseSampledValueTemplates.L1.value,
-                  connectorMaximumAmperage,
-                  { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
-                ),
-                currentPerPhaseSampledValueTemplates.L1.fluctuationPercent ??
-                  Constants.DEFAULT_FLUCTUATION_PERCENT,
-              );
-            const phase2FluctuatedValue =
-              currentPerPhaseSampledValueTemplates.L2?.value &&
-              getRandomFloatFluctuatedRounded(
-                OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
-                  currentPerPhaseSampledValueTemplates.L2.value,
-                  connectorMaximumAmperage,
-                  { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
-                ),
-                currentPerPhaseSampledValueTemplates.L2.fluctuationPercent ??
-                  Constants.DEFAULT_FLUCTUATION_PERCENT,
-              );
-            const phase3FluctuatedValue =
-              currentPerPhaseSampledValueTemplates.L3?.value &&
-              getRandomFloatFluctuatedRounded(
-                OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
-                  currentPerPhaseSampledValueTemplates.L3.value,
-                  connectorMaximumAmperage,
-                  { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
-                ),
-                currentPerPhaseSampledValueTemplates.L3.fluctuationPercent ??
-                  Constants.DEFAULT_FLUCTUATION_PERCENT,
-              );
+            const defaultFluctuatedAmperagePerPhase = isNotEmptyString(
+              currentSampledValueTemplate.value,
+            )
+              ? getRandomFloatFluctuatedRounded(
+                  OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+                    currentSampledValueTemplate.value,
+                    connectorMaximumAmperage,
+                    connectorMinimumAmperage,
+                    {
+                      limitationEnabled:
+                        chargingStation.stationInfo?.customValueLimitationMeterValues,
+                      fallbackValue: connectorMinimumAmperage,
+                    },
+                  ),
+                  currentSampledValueTemplate.fluctuationPercent ??
+                    Constants.DEFAULT_FLUCTUATION_PERCENT,
+                )
+              : undefined;
+            const phase1FluctuatedValue = isNotEmptyString(
+              currentPerPhaseSampledValueTemplates.L1?.value,
+            )
+              ? getRandomFloatFluctuatedRounded(
+                  OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+                    currentPerPhaseSampledValueTemplates.L1?.value,
+                    connectorMaximumAmperage,
+                    connectorMinimumAmperage,
+                    {
+                      limitationEnabled:
+                        chargingStation.stationInfo?.customValueLimitationMeterValues,
+                      fallbackValue: connectorMinimumAmperage,
+                    },
+                  ),
+                  currentPerPhaseSampledValueTemplates.L1?.fluctuationPercent ??
+                    Constants.DEFAULT_FLUCTUATION_PERCENT,
+                )
+              : undefined;
+            const phase2FluctuatedValue = isNotEmptyString(
+              currentPerPhaseSampledValueTemplates.L2?.value,
+            )
+              ? getRandomFloatFluctuatedRounded(
+                  OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+                    currentPerPhaseSampledValueTemplates.L2?.value,
+                    connectorMaximumAmperage,
+                    connectorMinimumAmperage,
+                    {
+                      limitationEnabled:
+                        chargingStation.stationInfo?.customValueLimitationMeterValues,
+                      fallbackValue: connectorMinimumAmperage,
+                    },
+                  ),
+                  currentPerPhaseSampledValueTemplates.L2?.fluctuationPercent ??
+                    Constants.DEFAULT_FLUCTUATION_PERCENT,
+                )
+              : undefined;
+            const phase3FluctuatedValue = isNotEmptyString(
+              currentPerPhaseSampledValueTemplates.L3?.value,
+            )
+              ? getRandomFloatFluctuatedRounded(
+                  OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+                    currentPerPhaseSampledValueTemplates.L3?.value,
+                    connectorMaximumAmperage,
+                    connectorMinimumAmperage,
+                    {
+                      limitationEnabled:
+                        chargingStation.stationInfo?.customValueLimitationMeterValues,
+                      fallbackValue: connectorMinimumAmperage,
+                    },
+                  ),
+                  currentPerPhaseSampledValueTemplates.L3?.fluctuationPercent ??
+                    Constants.DEFAULT_FLUCTUATION_PERCENT,
+                )
+              : undefined;
             currentMeasurandValues.L1 =
-              (phase1FluctuatedValue as number) ??
-              (defaultFluctuatedAmperagePerPhase as number) ??
+              phase1FluctuatedValue ??
+              defaultFluctuatedAmperagePerPhase ??
               getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage);
             currentMeasurandValues.L2 =
-              (phase2FluctuatedValue as number) ??
-              (defaultFluctuatedAmperagePerPhase as number) ??
+              phase2FluctuatedValue ??
+              defaultFluctuatedAmperagePerPhase ??
               getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage);
             currentMeasurandValues.L3 =
-              (phase3FluctuatedValue as number) ??
-              (defaultFluctuatedAmperagePerPhase as number) ??
+              phase3FluctuatedValue ??
+              defaultFluctuatedAmperagePerPhase ??
               getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage);
           } else {
-            currentMeasurandValues.L1 = currentSampledValueTemplate.value
+            currentMeasurandValues.L1 = isNotEmptyString(currentSampledValueTemplate.value)
               ? getRandomFloatFluctuatedRounded(
                   OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
                     currentSampledValueTemplate.value,
                     connectorMaximumAmperage,
-                    { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
+                    connectorMinimumAmperage,
+                    {
+                      limitationEnabled:
+                        chargingStation.stationInfo?.customValueLimitationMeterValues,
+                      fallbackValue: connectorMinimumAmperage,
+                    },
                   ),
                   currentSampledValueTemplate.fluctuationPercent ??
                     Constants.DEFAULT_FLUCTUATION_PERCENT,
@@ -600,14 +678,19 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
         case CurrentType.DC:
           connectorMaximumAmperage = DCElectricUtils.amperage(
             connectorMaximumAvailablePower,
-            chargingStation.getVoltageOut(),
+            chargingStation.stationInfo.voltageOut!,
           );
-          currentMeasurandValues.allPhases = currentSampledValueTemplate.value
+          currentMeasurandValues.allPhases = isNotEmptyString(currentSampledValueTemplate.value)
             ? getRandomFloatFluctuatedRounded(
                 OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
                   currentSampledValueTemplate.value,
                   connectorMaximumAmperage,
-                  { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
+                  connectorMinimumAmperage,
+                  {
+                    limitationEnabled:
+                      chargingStation.stationInfo?.customValueLimitationMeterValues,
+                    fallbackValue: connectorMinimumAmperage,
+                  },
                 ),
                 currentSampledValueTemplate.fluctuationPercent ??
                   Constants.DEFAULT_FLUCTUATION_PERCENT,
@@ -651,7 +734,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
           OCPP16ServiceUtils.buildSampledValue(
             currentPerPhaseSampledValueTemplates[
               phaseValue as keyof MeasurandPerPhaseSampledValueTemplates
-            ]! ?? currentSampledValueTemplate,
+            ] ?? currentSampledValueTemplate,
             currentMeasurandValues[phaseValue as keyof MeasurandPerPhaseSampledValueTemplates],
             undefined,
             phaseValue as OCPP16MeterValuePhase,
@@ -696,20 +779,25 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
         (connectorMaximumAvailablePower * interval) / (3600 * 1000),
         2,
       );
-      const energyValueRounded = energySampledValueTemplate.value
-        ? // Cumulate the fluctuated value around the static one
-          getRandomFloatFluctuatedRounded(
+      const connectorMinimumEnergyRounded = roundTo(
+        energySampledValueTemplate.minimumValue ?? 0,
+        2,
+      );
+      const energyValueRounded = isNotEmptyString(energySampledValueTemplate.value)
+        ? getRandomFloatFluctuatedRounded(
             OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
               energySampledValueTemplate.value,
               connectorMaximumEnergyRounded,
+              connectorMinimumEnergyRounded,
               {
-                limitationEnabled: chargingStation.getCustomValueLimitationMeterValues(),
+                limitationEnabled: chargingStation.stationInfo?.customValueLimitationMeterValues,
+                fallbackValue: connectorMinimumEnergyRounded,
                 unitMultiplier: unitDivider,
               },
             ),
             energySampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT,
           )
-        : getRandomFloatRounded(connectorMaximumEnergyRounded);
+        : getRandomFloatRounded(connectorMaximumEnergyRounded, connectorMinimumEnergyRounded);
       // Persist previous value on connector
       if (connector) {
         if (
@@ -736,12 +824,16 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
         ),
       );
       const sampledValuesIndex = meterValue.sampledValue.length - 1;
-      if (energyValueRounded > connectorMaximumEnergyRounded || debug) {
+      if (
+        energyValueRounded > connectorMaximumEnergyRounded ||
+        energyValueRounded < connectorMinimumEnergyRounded ||
+        debug
+      ) {
         logger.error(
           `${chargingStation.logPrefix()} MeterValues measurand ${
             meterValue.sampledValue[sampledValuesIndex].measurand ??
             OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
-          }: connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${energyValueRounded}/${connectorMaximumEnergyRounded}, duration: ${interval}ms`,
+          }: connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${connectorMinimumEnergyRounded}/${energyValueRounded}/${connectorMaximumEnergyRounded}, duration: ${interval}ms`,
         );
       }
     }
@@ -935,21 +1027,21 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
   public static composeChargingSchedules = (
     chargingScheduleHigher: OCPP16ChargingSchedule | undefined,
     chargingScheduleLower: OCPP16ChargingSchedule | undefined,
-    targetInterval: Interval,
+    compositeInterval: Interval,
   ): OCPP16ChargingSchedule | undefined => {
     if (!chargingScheduleHigher && !chargingScheduleLower) {
       return undefined;
     }
     if (chargingScheduleHigher && !chargingScheduleLower) {
-      return OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleHigher, targetInterval);
+      return OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleHigher, compositeInterval);
     }
     if (!chargingScheduleHigher && chargingScheduleLower) {
-      return OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleLower, targetInterval);
+      return OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleLower, compositeInterval);
     }
     const compositeChargingScheduleHigher: OCPP16ChargingSchedule | undefined =
-      OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleHigher!, targetInterval);
+      OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleHigher!, compositeInterval);
     const compositeChargingScheduleLower: OCPP16ChargingSchedule | undefined =
-      OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleLower!, targetInterval);
+      OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleLower!, compositeInterval);
     const compositeChargingScheduleHigherInterval: Interval = {
       start: compositeChargingScheduleHigher!.startSchedule!,
       end: addSeconds(
@@ -1086,7 +1178,6 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
                 },
               )
             ) {
-              schedulePeriod.startPeriod = 0;
               return false;
             }
             if (
@@ -1106,7 +1197,10 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
             }
             return true;
           })
-          .map((schedulePeriod) => {
+          .map((schedulePeriod, index) => {
+            if (index === 0 && schedulePeriod.startPeriod !== 0) {
+              schedulePeriod.startPeriod = 0;
+            }
             return {
               ...schedulePeriod,
               startPeriod: higherFirst
@@ -1165,25 +1259,28 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
 
   private static composeChargingSchedule = (
     chargingSchedule: OCPP16ChargingSchedule,
-    targetInterval: Interval,
+    compositeInterval: Interval,
   ): OCPP16ChargingSchedule | undefined => {
     const chargingScheduleInterval: Interval = {
       start: chargingSchedule.startSchedule!,
       end: addSeconds(chargingSchedule.startSchedule!, chargingSchedule.duration!),
     };
-    if (areIntervalsOverlapping(chargingScheduleInterval, targetInterval)) {
+    if (areIntervalsOverlapping(chargingScheduleInterval, compositeInterval)) {
       chargingSchedule.chargingSchedulePeriod.sort((a, b) => a.startPeriod - b.startPeriod);
-      if (isBefore(chargingScheduleInterval.start, targetInterval.start)) {
+      if (isBefore(chargingScheduleInterval.start, compositeInterval.start)) {
         return {
           ...chargingSchedule,
-          startSchedule: targetInterval.start as Date,
-          duration: differenceInSeconds(chargingScheduleInterval.end, targetInterval.start as Date),
-          chargingSchedulePeriod: chargingSchedule.chargingSchedulePeriod.filter(
-            (schedulePeriod, index) => {
+          startSchedule: compositeInterval.start as Date,
+          duration: differenceInSeconds(
+            chargingScheduleInterval.end,
+            compositeInterval.start as Date,
+          ),
+          chargingSchedulePeriod: chargingSchedule.chargingSchedulePeriod
+            .filter((schedulePeriod, index) => {
               if (
                 isWithinInterval(
                   addSeconds(chargingScheduleInterval.start, schedulePeriod.startPeriod)!,
-                  targetInterval,
+                  compositeInterval,
                 )
               ) {
                 return true;
@@ -1192,32 +1289,39 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
                 index < chargingSchedule.chargingSchedulePeriod.length - 1 &&
                 !isWithinInterval(
                   addSeconds(chargingScheduleInterval.start, schedulePeriod.startPeriod),
-                  targetInterval,
+                  compositeInterval,
                 ) &&
                 isWithinInterval(
                   addSeconds(
                     chargingScheduleInterval.start,
                     chargingSchedule.chargingSchedulePeriod[index + 1].startPeriod,
                   ),
-                  targetInterval,
+                  compositeInterval,
                 )
               ) {
-                schedulePeriod.startPeriod = 0;
                 return true;
               }
               return false;
-            },
-          ),
+            })
+            .map((schedulePeriod, index) => {
+              if (index === 0 && schedulePeriod.startPeriod !== 0) {
+                schedulePeriod.startPeriod = 0;
+              }
+              return schedulePeriod;
+            }),
         };
       }
-      if (isAfter(chargingScheduleInterval.end, targetInterval.end)) {
+      if (isAfter(chargingScheduleInterval.end, compositeInterval.end)) {
         return {
           ...chargingSchedule,
-          duration: differenceInSeconds(targetInterval.end as Date, chargingScheduleInterval.start),
+          duration: differenceInSeconds(
+            compositeInterval.end as Date,
+            chargingScheduleInterval.start,
+          ),
           chargingSchedulePeriod: chargingSchedule.chargingSchedulePeriod.filter((schedulePeriod) =>
             isWithinInterval(
               addSeconds(chargingScheduleInterval.start, schedulePeriod.startPeriod)!,
-              targetInterval,
+              compositeInterval,
             ),
           ),
         };
@@ -1232,12 +1336,11 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
     context?: MeterValueContext,
     phase?: OCPP16MeterValuePhase,
   ): OCPP16SampledValue {
-    const sampledValueValue = value ?? sampledValueTemplate?.value ?? null;
-    const sampledValueContext = context ?? sampledValueTemplate?.context ?? null;
+    const sampledValueContext = context ?? sampledValueTemplate?.context;
     const sampledValueLocation =
       sampledValueTemplate?.location ??
       OCPP16ServiceUtils.getMeasurandDefaultLocation(sampledValueTemplate.measurand!);
-    const sampledValuePhase = phase ?? sampledValueTemplate?.phase ?? null;
+    const sampledValuePhase = phase ?? sampledValueTemplate?.phase;
     return {
       ...(!isNullOrUndefined(sampledValueTemplate.unit) && {
         unit: sampledValueTemplate.unit,
@@ -1247,7 +1350,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
         measurand: sampledValueTemplate.measurand,
       }),
       ...(!isNullOrUndefined(sampledValueLocation) && { location: sampledValueLocation }),
-      ...(!isNullOrUndefined(sampledValueValue) && { value: sampledValueValue.toString() }),
+      ...(!isNullOrUndefined(value) && { value: value.toString() }),
       ...(!isNullOrUndefined(sampledValuePhase) && { phase: sampledValuePhase }),
     } as OCPP16SampledValue;
   }