// 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,
OCPP16MeterValuePhase,
OCPP16SampledValue,
} from '../../../types/ocpp/1.6/MeterValues';
-
-import type ChargingStation from '../../ChargingStation';
-import Constants from '../../../utils/Constants';
+import {
+ OCPP16IncomingRequestCommand,
+ OCPP16RequestCommand,
+} from '../../../types/ocpp/1.6/Requests';
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 { RequestCommand } from '../../../types/ocpp/Requests';
-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(
+export class OCPP16ServiceUtils extends OCPPServiceUtils {
+ public static checkFeatureProfile(
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, RequestCommand.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, RequestCommand.METER_VALUES);
- }
- }
-
- 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;
+ 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 buildMeterValue(
};
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
powerSampledValueTemplate.measurand ??
OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
}: Unknown ${chargingStation.getCurrentOutType()} currentOutType in template file ${
- chargingStation.stationTemplateFile
+ chargingStation.templateFile
}, cannot calculate ${
powerSampledValueTemplate.measurand ??
OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
} measurand value`;
const powerMeasurandValues = {} as MeasurandValues;
const unitDivider = powerSampledValueTemplate?.unit === MeterValueUnit.KILO_WATT ? 1000 : 1;
- const maxPower = Math.round(
- chargingStation.stationInfo.maxPower / chargingStation.stationInfo.powerDivider
- );
- const maxPowerPerPhase = Math.round(
- chargingStation.stationInfo.maxPower /
- 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:
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
);
powerMeasurandValues.L1 =
phase1FluctuatedValue ??
defaultFluctuatedPowerPerPhase ??
- Utils.getRandomFloatRounded(maxPowerPerPhase / unitDivider);
+ Utils.getRandomFloatRounded(connectorMaximumPowerPerPhase / unitDivider);
powerMeasurandValues.L2 =
phase2FluctuatedValue ??
defaultFluctuatedPowerPerPhase ??
- Utils.getRandomFloatRounded(maxPowerPerPhase / unitDivider);
+ Utils.getRandomFloatRounded(connectorMaximumPowerPerPhase / unitDivider);
powerMeasurandValues.L3 =
phase3FluctuatedValue ??
defaultFluctuatedPowerPerPhase ??
- Utils.getRandomFloatRounded(maxPowerPerPhase / unitDivider);
+ Utils.getRandomFloatRounded(connectorMaximumPowerPerPhase / unitDivider);
} 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
)
- : Utils.getRandomFloatRounded(maxPower / unitDivider);
+ : Utils.getRandomFloatRounded(connectorMaximumPower / unitDivider);
powerMeasurandValues.L2 = 0;
powerMeasurandValues.L3 = 0;
}
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
)
- : Utils.getRandomFloatRounded(maxPower / unitDivider);
+ : Utils.getRandomFloatRounded(connectorMaximumPower / unitDivider);
break;
default:
logger.error(errMsg);
)
);
const sampledValuesIndex = meterValue.sampledValue.length - 1;
- const maxPowerRounded = Utils.roundTo(maxPower / unitDivider, 2);
+ const connectorMaximumPowerRounded = Utils.roundTo(connectorMaximumPower / unitDivider, 2);
if (
- Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxPowerRounded ||
+ Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) >
+ connectorMaximumPowerRounded ||
debug
) {
logger.error(
OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
meterValue.sampledValue[sampledValuesIndex].value
- }/${maxPowerRounded}`
+ }/${connectorMaximumPowerRounded}`
);
}
for (
)
);
const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1;
- const maxPowerPerPhaseRounded = Utils.roundTo(maxPowerPerPhase / unitDivider, 2);
+ const connectorMaximumPowerPerPhaseRounded = Utils.roundTo(
+ connectorMaximumPowerPerPhase / unitDivider,
+ 2
+ );
if (
Utils.convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) >
- maxPowerPerPhaseRounded ||
+ connectorMaximumPowerPerPhaseRounded ||
debug
) {
logger.error(
meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
}, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
meterValue.sampledValue[sampledValuesPerPhaseIndex].value
- }/${maxPowerPerPhaseRounded}`
+ }/${connectorMaximumPowerPerPhaseRounded}`
);
}
}
}
// 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
currentSampledValueTemplate.measurand ??
OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
}: Unknown ${chargingStation.getCurrentOutType()} currentOutType in template file ${
- chargingStation.stationTemplateFile
+ chargingStation.templateFile
}, cannot calculate ${
currentSampledValueTemplate.measurand ??
OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
} measurand value`;
const currentMeasurandValues: MeasurandValues = {} as MeasurandValues;
- let maxAmperage: number;
+ const connectorMaximumAvailablePower =
+ chargingStation.getConnectorMaximumAvailablePower(connectorId);
+ let connectorMaximumAmperage: number;
switch (chargingStation.getCurrentOutType()) {
case CurrentType.AC:
- maxAmperage = ACElectricUtils.amperagePerPhaseFromPower(
+ connectorMaximumAmperage = ACElectricUtils.amperagePerPhaseFromPower(
chargingStation.getNumberOfPhases(),
- chargingStation.stationInfo.maxPower / chargingStation.stationInfo.powerDivider,
+ connectorMaximumAvailablePower,
chargingStation.getVoltageOut()
);
if (chargingStation.getNumberOfPhases() === 3) {
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
);
currentMeasurandValues.L1 =
phase1FluctuatedValue ??
defaultFluctuatedAmperagePerPhase ??
- Utils.getRandomFloatRounded(maxAmperage);
+ Utils.getRandomFloatRounded(connectorMaximumAmperage);
currentMeasurandValues.L2 =
phase2FluctuatedValue ??
defaultFluctuatedAmperagePerPhase ??
- Utils.getRandomFloatRounded(maxAmperage);
+ Utils.getRandomFloatRounded(connectorMaximumAmperage);
currentMeasurandValues.L3 =
phase3FluctuatedValue ??
defaultFluctuatedAmperagePerPhase ??
- Utils.getRandomFloatRounded(maxAmperage);
+ Utils.getRandomFloatRounded(connectorMaximumAmperage);
} else {
currentMeasurandValues.L1 = currentSampledValueTemplate.value
? Utils.getRandomFloatFluctuatedRounded(
- parseInt(currentSampledValueTemplate.value),
+ OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+ currentSampledValueTemplate.value,
+ connectorMaximumAmperage,
+ { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
+ ),
currentSampledValueTemplate.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT
)
- : Utils.getRandomFloatRounded(maxAmperage);
+ : Utils.getRandomFloatRounded(connectorMaximumAmperage);
currentMeasurandValues.L2 = 0;
currentMeasurandValues.L3 = 0;
}
);
break;
case CurrentType.DC:
- maxAmperage = DCElectricUtils.amperage(
- chargingStation.stationInfo.maxPower / chargingStation.stationInfo.powerDivider,
+ connectorMaximumAmperage = DCElectricUtils.amperage(
+ connectorMaximumAvailablePower,
chargingStation.getVoltageOut()
);
currentMeasurandValues.allPhases = currentSampledValueTemplate.value
? Utils.getRandomFloatFluctuatedRounded(
- parseInt(currentSampledValueTemplate.value),
+ OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
+ currentSampledValueTemplate.value,
+ connectorMaximumAmperage,
+ { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
+ ),
currentSampledValueTemplate.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT
)
- : Utils.getRandomFloatRounded(maxAmperage);
+ : Utils.getRandomFloatRounded(connectorMaximumAmperage);
break;
default:
logger.error(errMsg);
);
const sampledValuesIndex = meterValue.sampledValue.length - 1;
if (
- Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxAmperage ||
+ Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) >
+ connectorMaximumAmperage ||
debug
) {
logger.error(
OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
meterValue.sampledValue[sampledValuesIndex].value
- }/${maxAmperage}`
+ }/${connectorMaximumAmperage}`
);
}
for (
const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1;
if (
Utils.convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) >
- maxAmperage ||
+ connectorMaximumAmperage ||
debug
) {
logger.error(
meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
}, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
meterValue.sampledValue[sampledValuesPerPhaseIndex].value
- }/${maxAmperage}`
+ }/${connectorMaximumAmperage}`
);
}
}
}
// Energy.Active.Import.Register measurand (default)
- const energySampledValueTemplate = chargingStation.getSampledValueTemplate(connectorId);
+ const energySampledValueTemplate = ChargingStationUtils.getSampledValueTemplate(
+ chargingStation,
+ connectorId
+ );
if (energySampledValueTemplate) {
OCPP16ServiceUtils.checkMeasurandPowerDivider(
chargingStation,
);
const unitDivider =
energySampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1;
- const maxEnergyRounded = Utils.roundTo(
- ((chargingStation.stationInfo.maxPower / chargingStation.stationInfo.powerDivider) *
- interval) /
- (3600 * 1000),
+ const connectorMaximumAvailablePower =
+ chargingStation.getConnectorMaximumAvailablePower(connectorId);
+ const connectorMaximumEnergyRounded = Utils.roundTo(
+ (connectorMaximumAvailablePower * interval) / (3600 * 1000),
2
);
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(maxEnergyRounded);
+ : Utils.getRandomFloatRounded(connectorMaximumEnergyRounded);
// Persist previous value on connector
if (
connector &&
)
);
const sampledValuesIndex = meterValue.sampledValue.length - 1;
- if (energyValueRounded > maxEnergyRounded || 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}/${maxEnergyRounded}, duration: ${Utils.roundTo(
+ }, value: ${energyValueRounded}/${connectorMaximumEnergyRounded}, duration: ${Utils.roundTo(
interval / (3600 * 1000),
4
)}h`
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;
+ }
+ }
}