Factor out power limitation calculation in metervalues
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16ServiceUtils.ts
index 64b49c46d4f2f165ec320d56fcfead811babf048..721ae1c24bb78be42f8b3a250c27a82b801d37a7 100644 (file)
@@ -14,12 +14,19 @@ import {
   OCPP16MeterValuePhase,
   OCPP16SampledValue,
 } from '../../../types/ocpp/1.6/MeterValues';
+import {
+  OCPP16IncomingRequestCommand,
+  OCPP16RequestCommand,
+} from '../../../types/ocpp/1.6/Requests';
+import {
+  OCPP16StandardParametersKey,
+  OCPP16SupportedFeatureProfiles,
+} from '../../../types/ocpp/1.6/Configuration';
 
 import type ChargingStation from '../../ChargingStation';
 import Constants from '../../../utils/Constants';
 import { ErrorType } from '../../../types/ocpp/ErrorType';
 import MeasurandValues from '../../../types/MeasurandValues';
-import { OCPP16RequestCommand } from '../../../types/ocpp/1.6/Requests';
 import OCPPError from '../../../exception/OCPPError';
 import Utils from '../../../utils/Utils';
 import logger from '../../../utils/Logger';
@@ -44,6 +51,22 @@ export class OCPP16ServiceUtils {
     }
   }
 
+  public static checkFeatureProfile(
+    chargingStation: ChargingStation,
+    featureProfile: OCPP16SupportedFeatureProfiles,
+    command: OCPP16RequestCommand | OCPP16IncomingRequestCommand
+  ): boolean {
+    if (!chargingStation.hasFeatureProfile(featureProfile)) {
+      logger.warn(
+        `${chargingStation.logPrefix()} Trying to '${command}' without '${featureProfile}' feature enabled in ${
+          OCPP16StandardParametersKey.SupportedFeatureProfiles
+        } in configuration`
+      );
+      return false;
+    }
+    return true;
+  }
+
   public static buildSampledValue(
     sampledValueTemplate: SampledValueTemplate,
     value: number,
@@ -279,13 +302,11 @@ export class OCPP16ServiceUtils {
       } measurand value`;
       const powerMeasurandValues = {} as MeasurandValues;
       const unitDivider = powerSampledValueTemplate?.unit === MeterValueUnit.KILO_WATT ? 1000 : 1;
-      const maximumPower = Math.round(
-        chargingStation.getMaximumConfiguredPower() / chargingStation.stationInfo.powerDivider
-      );
-      const maximumPowerPerPhase = Math.round(
-        chargingStation.getMaximumConfiguredPower() /
-          chargingStation.stationInfo.powerDivider /
-          chargingStation.getNumberOfPhases()
+      const connectorMaximumAvailablePower =
+        chargingStation.getConnectorMaximumAvailablePower(connectorId);
+      const connectorMaximumPower = Math.round(connectorMaximumAvailablePower);
+      const connectorMaximumPowerPerPhase = Math.round(
+        connectorMaximumAvailablePower / chargingStation.getNumberOfPhases()
       );
       switch (chargingStation.getCurrentOutType()) {
         case CurrentType.AC:
@@ -321,15 +342,15 @@ export class OCPP16ServiceUtils {
             powerMeasurandValues.L1 =
               phase1FluctuatedValue ??
               defaultFluctuatedPowerPerPhase ??
-              Utils.getRandomFloatRounded(maximumPowerPerPhase / unitDivider);
+              Utils.getRandomFloatRounded(connectorMaximumPowerPerPhase / unitDivider);
             powerMeasurandValues.L2 =
               phase2FluctuatedValue ??
               defaultFluctuatedPowerPerPhase ??
-              Utils.getRandomFloatRounded(maximumPowerPerPhase / unitDivider);
+              Utils.getRandomFloatRounded(connectorMaximumPowerPerPhase / unitDivider);
             powerMeasurandValues.L3 =
               phase3FluctuatedValue ??
               defaultFluctuatedPowerPerPhase ??
-              Utils.getRandomFloatRounded(maximumPowerPerPhase / unitDivider);
+              Utils.getRandomFloatRounded(connectorMaximumPowerPerPhase / unitDivider);
           } else {
             powerMeasurandValues.L1 = powerSampledValueTemplate.value
               ? Utils.getRandomFloatFluctuatedRounded(
@@ -337,7 +358,7 @@ export class OCPP16ServiceUtils {
                   powerSampledValueTemplate.fluctuationPercent ??
                     Constants.DEFAULT_FLUCTUATION_PERCENT
                 )
-              : Utils.getRandomFloatRounded(maximumPower / unitDivider);
+              : Utils.getRandomFloatRounded(connectorMaximumPower / unitDivider);
             powerMeasurandValues.L2 = 0;
             powerMeasurandValues.L3 = 0;
           }
@@ -353,7 +374,7 @@ export class OCPP16ServiceUtils {
                 powerSampledValueTemplate.fluctuationPercent ??
                   Constants.DEFAULT_FLUCTUATION_PERCENT
               )
-            : Utils.getRandomFloatRounded(maximumPower / unitDivider);
+            : Utils.getRandomFloatRounded(connectorMaximumPower / unitDivider);
           break;
         default:
           logger.error(errMsg);
@@ -366,10 +387,10 @@ export class OCPP16ServiceUtils {
         )
       );
       const sampledValuesIndex = meterValue.sampledValue.length - 1;
-      const maximumPowerRounded = Utils.roundTo(maximumPower / unitDivider, 2);
+      const connectorMaximumPowerRounded = Utils.roundTo(connectorMaximumPower / unitDivider, 2);
       if (
         Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) >
-          maximumPowerRounded ||
+          connectorMaximumPowerRounded ||
         debug
       ) {
         logger.error(
@@ -378,7 +399,7 @@ export class OCPP16ServiceUtils {
             OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
           }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
             meterValue.sampledValue[sampledValuesIndex].value
-          }/${maximumPowerRounded}`
+          }/${connectorMaximumPowerRounded}`
         );
       }
       for (
@@ -397,10 +418,13 @@ export class OCPP16ServiceUtils {
           )
         );
         const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1;
-        const maximumPowerPerPhaseRounded = Utils.roundTo(maximumPowerPerPhase / unitDivider, 2);
+        const connectorMaximumPowerPerPhaseRounded = Utils.roundTo(
+          connectorMaximumPowerPerPhase / unitDivider,
+          2
+        );
         if (
           Utils.convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) >
-            maximumPowerPerPhaseRounded ||
+            connectorMaximumPowerPerPhaseRounded ||
           debug
         ) {
           logger.error(
@@ -411,7 +435,7 @@ export class OCPP16ServiceUtils {
               meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
             }, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
               meterValue.sampledValue[sampledValuesPerPhaseIndex].value
-            }/${maximumPowerPerPhaseRounded}`
+            }/${connectorMaximumPowerPerPhaseRounded}`
           );
         }
       }
@@ -456,12 +480,14 @@ export class OCPP16ServiceUtils {
         OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
       } measurand value`;
       const currentMeasurandValues: MeasurandValues = {} as MeasurandValues;
-      let maximumAmperage: number;
+      const connectorMaximumAvailablePower =
+        chargingStation.getConnectorMaximumAvailablePower(connectorId);
+      let connectorMaximumAmperage: number;
       switch (chargingStation.getCurrentOutType()) {
         case CurrentType.AC:
-          maximumAmperage = ACElectricUtils.amperagePerPhaseFromPower(
+          connectorMaximumAmperage = ACElectricUtils.amperagePerPhaseFromPower(
             chargingStation.getNumberOfPhases(),
-            chargingStation.getMaximumConfiguredPower() / chargingStation.stationInfo.powerDivider,
+            connectorMaximumAvailablePower,
             chargingStation.getVoltageOut()
           );
           if (chargingStation.getNumberOfPhases() === 3) {
@@ -496,15 +522,15 @@ export class OCPP16ServiceUtils {
             currentMeasurandValues.L1 =
               phase1FluctuatedValue ??
               defaultFluctuatedAmperagePerPhase ??
-              Utils.getRandomFloatRounded(maximumAmperage);
+              Utils.getRandomFloatRounded(connectorMaximumAmperage);
             currentMeasurandValues.L2 =
               phase2FluctuatedValue ??
               defaultFluctuatedAmperagePerPhase ??
-              Utils.getRandomFloatRounded(maximumAmperage);
+              Utils.getRandomFloatRounded(connectorMaximumAmperage);
             currentMeasurandValues.L3 =
               phase3FluctuatedValue ??
               defaultFluctuatedAmperagePerPhase ??
-              Utils.getRandomFloatRounded(maximumAmperage);
+              Utils.getRandomFloatRounded(connectorMaximumAmperage);
           } else {
             currentMeasurandValues.L1 = currentSampledValueTemplate.value
               ? Utils.getRandomFloatFluctuatedRounded(
@@ -512,7 +538,7 @@ export class OCPP16ServiceUtils {
                   currentSampledValueTemplate.fluctuationPercent ??
                     Constants.DEFAULT_FLUCTUATION_PERCENT
                 )
-              : Utils.getRandomFloatRounded(maximumAmperage);
+              : Utils.getRandomFloatRounded(connectorMaximumAmperage);
             currentMeasurandValues.L2 = 0;
             currentMeasurandValues.L3 = 0;
           }
@@ -523,8 +549,8 @@ export class OCPP16ServiceUtils {
           );
           break;
         case CurrentType.DC:
-          maximumAmperage = DCElectricUtils.amperage(
-            chargingStation.getMaximumConfiguredPower() / chargingStation.stationInfo.powerDivider,
+          connectorMaximumAmperage = DCElectricUtils.amperage(
+            connectorMaximumAvailablePower,
             chargingStation.getVoltageOut()
           );
           currentMeasurandValues.allPhases = currentSampledValueTemplate.value
@@ -533,7 +559,7 @@ export class OCPP16ServiceUtils {
                 currentSampledValueTemplate.fluctuationPercent ??
                   Constants.DEFAULT_FLUCTUATION_PERCENT
               )
-            : Utils.getRandomFloatRounded(maximumAmperage);
+            : Utils.getRandomFloatRounded(connectorMaximumAmperage);
           break;
         default:
           logger.error(errMsg);
@@ -547,7 +573,8 @@ export class OCPP16ServiceUtils {
       );
       const sampledValuesIndex = meterValue.sampledValue.length - 1;
       if (
-        Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maximumAmperage ||
+        Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) >
+          connectorMaximumAmperage ||
         debug
       ) {
         logger.error(
@@ -556,7 +583,7 @@ export class OCPP16ServiceUtils {
             OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
           }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
             meterValue.sampledValue[sampledValuesIndex].value
-          }/${maximumAmperage}`
+          }/${connectorMaximumAmperage}`
         );
       }
       for (
@@ -577,7 +604,7 @@ export class OCPP16ServiceUtils {
         const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1;
         if (
           Utils.convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) >
-            maximumAmperage ||
+            connectorMaximumAmperage ||
           debug
         ) {
           logger.error(
@@ -588,7 +615,7 @@ export class OCPP16ServiceUtils {
               meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
             }, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
               meterValue.sampledValue[sampledValuesPerPhaseIndex].value
-            }/${maximumAmperage}`
+            }/${connectorMaximumAmperage}`
           );
         }
       }
@@ -602,10 +629,10 @@ export class OCPP16ServiceUtils {
       );
       const unitDivider =
         energySampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1;
-      const maximumEnergyRounded = Utils.roundTo(
-        ((chargingStation.getMaximumConfiguredPower() / chargingStation.stationInfo.powerDivider) *
-          interval) /
-          (3600 * 1000),
+      const connectorMaximumAvailablePower =
+        chargingStation.getConnectorMaximumAvailablePower(connectorId);
+      const connectorMaximumEnergyRounded = Utils.roundTo(
+        (connectorMaximumAvailablePower * interval) / (3600 * 1000),
         2
       );
       const energyValueRounded = energySampledValueTemplate.value
@@ -614,7 +641,7 @@ export class OCPP16ServiceUtils {
             parseInt(energySampledValueTemplate.value),
             energySampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
           )
-        : Utils.getRandomFloatRounded(maximumEnergyRounded);
+        : Utils.getRandomFloatRounded(connectorMaximumEnergyRounded);
       // Persist previous value on connector
       if (
         connector &&
@@ -640,14 +667,14 @@ export class OCPP16ServiceUtils {
         )
       );
       const sampledValuesIndex = meterValue.sampledValue.length - 1;
-      if (energyValueRounded > maximumEnergyRounded || debug) {
+      if (energyValueRounded > connectorMaximumEnergyRounded || debug) {
         logger.error(
           `${chargingStation.logPrefix()} MeterValues measurand ${
             meterValue.sampledValue[sampledValuesIndex].measurand ??
             OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
           }: connectorId ${connectorId}, transaction ${
             connector.transactionId
-          }, value: ${energyValueRounded}/${maximumEnergyRounded}, duration: ${Utils.roundTo(
+          }, value: ${energyValueRounded}/${connectorMaximumEnergyRounded}, duration: ${Utils.roundTo(
             interval / (3600 * 1000),
             4
           )}h`