// Partial Copyright Jerome Benoit. 2021. All Rights Reserved.
-import { ACElectricUtils, DCElectricUtils } from '../../../utils/ElectricUtils';
+import OCPPError from '../../../exception/OCPPError';
import { CurrentType, Voltage } from '../../../types/ChargingStationTemplate';
import MeasurandPerPhaseSampledValueTemplates, {
SampledValueTemplate,
} from '../../../types/MeasurandPerPhaseSampledValueTemplates';
+import MeasurandValues from '../../../types/MeasurandValues';
+import {
+ OCPP16StandardParametersKey,
+ OCPP16SupportedFeatureProfiles,
+} from '../../../types/ocpp/1.6/Configuration';
import {
MeterValueContext,
MeterValueLocation,
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 OCPPError from '../../../exception/OCPPError';
-import Utils from '../../../utils/Utils';
+import Constants from '../../../utils/Constants';
+import { ACElectricUtils, DCElectricUtils } from '../../../utils/ElectricUtils';
import logger from '../../../utils/Logger';
+import Utils from '../../../utils/Utils';
+import type ChargingStation from '../../ChargingStation';
+import { ChargingStationUtils } from '../../ChargingStationUtils';
+import { OCPPServiceUtils } from '../OCPPServiceUtils';
-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,
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,
};
const connector = chargingStation.getConnectorStatus(connectorId);
// SoC measurand
- const socSampledValueTemplate = chargingStation.getSampledValueTemplate(
+ const socSampledValueTemplate = ChargingStationUtils.getSampledValueTemplate(
+ chargingStation,
connectorId,
OCPP16MeterValueMeasurand.STATE_OF_CHARGE
);
}
}
// Voltage measurand
- const voltageSampledValueTemplate = chargingStation.getSampledValueTemplate(
+ const voltageSampledValueTemplate = ChargingStationUtils.getSampledValueTemplate(
+ chargingStation,
connectorId,
OCPP16MeterValueMeasurand.VOLTAGE
);
) {
const phaseLineToNeutralValue = `L${phase}-N`;
const voltagePhaseLineToNeutralSampledValueTemplate =
- chargingStation.getSampledValueTemplate(
+ ChargingStationUtils.getSampledValueTemplate(
+ chargingStation,
connectorId,
OCPP16MeterValueMeasurand.VOLTAGE,
phaseLineToNeutralValue as OCPP16MeterValuePhase
: chargingStation.getNumberOfPhases()
}`;
const voltagePhaseLineToLineSampledValueTemplate =
- chargingStation.getSampledValueTemplate(
+ ChargingStationUtils.getSampledValueTemplate(
+ chargingStation,
connectorId,
OCPP16MeterValueMeasurand.VOLTAGE,
phaseLineToLineValue as OCPP16MeterValuePhase
}
}
// Power.Active.Import measurand
- const powerSampledValueTemplate = chargingStation.getSampledValueTemplate(
+ const powerSampledValueTemplate = ChargingStationUtils.getSampledValueTemplate(
+ chargingStation,
connectorId,
OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT
);
let powerPerPhaseSampledValueTemplates: MeasurandPerPhaseSampledValueTemplates = {};
if (chargingStation.getNumberOfPhases() === 3) {
powerPerPhaseSampledValueTemplates = {
- L1: chargingStation.getSampledValueTemplate(
+ L1: ChargingStationUtils.getSampledValueTemplate(
+ chargingStation,
connectorId,
OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT,
OCPP16MeterValuePhase.L1_N
),
- L2: chargingStation.getSampledValueTemplate(
+ L2: ChargingStationUtils.getSampledValueTemplate(
+ chargingStation,
connectorId,
OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT,
OCPP16MeterValuePhase.L2_N
),
- L3: chargingStation.getSampledValueTemplate(
+ L3: ChargingStationUtils.getSampledValueTemplate(
+ chargingStation,
connectorId,
OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT,
OCPP16MeterValuePhase.L3_N
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
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
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
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
} 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
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
}
}
// Current.Import measurand
- const currentSampledValueTemplate = chargingStation.getSampledValueTemplate(
+ const currentSampledValueTemplate = ChargingStationUtils.getSampledValueTemplate(
+ chargingStation,
connectorId,
OCPP16MeterValueMeasurand.CURRENT_IMPORT
);
let currentPerPhaseSampledValueTemplates: MeasurandPerPhaseSampledValueTemplates = {};
if (chargingStation.getNumberOfPhases() === 3) {
currentPerPhaseSampledValueTemplates = {
- L1: chargingStation.getSampledValueTemplate(
+ L1: ChargingStationUtils.getSampledValueTemplate(
+ chargingStation,
connectorId,
OCPP16MeterValueMeasurand.CURRENT_IMPORT,
OCPP16MeterValuePhase.L1
),
- L2: chargingStation.getSampledValueTemplate(
+ L2: ChargingStationUtils.getSampledValueTemplate(
+ chargingStation,
connectorId,
OCPP16MeterValueMeasurand.CURRENT_IMPORT,
OCPP16MeterValuePhase.L2
),
- L3: chargingStation.getSampledValueTemplate(
+ L3: ChargingStationUtils.getSampledValueTemplate(
+ chargingStation,
connectorId,
OCPP16MeterValueMeasurand.CURRENT_IMPORT,
OCPP16MeterValuePhase.L3
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
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
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
} 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
)
);
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
)
}
}
// Energy.Active.Import.Register measurand (default)
- const energySampledValueTemplate = chargingStation.getSampledValueTemplate(connectorId);
+ const energySampledValueTemplate = ChargingStationUtils.getSampledValueTemplate(
+ chargingStation,
+ connectorId
+ );
if (energySampledValueTemplate) {
OCPP16ServiceUtils.checkMeasurandPowerDivider(
chargingStation,
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
)
sampledValue: [],
};
// Energy.Active.Import.Register measurand (default)
- const sampledValueTemplate = chargingStation.getSampledValueTemplate(connectorId);
+ const sampledValueTemplate = ChargingStationUtils.getSampledValueTemplate(
+ chargingStation,
+ connectorId
+ );
const unitDivider = sampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1;
meterValue.sampledValue.push(
OCPP16ServiceUtils.buildSampledValue(
sampledValue: [],
};
// Energy.Active.Import.Register measurand (default)
- const sampledValueTemplate = chargingStation.getSampledValueTemplate(connectorId);
+ const sampledValueTemplate = ChargingStationUtils.getSampledValueTemplate(
+ chargingStation,
+ connectorId
+ );
const unitDivider = sampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1;
meterValue.sampledValue.push(
OCPP16ServiceUtils.buildSampledValue(
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.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?.powerDivider <= 0) {
+ const errMsg = `${chargingStation.logPrefix()} MeterValues measurand ${
+ measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
+ }: powerDivider have zero or below value ${chargingStation.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;
+ }
+ }
}