Skip to content
This repository has been archived by the owner on Jun 12, 2024. It is now read-only.

Commit

Permalink
Merge pull request #232 from jerome-benoit/feature/fluctuated-meterva…
Browse files Browse the repository at this point in the history
…lues-limit
  • Loading branch information
Jérôme Benoit authored May 8, 2022
2 parents 1b6498b + 90befdb commit 4be9849
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 90 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
4 changes: 4 additions & 0 deletions src/charging-station/ChargingStation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
236 changes: 146 additions & 90 deletions src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -314,28 +239,44 @@ export class OCPP16ServiceUtils {
const defaultFluctuatedPowerPerPhase =
powerSampledValueTemplate.value &&
Utils.getRandomFloatFluctuatedRounded(
parseInt(powerSampledValueTemplate.value) / chargingStation.getNumberOfPhases(),
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
powerSampledValueTemplate.value,
connectorMaximumPower / unitDivider,
{ limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
) / chargingStation.getNumberOfPhases(),
powerSampledValueTemplate.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT
);
const phase1FluctuatedValue =
powerPerPhaseSampledValueTemplates?.L1?.value &&
Utils.getRandomFloatFluctuatedRounded(
parseInt(powerPerPhaseSampledValueTemplates.L1.value),
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
powerPerPhaseSampledValueTemplates.L1.value,
connectorMaximumPowerPerPhase / unitDivider,
{ limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
),
powerPerPhaseSampledValueTemplates.L1.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT
);
const phase2FluctuatedValue =
powerPerPhaseSampledValueTemplates?.L2?.value &&
Utils.getRandomFloatFluctuatedRounded(
parseInt(powerPerPhaseSampledValueTemplates.L2.value),
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
powerPerPhaseSampledValueTemplates.L2.value,
connectorMaximumPowerPerPhase / unitDivider,
{ limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
),
powerPerPhaseSampledValueTemplates.L2.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT
);
const phase3FluctuatedValue =
powerPerPhaseSampledValueTemplates?.L3?.value &&
Utils.getRandomFloatFluctuatedRounded(
parseInt(powerPerPhaseSampledValueTemplates.L3.value),
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
powerPerPhaseSampledValueTemplates.L3.value,
connectorMaximumPowerPerPhase / unitDivider,
{ limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
),
powerPerPhaseSampledValueTemplates.L3.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT
);
Expand All @@ -354,7 +295,11 @@ export class OCPP16ServiceUtils {
} else {
powerMeasurandValues.L1 = powerSampledValueTemplate.value
? Utils.getRandomFloatFluctuatedRounded(
parseInt(powerSampledValueTemplate.value),
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
powerSampledValueTemplate.value,
connectorMaximumPower / unitDivider,
{ limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
),
powerSampledValueTemplate.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT
)
Expand All @@ -370,7 +315,11 @@ export class OCPP16ServiceUtils {
case CurrentType.DC:
powerMeasurandValues.allPhases = powerSampledValueTemplate.value
? Utils.getRandomFloatFluctuatedRounded(
parseInt(powerSampledValueTemplate.value),
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
powerSampledValueTemplate.value,
connectorMaximumPower / unitDivider,
{ limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
),
powerSampledValueTemplate.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT
)
Expand Down Expand Up @@ -494,28 +443,44 @@ export class OCPP16ServiceUtils {
const defaultFluctuatedAmperagePerPhase =
currentSampledValueTemplate.value &&
Utils.getRandomFloatFluctuatedRounded(
parseInt(currentSampledValueTemplate.value),
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
currentSampledValueTemplate.value,
connectorMaximumAmperage,
{ limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
),
currentSampledValueTemplate.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT
);
const phase1FluctuatedValue =
currentPerPhaseSampledValueTemplates?.L1?.value &&
Utils.getRandomFloatFluctuatedRounded(
parseInt(currentPerPhaseSampledValueTemplates.L1.value),
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
currentPerPhaseSampledValueTemplates.L1.value,
connectorMaximumAmperage,
{ limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
),
currentPerPhaseSampledValueTemplates.L1.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT
);
const phase2FluctuatedValue =
currentPerPhaseSampledValueTemplates?.L2?.value &&
Utils.getRandomFloatFluctuatedRounded(
parseInt(currentPerPhaseSampledValueTemplates.L2.value),
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
currentPerPhaseSampledValueTemplates.L2.value,
connectorMaximumAmperage,
{ limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
),
currentPerPhaseSampledValueTemplates.L2.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT
);
const phase3FluctuatedValue =
currentPerPhaseSampledValueTemplates?.L3?.value &&
Utils.getRandomFloatFluctuatedRounded(
parseInt(currentPerPhaseSampledValueTemplates.L3.value),
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
currentPerPhaseSampledValueTemplates.L3.value,
connectorMaximumAmperage,
{ limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
),
currentPerPhaseSampledValueTemplates.L3.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT
);
Expand All @@ -534,7 +499,11 @@ export class OCPP16ServiceUtils {
} else {
currentMeasurandValues.L1 = currentSampledValueTemplate.value
? Utils.getRandomFloatFluctuatedRounded(
parseInt(currentSampledValueTemplate.value),
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
currentSampledValueTemplate.value,
connectorMaximumAmperage,
{ limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
),
currentSampledValueTemplate.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT
)
Expand All @@ -555,7 +524,11 @@ export class OCPP16ServiceUtils {
);
currentMeasurandValues.allPhases = currentSampledValueTemplate.value
? Utils.getRandomFloatFluctuatedRounded(
parseInt(currentSampledValueTemplate.value),
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
currentSampledValueTemplate.value,
connectorMaximumAmperage,
{ limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
),
currentSampledValueTemplate.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT
)
Expand Down Expand Up @@ -638,7 +611,14 @@ export class OCPP16ServiceUtils {
const energyValueRounded = energySampledValueTemplate.value
? // Cumulate the fluctuated value around the static one
Utils.getRandomFloatFluctuatedRounded(
parseInt(energySampledValueTemplate.value),
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
energySampledValueTemplate.value,
connectorMaximumEnergyRounded,
{
limitationEnabled: chargingStation.getCustomValueLimitationMeterValues(),
unitMultiplier: unitDivider,
}
),
energySampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
)
: Utils.getRandomFloatRounded(connectorMaximumEnergyRounded);
Expand Down Expand Up @@ -737,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;
}
}
}
16 changes: 16 additions & 0 deletions src/charging-station/ocpp/OCPPServiceUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export class OCPPServiceUtils {
protected static getLimitFromSampledValueTemplateCustomValue(
value: string,
limit: number,
options: { limitationEnabled?: boolean; unitMultiplier?: number } = {
limitationEnabled: true,
unitMultiplier: 1,
}
): number {
options.limitationEnabled = options?.limitationEnabled ?? true;
options.unitMultiplier = options?.unitMultiplier ?? 1;
return options?.limitationEnabled
? Math.min(parseInt(value) * options.unitMultiplier, limit)
: parseInt(value) * options.unitMultiplier;
}
}
Loading

0 comments on commit 4be9849

Please sign in to comment.