From 7bc31f9cc53264cf4a55064d10c97898d1c903f0 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Sun, 8 May 2022 22:48:10 +0200 Subject: [PATCH] Add tunable in template to disable limitation on custom metervalues template MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- README.md | 1 + src/charging-station/ChargingStation.ts | 4 + .../ocpp/1.6/OCPP16ServiceUtils.ts | 246 ++++++++++-------- src/types/ChargingStationTemplate.ts | 1 + 4 files changed, 142 insertions(+), 110 deletions(-) diff --git a/README.md b/README.md index 64a95f80..7873b31f 100644 --- 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 | diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index fc2c5200..d36700d6 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -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 ( diff --git a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts index b9dcfd1d..7592713b 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts @@ -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; + } + } } diff --git a/src/types/ChargingStationTemplate.ts b/src/types/ChargingStationTemplate.ts index 7cc8023f..789ce248 100644 --- a/src/types/ChargingStationTemplate.ts +++ b/src/types/ChargingStationTemplate.ts @@ -92,6 +92,7 @@ export default interface ChargingStationTemplate { transactionDataMeterValues?: boolean; mainVoltageMeterValues?: boolean; phaseLineToLineVoltageMeterValues?: boolean; + customValueLimitationMeterValues?: boolean; Configuration?: ChargingStationOcppConfiguration; AutomaticTransactionGenerator: AutomaticTransactionGenerator; Connectors: Record; -- 2.34.1