Add tunable in template to disable limitation on custom metervalues
authorJérôme Benoit <jerome.benoit@sap.com>
Sun, 8 May 2022 20:48:10 +0000 (22:48 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Sun, 8 May 2022 20:48:10 +0000 (22:48 +0200)
template

Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
README.md
src/charging-station/ChargingStation.ts
src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts
src/types/ChargingStationTemplate.ts

index 64a95f80c4fba92a3206b826172aea8458871989..7873b31f337766396cb931f7a3f5bbbe52f09e37 100644 (file)
--- a/README.md
+++ b/README.md
@@ -144,6 +144,7 @@ But the modifications to test have to be done to the files in the build result d
 | transactionDataMeterValues        | true/false | false           | boolean                           | enable transaction data MeterValues at stop transaction                                                                                                                        |
 | mainVoltageMeterValues            | true/false | true            | boolean                           | include charging stations main voltage MeterValues on three phased charging stations                                                                                           |
 | phaseLineToLineVoltageMeterValues | true/false | true            | boolean                           | include charging stations line to line voltage MeterValues on three phased charging stations                                                                                   |
+| customValueLimitationMeterValues  | true/false | true            | boolean                           | enable limitation on custom fluctuated value in MeterValues                                                                                                                    |
 | Configuration                     |            |                 | ChargingStationConfiguration      | charging stations OCPP parameters configuration section                                                                                                                        |
 | AutomaticTransactionGenerator     |            |                 | AutomaticTransactionGenerator     | charging stations ATG configuration section                                                                                                                                    |
 | Connectors                        |            |                 | Connectors                        | charging stations connectors configuration section                                                                                                                             |
index fc2c5200ac35e9c5510b859921624815aa6f8ea3..d36700d64c382082155a1138f2ba2bef524a62b8 100644 (file)
@@ -306,6 +306,10 @@ export default class ChargingStation {
     return this.stationInfo.phaseLineToLineVoltageMeterValues ?? false;
   }
 
+  public getCustomValueLimitationMeterValues(): boolean {
+    return this.stationInfo.customValueLimitationMeterValues ?? true;
+  }
+
   public getConnectorIdByTransactionId(transactionId: number): number | undefined {
     for (const connectorId of this.connectors.keys()) {
       if (
index b9dcfd1db64895fadfec23d240615e947e1ba876..7592713b3007b85994c458cf2602f0b349e885eb 100644 (file)
@@ -28,29 +28,11 @@ import Constants from '../../../utils/Constants';
 import { ErrorType } from '../../../types/ocpp/ErrorType';
 import MeasurandValues from '../../../types/MeasurandValues';
 import OCPPError from '../../../exception/OCPPError';
+import { OCPPServiceUtils } from '../OCPPServiceUtils';
 import Utils from '../../../utils/Utils';
 import logger from '../../../utils/Logger';
 
-export class OCPP16ServiceUtils {
-  public static checkMeasurandPowerDivider(
-    chargingStation: ChargingStation,
-    measurandType: OCPP16MeterValueMeasurand
-  ): void {
-    if (Utils.isUndefined(chargingStation.stationInfo.powerDivider)) {
-      const errMsg = `${chargingStation.logPrefix()} MeterValues measurand ${
-        measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
-      }: powerDivider is undefined`;
-      logger.error(errMsg);
-      throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, OCPP16RequestCommand.METER_VALUES);
-    } else if (chargingStation.stationInfo?.powerDivider <= 0) {
-      const errMsg = `${chargingStation.logPrefix()} MeterValues measurand ${
-        measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
-      }: powerDivider have zero or below value ${chargingStation.stationInfo.powerDivider}`;
-      logger.error(errMsg);
-      throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, OCPP16RequestCommand.METER_VALUES);
-    }
-  }
-
+export class OCPP16ServiceUtils extends OCPPServiceUtils {
   public static checkFeatureProfile(
     chargingStation: ChargingStation,
     featureProfile: OCPP16SupportedFeatureProfiles,
@@ -67,63 +49,6 @@ export class OCPP16ServiceUtils {
     return true;
   }
 
-  public static buildSampledValue(
-    sampledValueTemplate: SampledValueTemplate,
-    value: number,
-    context?: MeterValueContext,
-    phase?: OCPP16MeterValuePhase
-  ): OCPP16SampledValue {
-    const sampledValueValue = value ?? sampledValueTemplate?.value ?? null;
-    const sampledValueContext = context ?? sampledValueTemplate?.context ?? null;
-    const sampledValueLocation =
-      sampledValueTemplate?.location ??
-      OCPP16ServiceUtils.getMeasurandDefaultLocation(sampledValueTemplate?.measurand ?? null);
-    const sampledValuePhase = phase ?? sampledValueTemplate?.phase ?? null;
-    return {
-      ...(!Utils.isNullOrUndefined(sampledValueTemplate.unit) && {
-        unit: sampledValueTemplate.unit,
-      }),
-      ...(!Utils.isNullOrUndefined(sampledValueContext) && { context: sampledValueContext }),
-      ...(!Utils.isNullOrUndefined(sampledValueTemplate.measurand) && {
-        measurand: sampledValueTemplate.measurand,
-      }),
-      ...(!Utils.isNullOrUndefined(sampledValueLocation) && { location: sampledValueLocation }),
-      ...(!Utils.isNullOrUndefined(sampledValueValue) && { value: sampledValueValue.toString() }),
-      ...(!Utils.isNullOrUndefined(sampledValuePhase) && { phase: sampledValuePhase }),
-    };
-  }
-
-  public static getMeasurandDefaultUnit(
-    measurandType: OCPP16MeterValueMeasurand
-  ): MeterValueUnit | undefined {
-    switch (measurandType) {
-      case OCPP16MeterValueMeasurand.CURRENT_EXPORT:
-      case OCPP16MeterValueMeasurand.CURRENT_IMPORT:
-      case OCPP16MeterValueMeasurand.CURRENT_OFFERED:
-        return MeterValueUnit.AMP;
-      case OCPP16MeterValueMeasurand.ENERGY_ACTIVE_EXPORT_REGISTER:
-      case OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER:
-        return MeterValueUnit.WATT_HOUR;
-      case OCPP16MeterValueMeasurand.POWER_ACTIVE_EXPORT:
-      case OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT:
-      case OCPP16MeterValueMeasurand.POWER_OFFERED:
-        return MeterValueUnit.WATT;
-      case OCPP16MeterValueMeasurand.STATE_OF_CHARGE:
-        return MeterValueUnit.PERCENT;
-      case OCPP16MeterValueMeasurand.VOLTAGE:
-        return MeterValueUnit.VOLT;
-    }
-  }
-
-  public static getMeasurandDefaultLocation(
-    measurandType: OCPP16MeterValueMeasurand
-  ): MeterValueLocation | undefined {
-    switch (measurandType) {
-      case OCPP16MeterValueMeasurand.STATE_OF_CHARGE:
-        return MeterValueLocation.EV;
-    }
-  }
-
   public static buildMeterValue(
     chargingStation: ChargingStation,
     connectorId: number,
@@ -314,9 +239,10 @@ export class OCPP16ServiceUtils {
             const defaultFluctuatedPowerPerPhase =
               powerSampledValueTemplate.value &&
               Utils.getRandomFloatFluctuatedRounded(
-                Math.min(
-                  parseInt(powerSampledValueTemplate.value),
-                  connectorMaximumPower / unitDivider
+                OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+                  powerSampledValueTemplate.value,
+                  connectorMaximumPower / unitDivider,
+                  { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
                 ) / chargingStation.getNumberOfPhases(),
                 powerSampledValueTemplate.fluctuationPercent ??
                   Constants.DEFAULT_FLUCTUATION_PERCENT
@@ -324,9 +250,10 @@ export class OCPP16ServiceUtils {
             const phase1FluctuatedValue =
               powerPerPhaseSampledValueTemplates?.L1?.value &&
               Utils.getRandomFloatFluctuatedRounded(
-                Math.min(
-                  parseInt(powerPerPhaseSampledValueTemplates.L1.value),
-                  connectorMaximumPowerPerPhase / unitDivider
+                OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+                  powerPerPhaseSampledValueTemplates.L1.value,
+                  connectorMaximumPowerPerPhase / unitDivider,
+                  { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
                 ),
                 powerPerPhaseSampledValueTemplates.L1.fluctuationPercent ??
                   Constants.DEFAULT_FLUCTUATION_PERCENT
@@ -334,9 +261,10 @@ export class OCPP16ServiceUtils {
             const phase2FluctuatedValue =
               powerPerPhaseSampledValueTemplates?.L2?.value &&
               Utils.getRandomFloatFluctuatedRounded(
-                Math.min(
-                  parseInt(powerPerPhaseSampledValueTemplates.L2.value),
-                  connectorMaximumPowerPerPhase / unitDivider
+                OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+                  powerPerPhaseSampledValueTemplates.L2.value,
+                  connectorMaximumPowerPerPhase / unitDivider,
+                  { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
                 ),
                 powerPerPhaseSampledValueTemplates.L2.fluctuationPercent ??
                   Constants.DEFAULT_FLUCTUATION_PERCENT
@@ -344,9 +272,10 @@ export class OCPP16ServiceUtils {
             const phase3FluctuatedValue =
               powerPerPhaseSampledValueTemplates?.L3?.value &&
               Utils.getRandomFloatFluctuatedRounded(
-                Math.min(
-                  parseInt(powerPerPhaseSampledValueTemplates.L3.value),
-                  connectorMaximumPowerPerPhase / unitDivider
+                OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+                  powerPerPhaseSampledValueTemplates.L3.value,
+                  connectorMaximumPowerPerPhase / unitDivider,
+                  { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
                 ),
                 powerPerPhaseSampledValueTemplates.L3.fluctuationPercent ??
                   Constants.DEFAULT_FLUCTUATION_PERCENT
@@ -366,9 +295,10 @@ export class OCPP16ServiceUtils {
           } else {
             powerMeasurandValues.L1 = powerSampledValueTemplate.value
               ? Utils.getRandomFloatFluctuatedRounded(
-                  Math.min(
-                    parseInt(powerSampledValueTemplate.value),
-                    connectorMaximumPower / unitDivider
+                  OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+                    powerSampledValueTemplate.value,
+                    connectorMaximumPower / unitDivider,
+                    { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
                   ),
                   powerSampledValueTemplate.fluctuationPercent ??
                     Constants.DEFAULT_FLUCTUATION_PERCENT
@@ -385,9 +315,10 @@ export class OCPP16ServiceUtils {
         case CurrentType.DC:
           powerMeasurandValues.allPhases = powerSampledValueTemplate.value
             ? Utils.getRandomFloatFluctuatedRounded(
-                Math.min(
-                  parseInt(powerSampledValueTemplate.value),
-                  connectorMaximumPower / unitDivider
+                OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+                  powerSampledValueTemplate.value,
+                  connectorMaximumPower / unitDivider,
+                  { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
                 ),
                 powerSampledValueTemplate.fluctuationPercent ??
                   Constants.DEFAULT_FLUCTUATION_PERCENT
@@ -512,16 +443,21 @@ export class OCPP16ServiceUtils {
             const defaultFluctuatedAmperagePerPhase =
               currentSampledValueTemplate.value &&
               Utils.getRandomFloatFluctuatedRounded(
-                Math.min(parseInt(currentSampledValueTemplate.value), connectorMaximumAmperage),
+                OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+                  currentSampledValueTemplate.value,
+                  connectorMaximumAmperage,
+                  { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
+                ),
                 currentSampledValueTemplate.fluctuationPercent ??
                   Constants.DEFAULT_FLUCTUATION_PERCENT
               );
             const phase1FluctuatedValue =
               currentPerPhaseSampledValueTemplates?.L1?.value &&
               Utils.getRandomFloatFluctuatedRounded(
-                Math.min(
-                  parseInt(currentPerPhaseSampledValueTemplates.L1.value),
-                  connectorMaximumAmperage
+                OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+                  currentPerPhaseSampledValueTemplates.L1.value,
+                  connectorMaximumAmperage,
+                  { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
                 ),
                 currentPerPhaseSampledValueTemplates.L1.fluctuationPercent ??
                   Constants.DEFAULT_FLUCTUATION_PERCENT
@@ -529,9 +465,10 @@ export class OCPP16ServiceUtils {
             const phase2FluctuatedValue =
               currentPerPhaseSampledValueTemplates?.L2?.value &&
               Utils.getRandomFloatFluctuatedRounded(
-                Math.min(
-                  parseInt(currentPerPhaseSampledValueTemplates.L2.value),
-                  connectorMaximumAmperage
+                OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+                  currentPerPhaseSampledValueTemplates.L2.value,
+                  connectorMaximumAmperage,
+                  { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
                 ),
                 currentPerPhaseSampledValueTemplates.L2.fluctuationPercent ??
                   Constants.DEFAULT_FLUCTUATION_PERCENT
@@ -539,9 +476,10 @@ export class OCPP16ServiceUtils {
             const phase3FluctuatedValue =
               currentPerPhaseSampledValueTemplates?.L3?.value &&
               Utils.getRandomFloatFluctuatedRounded(
-                Math.min(
-                  parseInt(currentPerPhaseSampledValueTemplates.L3.value),
-                  connectorMaximumAmperage
+                OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+                  currentPerPhaseSampledValueTemplates.L3.value,
+                  connectorMaximumAmperage,
+                  { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
                 ),
                 currentPerPhaseSampledValueTemplates.L3.fluctuationPercent ??
                   Constants.DEFAULT_FLUCTUATION_PERCENT
@@ -561,7 +499,11 @@ export class OCPP16ServiceUtils {
           } else {
             currentMeasurandValues.L1 = currentSampledValueTemplate.value
               ? Utils.getRandomFloatFluctuatedRounded(
-                  Math.min(parseInt(currentSampledValueTemplate.value), connectorMaximumAmperage),
+                  OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+                    currentSampledValueTemplate.value,
+                    connectorMaximumAmperage,
+                    { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
+                  ),
                   currentSampledValueTemplate.fluctuationPercent ??
                     Constants.DEFAULT_FLUCTUATION_PERCENT
                 )
@@ -582,7 +524,11 @@ export class OCPP16ServiceUtils {
           );
           currentMeasurandValues.allPhases = currentSampledValueTemplate.value
             ? Utils.getRandomFloatFluctuatedRounded(
-                Math.min(parseInt(currentSampledValueTemplate.value), connectorMaximumAmperage),
+                OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+                  currentSampledValueTemplate.value,
+                  connectorMaximumAmperage,
+                  { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
+                ),
                 currentSampledValueTemplate.fluctuationPercent ??
                   Constants.DEFAULT_FLUCTUATION_PERCENT
               )
@@ -665,9 +611,13 @@ export class OCPP16ServiceUtils {
       const energyValueRounded = energySampledValueTemplate.value
         ? // Cumulate the fluctuated value around the static one
           Utils.getRandomFloatFluctuatedRounded(
-            Math.min(
-              parseInt(energySampledValueTemplate.value) * unitDivider,
-              connectorMaximumEnergyRounded
+            OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+              energySampledValueTemplate.value,
+              connectorMaximumEnergyRounded,
+              {
+                limitationEnabled: chargingStation.getCustomValueLimitationMeterValues(),
+                unitMultiplier: unitDivider,
+              }
             ),
             energySampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
           )
@@ -767,4 +717,80 @@ export class OCPP16ServiceUtils {
     meterValues.push(transactionEndMeterValue);
     return meterValues;
   }
+
+  private static buildSampledValue(
+    sampledValueTemplate: SampledValueTemplate,
+    value: number,
+    context?: MeterValueContext,
+    phase?: OCPP16MeterValuePhase
+  ): OCPP16SampledValue {
+    const sampledValueValue = value ?? sampledValueTemplate?.value ?? null;
+    const sampledValueContext = context ?? sampledValueTemplate?.context ?? null;
+    const sampledValueLocation =
+      sampledValueTemplate?.location ??
+      OCPP16ServiceUtils.getMeasurandDefaultLocation(sampledValueTemplate?.measurand ?? null);
+    const sampledValuePhase = phase ?? sampledValueTemplate?.phase ?? null;
+    return {
+      ...(!Utils.isNullOrUndefined(sampledValueTemplate.unit) && {
+        unit: sampledValueTemplate.unit,
+      }),
+      ...(!Utils.isNullOrUndefined(sampledValueContext) && { context: sampledValueContext }),
+      ...(!Utils.isNullOrUndefined(sampledValueTemplate.measurand) && {
+        measurand: sampledValueTemplate.measurand,
+      }),
+      ...(!Utils.isNullOrUndefined(sampledValueLocation) && { location: sampledValueLocation }),
+      ...(!Utils.isNullOrUndefined(sampledValueValue) && { value: sampledValueValue.toString() }),
+      ...(!Utils.isNullOrUndefined(sampledValuePhase) && { phase: sampledValuePhase }),
+    };
+  }
+
+  private static checkMeasurandPowerDivider(
+    chargingStation: ChargingStation,
+    measurandType: OCPP16MeterValueMeasurand
+  ): void {
+    if (Utils.isUndefined(chargingStation.stationInfo.powerDivider)) {
+      const errMsg = `${chargingStation.logPrefix()} MeterValues measurand ${
+        measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
+      }: powerDivider is undefined`;
+      logger.error(errMsg);
+      throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, OCPP16RequestCommand.METER_VALUES);
+    } else if (chargingStation.stationInfo?.powerDivider <= 0) {
+      const errMsg = `${chargingStation.logPrefix()} MeterValues measurand ${
+        measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
+      }: powerDivider have zero or below value ${chargingStation.stationInfo.powerDivider}`;
+      logger.error(errMsg);
+      throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, OCPP16RequestCommand.METER_VALUES);
+    }
+  }
+
+  private static getMeasurandDefaultLocation(
+    measurandType: OCPP16MeterValueMeasurand
+  ): MeterValueLocation | undefined {
+    switch (measurandType) {
+      case OCPP16MeterValueMeasurand.STATE_OF_CHARGE:
+        return MeterValueLocation.EV;
+    }
+  }
+
+  private static getMeasurandDefaultUnit(
+    measurandType: OCPP16MeterValueMeasurand
+  ): MeterValueUnit | undefined {
+    switch (measurandType) {
+      case OCPP16MeterValueMeasurand.CURRENT_EXPORT:
+      case OCPP16MeterValueMeasurand.CURRENT_IMPORT:
+      case OCPP16MeterValueMeasurand.CURRENT_OFFERED:
+        return MeterValueUnit.AMP;
+      case OCPP16MeterValueMeasurand.ENERGY_ACTIVE_EXPORT_REGISTER:
+      case OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER:
+        return MeterValueUnit.WATT_HOUR;
+      case OCPP16MeterValueMeasurand.POWER_ACTIVE_EXPORT:
+      case OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT:
+      case OCPP16MeterValueMeasurand.POWER_OFFERED:
+        return MeterValueUnit.WATT;
+      case OCPP16MeterValueMeasurand.STATE_OF_CHARGE:
+        return MeterValueUnit.PERCENT;
+      case OCPP16MeterValueMeasurand.VOLTAGE:
+        return MeterValueUnit.VOLT;
+    }
+  }
 }
index 7cc8023f8a729acb6160d2fd089a37285d8d2414..789ce248bc3d5e40beba0ccc2a2e221a618501fb 100644 (file)
@@ -92,6 +92,7 @@ export default interface ChargingStationTemplate {
   transactionDataMeterValues?: boolean;
   mainVoltageMeterValues?: boolean;
   phaseLineToLineVoltageMeterValues?: boolean;
+  customValueLimitationMeterValues?: boolean;
   Configuration?: ChargingStationOcppConfiguration;
   AutomaticTransactionGenerator: AutomaticTransactionGenerator;
   Connectors: Record<string, ConnectorStatus>;