import { OCPP20Constants } from './2.0/OCPP20Constants.js'
import { OCPPConstants } from './OCPPConstants.js'
+interface MultiPhaseMeasurandData {
+ perPhaseTemplates: MeasurandPerPhaseSampledValueTemplates
+ template: SampledValueTemplate
+ values: MeasurandValues
+}
+
+interface SingleValueMeasurandData {
+ template: SampledValueTemplate
+ value: number
+}
+
export const getMessageTypeString = (messageType: MessageType | undefined): string => {
switch (messageType) {
case MessageType.CALL_ERROR_MESSAGE:
}
}
-export const buildMeterValue = (
+const buildSocMeasurandValue = (
+ chargingStation: ChargingStation,
+ connectorId: number
+): null | SingleValueMeasurandData => {
+ const socSampledValueTemplate = getSampledValueTemplate(
+ chargingStation,
+ connectorId,
+ MeterValueMeasurand.STATE_OF_CHARGE
+ )
+ if (socSampledValueTemplate == null) {
+ return null
+ }
+
+ const socMaximumValue = 100
+ const socMinimumValue = socSampledValueTemplate.minimumValue ?? 0
+ const socSampledValueTemplateValue = isNotEmptyString(socSampledValueTemplate.value)
+ ? getRandomFloatFluctuatedRounded(
+ Number.parseInt(socSampledValueTemplate.value),
+ socSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
+ )
+ : randomInt(socMinimumValue, socMaximumValue + 1)
+
+ return {
+ template: socSampledValueTemplate,
+ value: socSampledValueTemplateValue,
+ }
+}
+
+const validateSocMeasurandValue = (
chargingStation: ChargingStation,
connectorId: number,
- transactionId: number,
- interval: number,
- debug = false
-): MeterValue => {
+ sampledValue: SampledValue,
+ socMinimumValue: number,
+ socMaximumValue: number,
+ debug: boolean
+): void => {
const connector = chargingStation.getConnectorStatus(connectorId)
- let meterValue: MeterValue
- let connectorMaximumAvailablePower: number | undefined
- let socSampledValueTemplate: SampledValueTemplate | undefined
- let voltageSampledValueTemplate: SampledValueTemplate | undefined
- let powerSampledValueTemplate: SampledValueTemplate | undefined
- let powerPerPhaseSampledValueTemplates: MeasurandPerPhaseSampledValueTemplates = {}
- let currentSampledValueTemplate: SampledValueTemplate | undefined
- let currentPerPhaseSampledValueTemplates: MeasurandPerPhaseSampledValueTemplates = {}
- let energySampledValueTemplate: SampledValueTemplate | undefined
- switch (chargingStation.stationInfo?.ocppVersion) {
- case OCPPVersion.VERSION_16:
- meterValue = {
- sampledValue: [],
- timestamp: new Date(),
- }
- // SoC measurand
- socSampledValueTemplate = getSampledValueTemplate(
+ if (
+ convertToInt(sampledValue.value) > socMaximumValue ||
+ convertToInt(sampledValue.value) < socMinimumValue ||
+ debug
+ ) {
+ logger.error(
+ `${chargingStation.logPrefix()} MeterValues measurand ${
+ sampledValue.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+ }: connector id ${connectorId.toString()}, transaction id ${connector?.transactionId?.toString()}, value: ${socMinimumValue.toString()}/${sampledValue.value.toString()}/${socMaximumValue.toString()}`
+ )
+ }
+}
+
+const buildVoltageMeasurandValue = (
+ chargingStation: ChargingStation,
+ connectorId: number
+): null | SingleValueMeasurandData => {
+ const voltageSampledValueTemplate = getSampledValueTemplate(
+ chargingStation,
+ connectorId,
+ MeterValueMeasurand.VOLTAGE
+ )
+ if (voltageSampledValueTemplate == null) {
+ return null
+ }
+
+ const voltageSampledValueTemplateValue = isNotEmptyString(voltageSampledValueTemplate.value)
+ ? Number.parseInt(voltageSampledValueTemplate.value)
+ : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ chargingStation.stationInfo.voltageOut!
+ const fluctuationPercent =
+ voltageSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
+ const voltageMeasurandValue = getRandomFloatFluctuatedRounded(
+ voltageSampledValueTemplateValue,
+ fluctuationPercent
+ )
+
+ return {
+ template: voltageSampledValueTemplate,
+ value: voltageMeasurandValue,
+ }
+}
+
+const addMainVoltageToMeterValue = (
+ chargingStation: ChargingStation,
+ meterValue: MeterValue,
+ voltageData: { template: SampledValueTemplate; value: number }
+): void => {
+ if (
+ chargingStation.getNumberOfPhases() !== 3 ||
+ (chargingStation.getNumberOfPhases() === 3 &&
+ chargingStation.stationInfo.mainVoltageMeterValues === true)
+ ) {
+ meterValue.sampledValue.push(
+ buildSampledValue(
+ chargingStation.stationInfo.ocppVersion,
+ voltageData.template,
+ voltageData.value
+ )
+ )
+ }
+}
+
+const addPhaseVoltageToMeterValue = (
+ chargingStation: ChargingStation,
+ connectorId: number,
+ meterValue: MeterValue,
+ mainVoltageData: { template: SampledValueTemplate; value: number },
+ phase: number
+): void => {
+ const phaseLineToNeutralValue = `L${phase.toString()}-N` as MeterValuePhase
+ const voltagePhaseLineToNeutralSampledValueTemplate = getSampledValueTemplate(
+ chargingStation,
+ connectorId,
+ MeterValueMeasurand.VOLTAGE,
+ phaseLineToNeutralValue
+ )
+ let voltagePhaseLineToNeutralMeasurandValue: number | undefined
+ if (voltagePhaseLineToNeutralSampledValueTemplate != null) {
+ const voltagePhaseLineToNeutralSampledValueTemplateValue = isNotEmptyString(
+ voltagePhaseLineToNeutralSampledValueTemplate.value
+ )
+ ? Number.parseInt(voltagePhaseLineToNeutralSampledValueTemplate.value)
+ : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ chargingStation.stationInfo.voltageOut!
+ const fluctuationPhaseToNeutralPercent =
+ voltagePhaseLineToNeutralSampledValueTemplate.fluctuationPercent ??
+ Constants.DEFAULT_FLUCTUATION_PERCENT
+ voltagePhaseLineToNeutralMeasurandValue = getRandomFloatFluctuatedRounded(
+ voltagePhaseLineToNeutralSampledValueTemplateValue,
+ fluctuationPhaseToNeutralPercent
+ )
+ }
+ meterValue.sampledValue.push(
+ buildSampledValue(
+ chargingStation.stationInfo.ocppVersion,
+ voltagePhaseLineToNeutralSampledValueTemplate ?? mainVoltageData.template,
+ voltagePhaseLineToNeutralMeasurandValue ?? mainVoltageData.value,
+ undefined,
+ phaseLineToNeutralValue
+ )
+ )
+}
+
+const addLineToLineVoltageToMeterValue = (
+ chargingStation: ChargingStation,
+ connectorId: number,
+ meterValue: MeterValue,
+ mainVoltageData: { template: SampledValueTemplate; value: number },
+ phase: number
+): void => {
+ if (chargingStation.stationInfo.phaseLineToLineVoltageMeterValues === true) {
+ const phaseLineToLineValue = `L${phase.toString()}-L${
+ (phase + 1) % chargingStation.getNumberOfPhases() !== 0
+ ? ((phase + 1) % chargingStation.getNumberOfPhases()).toString()
+ : chargingStation.getNumberOfPhases().toString()
+ }` as MeterValuePhase
+ const voltagePhaseLineToLineValueRounded = roundTo(
+ Math.sqrt(chargingStation.getNumberOfPhases()) *
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ chargingStation.stationInfo.voltageOut!,
+ 2
+ )
+ const voltagePhaseLineToLineSampledValueTemplate = getSampledValueTemplate(
+ chargingStation,
+ connectorId,
+ MeterValueMeasurand.VOLTAGE,
+ phaseLineToLineValue
+ )
+ let voltagePhaseLineToLineMeasurandValue: number | undefined
+ if (voltagePhaseLineToLineSampledValueTemplate != null) {
+ const voltagePhaseLineToLineSampledValueTemplateValue = isNotEmptyString(
+ voltagePhaseLineToLineSampledValueTemplate.value
+ )
+ ? Number.parseInt(voltagePhaseLineToLineSampledValueTemplate.value)
+ : voltagePhaseLineToLineValueRounded
+ const fluctuationPhaseLineToLinePercent =
+ voltagePhaseLineToLineSampledValueTemplate.fluctuationPercent ??
+ Constants.DEFAULT_FLUCTUATION_PERCENT
+ voltagePhaseLineToLineMeasurandValue = getRandomFloatFluctuatedRounded(
+ voltagePhaseLineToLineSampledValueTemplateValue,
+ fluctuationPhaseLineToLinePercent
+ )
+ }
+ meterValue.sampledValue.push(
+ buildSampledValue(
+ chargingStation.stationInfo.ocppVersion,
+ voltagePhaseLineToLineSampledValueTemplate ?? mainVoltageData.template,
+ voltagePhaseLineToLineMeasurandValue ?? voltagePhaseLineToLineValueRounded,
+ undefined,
+ phaseLineToLineValue
+ )
+ )
+ }
+}
+
+const buildEnergyMeasurandValue = (
+ chargingStation: ChargingStation,
+ connectorId: number,
+ interval: number
+): null | SingleValueMeasurandData => {
+ const energyTemplate = getSampledValueTemplate(chargingStation, connectorId)
+ if (energyTemplate == null) {
+ return null
+ }
+
+ checkMeasurandPowerDivider(chargingStation, energyTemplate.measurand)
+ const unitDivider = energyTemplate.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1
+ const connectorMaximumAvailablePower =
+ chargingStation.getConnectorMaximumAvailablePower(connectorId)
+ const connectorMaximumEnergyRounded = roundTo(
+ (connectorMaximumAvailablePower * interval) / (3600 * 1000),
+ 2
+ )
+ const connectorMinimumEnergyRounded = roundTo(energyTemplate.minimumValue ?? 0, 2)
+
+ const energyValueRounded = isNotEmptyString(energyTemplate.value)
+ ? getRandomFloatFluctuatedRounded(
+ getLimitFromSampledValueTemplateCustomValue(
+ energyTemplate.value,
+ connectorMaximumEnergyRounded,
+ connectorMinimumEnergyRounded,
+ {
+ fallbackValue: connectorMinimumEnergyRounded,
+ limitationEnabled: chargingStation.stationInfo?.customValueLimitationMeterValues,
+ unitMultiplier: unitDivider,
+ }
+ ),
+ energyTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
+ )
+ : getRandomFloatRounded(connectorMaximumEnergyRounded, connectorMinimumEnergyRounded)
+
+ return {
+ template: energyTemplate,
+ value: energyValueRounded,
+ }
+}
+
+const updateConnectorEnergyValues = (
+ connector: ConnectorStatus | undefined,
+ energyValue: number
+): void => {
+ if (connector != null) {
+ if (
+ connector.energyActiveImportRegisterValue != null &&
+ connector.energyActiveImportRegisterValue >= 0 &&
+ connector.transactionEnergyActiveImportRegisterValue != null &&
+ connector.transactionEnergyActiveImportRegisterValue >= 0
+ ) {
+ connector.energyActiveImportRegisterValue += energyValue
+ connector.transactionEnergyActiveImportRegisterValue += energyValue
+ } else {
+ connector.energyActiveImportRegisterValue = 0
+ connector.transactionEnergyActiveImportRegisterValue = 0
+ }
+ }
+}
+
+const validateEnergyMeasurandValue = (
+ chargingStation: ChargingStation,
+ connectorId: number,
+ sampledValue: SampledValue,
+ energyValue: number,
+ minValue: number,
+ maxValue: number,
+ interval: number,
+ debug: boolean
+): void => {
+ if (energyValue > maxValue || energyValue < minValue || debug) {
+ const connector = chargingStation.getConnectorStatus(connectorId)
+ logger.error(
+ `${chargingStation.logPrefix()} MeterValues measurand ${
+ sampledValue.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+ }: connector id ${connectorId.toString()}, transaction id ${connector?.transactionId?.toString()}, value: ${minValue.toString()}/${energyValue.toString()}/${maxValue.toString()}, duration: ${interval.toString()}ms`
+ )
+ }
+}
+
+const buildPowerMeasurandValue = (
+ chargingStation: ChargingStation,
+ connectorId: number
+): MultiPhaseMeasurandData | null => {
+ const powerTemplate = getSampledValueTemplate(
+ chargingStation,
+ connectorId,
+ MeterValueMeasurand.POWER_ACTIVE_IMPORT
+ )
+ if (powerTemplate == null) {
+ return null
+ }
+
+ let perPhaseTemplates: MeasurandPerPhaseSampledValueTemplates = {}
+ if (chargingStation.getNumberOfPhases() === 3) {
+ perPhaseTemplates = {
+ L1: getSampledValueTemplate(
chargingStation,
connectorId,
- MeterValueMeasurand.STATE_OF_CHARGE
- )
- if (socSampledValueTemplate != null) {
- const socMaximumValue = 100
- const socMinimumValue = socSampledValueTemplate.minimumValue ?? 0
- const socSampledValueTemplateValue = isNotEmptyString(socSampledValueTemplate.value)
+ MeterValueMeasurand.POWER_ACTIVE_IMPORT,
+ MeterValuePhase.L1_N
+ ),
+ L2: getSampledValueTemplate(
+ chargingStation,
+ connectorId,
+ MeterValueMeasurand.POWER_ACTIVE_IMPORT,
+ MeterValuePhase.L2_N
+ ),
+ L3: getSampledValueTemplate(
+ chargingStation,
+ connectorId,
+ MeterValueMeasurand.POWER_ACTIVE_IMPORT,
+ MeterValuePhase.L3_N
+ ),
+ }
+ }
+
+ checkMeasurandPowerDivider(chargingStation, powerTemplate.measurand)
+ const powerValues: MeasurandValues = {} as MeasurandValues
+ const unitDivider = powerTemplate.unit === MeterValueUnit.KILO_WATT ? 1000 : 1
+ const connectorMaximumAvailablePower =
+ chargingStation.getConnectorMaximumAvailablePower(connectorId)
+ const connectorMaximumPower = Math.round(connectorMaximumAvailablePower)
+ const connectorMaximumPowerPerPhase = Math.round(
+ connectorMaximumAvailablePower / chargingStation.getNumberOfPhases()
+ )
+ const connectorMinimumPower = Math.round(powerTemplate.minimumValue ?? 0)
+ const connectorMinimumPowerPerPhase = Math.round(
+ connectorMinimumPower / chargingStation.getNumberOfPhases()
+ )
+
+ switch (chargingStation.stationInfo?.currentOutType) {
+ case CurrentType.AC:
+ if (chargingStation.getNumberOfPhases() === 3) {
+ const defaultFluctuatedPowerPerPhase = isNotEmptyString(powerTemplate.value)
? getRandomFloatFluctuatedRounded(
- Number.parseInt(socSampledValueTemplate.value),
- socSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
+ getLimitFromSampledValueTemplateCustomValue(
+ powerTemplate.value,
+ connectorMaximumPower / unitDivider,
+ connectorMinimumPower / unitDivider,
+ {
+ fallbackValue: connectorMinimumPower / unitDivider,
+ limitationEnabled: chargingStation.stationInfo.customValueLimitationMeterValues,
+ }
+ ) / chargingStation.getNumberOfPhases(),
+ powerTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
)
- : randomInt(socMinimumValue, socMaximumValue + 1)
- meterValue.sampledValue.push(
- buildSampledValue(
- chargingStation.stationInfo.ocppVersion,
- socSampledValueTemplate,
- socSampledValueTemplateValue
+ : undefined
+
+ const phase1Value = isNotEmptyString(perPhaseTemplates.L1?.value)
+ ? getRandomFloatFluctuatedRounded(
+ getLimitFromSampledValueTemplateCustomValue(
+ perPhaseTemplates.L1.value,
+ connectorMaximumPowerPerPhase / unitDivider,
+ connectorMinimumPowerPerPhase / unitDivider,
+ {
+ fallbackValue: connectorMinimumPowerPerPhase / unitDivider,
+ limitationEnabled: chargingStation.stationInfo.customValueLimitationMeterValues,
+ }
+ ),
+ perPhaseTemplates.L1.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
)
- )
- const sampledValuesIndex = meterValue.sampledValue.length - 1
- if (
- convertToInt(meterValue.sampledValue[sampledValuesIndex].value) > socMaximumValue ||
- convertToInt(meterValue.sampledValue[sampledValuesIndex].value) < socMinimumValue ||
- debug
- ) {
- logger.error(
- `${chargingStation.logPrefix()} MeterValues measurand ${
- meterValue.sampledValue[sampledValuesIndex].measurand ??
- MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
- }: connector id ${connectorId.toString()}, transaction id ${connector?.transactionId?.toString()}, value: ${socMinimumValue.toString()}/${meterValue.sampledValue[
- sampledValuesIndex
- ].value.toString()}/${socMaximumValue.toString()}`
+ : undefined
+
+ const phase2Value = isNotEmptyString(perPhaseTemplates.L2?.value)
+ ? getRandomFloatFluctuatedRounded(
+ getLimitFromSampledValueTemplateCustomValue(
+ perPhaseTemplates.L2.value,
+ connectorMaximumPowerPerPhase / unitDivider,
+ connectorMinimumPowerPerPhase / unitDivider,
+ {
+ fallbackValue: connectorMinimumPowerPerPhase / unitDivider,
+ limitationEnabled: chargingStation.stationInfo.customValueLimitationMeterValues,
+ }
+ ),
+ perPhaseTemplates.L2.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
)
- }
+ : undefined
+
+ const phase3Value = isNotEmptyString(perPhaseTemplates.L3?.value)
+ ? getRandomFloatFluctuatedRounded(
+ getLimitFromSampledValueTemplateCustomValue(
+ perPhaseTemplates.L3.value,
+ connectorMaximumPowerPerPhase / unitDivider,
+ connectorMinimumPowerPerPhase / unitDivider,
+ {
+ fallbackValue: connectorMinimumPowerPerPhase / unitDivider,
+ limitationEnabled: chargingStation.stationInfo.customValueLimitationMeterValues,
+ }
+ ),
+ perPhaseTemplates.L3.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
+ )
+ : undefined
+
+ powerValues.L1 =
+ phase1Value ??
+ defaultFluctuatedPowerPerPhase ??
+ getRandomFloatRounded(
+ connectorMaximumPowerPerPhase / unitDivider,
+ connectorMinimumPowerPerPhase / unitDivider
+ )
+ powerValues.L2 =
+ phase2Value ??
+ defaultFluctuatedPowerPerPhase ??
+ getRandomFloatRounded(
+ connectorMaximumPowerPerPhase / unitDivider,
+ connectorMinimumPowerPerPhase / unitDivider
+ )
+ powerValues.L3 =
+ phase3Value ??
+ defaultFluctuatedPowerPerPhase ??
+ getRandomFloatRounded(
+ connectorMaximumPowerPerPhase / unitDivider,
+ connectorMinimumPowerPerPhase / unitDivider
+ )
+ } else {
+ powerValues.L1 = isNotEmptyString(powerTemplate.value)
+ ? getRandomFloatFluctuatedRounded(
+ getLimitFromSampledValueTemplateCustomValue(
+ powerTemplate.value,
+ connectorMaximumPower / unitDivider,
+ connectorMinimumPower / unitDivider,
+ {
+ fallbackValue: connectorMinimumPower / unitDivider,
+ limitationEnabled: chargingStation.stationInfo.customValueLimitationMeterValues,
+ }
+ ),
+ powerTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
+ )
+ : getRandomFloatRounded(
+ connectorMaximumPower / unitDivider,
+ connectorMinimumPower / unitDivider
+ )
+ powerValues.L2 = 0
+ powerValues.L3 = 0
}
- // Voltage measurand
- voltageSampledValueTemplate = getSampledValueTemplate(
+ powerValues.allPhases = roundTo(powerValues.L1 + powerValues.L2 + powerValues.L3, 2)
+ break
+ case CurrentType.DC:
+ powerValues.allPhases = isNotEmptyString(powerTemplate.value)
+ ? getRandomFloatFluctuatedRounded(
+ getLimitFromSampledValueTemplateCustomValue(
+ powerTemplate.value,
+ connectorMaximumPower / unitDivider,
+ connectorMinimumPower / unitDivider,
+ {
+ fallbackValue: connectorMinimumPower / unitDivider,
+ limitationEnabled: chargingStation.stationInfo.customValueLimitationMeterValues,
+ }
+ ),
+ powerTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
+ )
+ : getRandomFloatRounded(
+ connectorMaximumPower / unitDivider,
+ connectorMinimumPower / unitDivider
+ )
+ break
+ default: {
+ const errMsg = `MeterValues measurand ${
+ powerTemplate.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+ }: Unknown ${chargingStation.stationInfo?.currentOutType} currentOutType in template file ${
+ chargingStation.templateFile
+ }, cannot calculate ${
+ powerTemplate.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
+ } measurand value`
+ logger.error(`${chargingStation.logPrefix()} ${errMsg}`)
+ throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, RequestCommand.METER_VALUES)
+ }
+ }
+
+ return {
+ perPhaseTemplates,
+ template: powerTemplate,
+ values: powerValues,
+ }
+}
+
+const validatePowerMeasurandValue = (
+ chargingStation: ChargingStation,
+ connectorId: number,
+ connector: ConnectorStatus | undefined,
+ sampledValue: SampledValue,
+ connectorMaximumPower: number,
+ connectorMinimumPower: number,
+ debug: boolean
+): void => {
+ if (
+ convertToFloat(sampledValue.value) > connectorMaximumPower ||
+ convertToFloat(sampledValue.value) < connectorMinimumPower ||
+ debug
+ ) {
+ logger.error(
+ `${chargingStation.logPrefix()} MeterValues measurand ${
+ sampledValue.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+ }: connector id ${connectorId.toString()}, transaction id ${connector?.transactionId?.toString()}, value: ${connectorMinimumPower.toString()}/${sampledValue.value.toString()}/${connectorMaximumPower.toString()}`
+ )
+ }
+}
+
+const validateCurrentMeasurandValue = (
+ chargingStation: ChargingStation,
+ connectorId: number,
+ connector: ConnectorStatus | undefined,
+ sampledValue: SampledValue,
+ connectorMaximumAmperage: number,
+ connectorMinimumAmperage: number,
+ debug: boolean
+): void => {
+ if (
+ convertToFloat(sampledValue.value) > connectorMaximumAmperage ||
+ convertToFloat(sampledValue.value) < connectorMinimumAmperage ||
+ debug
+ ) {
+ logger.error(
+ `${chargingStation.logPrefix()} MeterValues measurand ${
+ sampledValue.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+ }: connector id ${connectorId.toString()}, transaction id ${connector?.transactionId?.toString()}, value: ${connectorMinimumAmperage.toString()}/${sampledValue.value.toString()}/${connectorMaximumAmperage.toString()}`
+ )
+ }
+}
+
+const validateCurrentMeasurandPhaseValue = (
+ chargingStation: ChargingStation,
+ connectorId: number,
+ connector: ConnectorStatus | undefined,
+ sampledValue: SampledValue,
+ connectorMaximumAmperage: number,
+ connectorMinimumAmperage: number,
+ debug: boolean
+): void => {
+ if (
+ convertToFloat(sampledValue.value) > connectorMaximumAmperage ||
+ convertToFloat(sampledValue.value) < connectorMinimumAmperage ||
+ debug
+ ) {
+ logger.error(
+ `${chargingStation.logPrefix()} MeterValues measurand ${
+ sampledValue.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
+ }: phase ${
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+ sampledValue.phase
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+ }, connector id ${connectorId.toString()}, transaction id ${connector?.transactionId?.toString()}, value: ${connectorMinimumAmperage.toString()}/${sampledValue.value.toString()}/${connectorMaximumAmperage.toString()}`
+ )
+ }
+}
+
+const buildCurrentMeasurandValue = (
+ chargingStation: ChargingStation,
+ connectorId: number
+): MultiPhaseMeasurandData | null => {
+ const currentTemplate = getSampledValueTemplate(
+ chargingStation,
+ connectorId,
+ MeterValueMeasurand.CURRENT_IMPORT
+ )
+ if (currentTemplate == null) {
+ return null
+ }
+
+ let perPhaseTemplates: MeasurandPerPhaseSampledValueTemplates = {}
+ if (chargingStation.getNumberOfPhases() === 3) {
+ perPhaseTemplates = {
+ L1: getSampledValueTemplate(
+ chargingStation,
+ connectorId,
+ MeterValueMeasurand.CURRENT_IMPORT,
+ MeterValuePhase.L1
+ ),
+ L2: getSampledValueTemplate(
chargingStation,
connectorId,
- MeterValueMeasurand.VOLTAGE
+ MeterValueMeasurand.CURRENT_IMPORT,
+ MeterValuePhase.L2
+ ),
+ L3: getSampledValueTemplate(
+ chargingStation,
+ connectorId,
+ MeterValueMeasurand.CURRENT_IMPORT,
+ MeterValuePhase.L3
+ ),
+ }
+ }
+
+ checkMeasurandPowerDivider(chargingStation, currentTemplate.measurand)
+ const currentValues: MeasurandValues = {} as MeasurandValues
+ const connectorMaximumAvailablePower =
+ chargingStation.getConnectorMaximumAvailablePower(connectorId)
+ const connectorMinimumAmperage = currentTemplate.minimumValue ?? 0
+ let connectorMaximumAmperage: number
+
+ switch (chargingStation.stationInfo?.currentOutType) {
+ case CurrentType.AC:
+ connectorMaximumAmperage = ACElectricUtils.amperagePerPhaseFromPower(
+ chargingStation.getNumberOfPhases(),
+ connectorMaximumAvailablePower,
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ chargingStation.stationInfo.voltageOut!
)
- if (voltageSampledValueTemplate != null) {
- const voltageSampledValueTemplateValue = isNotEmptyString(voltageSampledValueTemplate.value)
- ? Number.parseInt(voltageSampledValueTemplate.value)
- : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- chargingStation.stationInfo.voltageOut!
- const fluctuationPercent =
- voltageSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
- const voltageMeasurandValue = getRandomFloatFluctuatedRounded(
- voltageSampledValueTemplateValue,
- fluctuationPercent
- )
- if (
- chargingStation.getNumberOfPhases() !== 3 ||
- (chargingStation.getNumberOfPhases() === 3 &&
- chargingStation.stationInfo.mainVoltageMeterValues === true)
- ) {
- meterValue.sampledValue.push(
- buildSampledValue(
- chargingStation.stationInfo.ocppVersion,
- voltageSampledValueTemplate,
- voltageMeasurandValue
- )
+ if (chargingStation.getNumberOfPhases() === 3) {
+ const defaultFluctuatedAmperagePerPhase = isNotEmptyString(currentTemplate.value)
+ ? getRandomFloatFluctuatedRounded(
+ getLimitFromSampledValueTemplateCustomValue(
+ currentTemplate.value,
+ connectorMaximumAmperage,
+ connectorMinimumAmperage,
+ {
+ fallbackValue: connectorMinimumAmperage,
+ limitationEnabled: chargingStation.stationInfo.customValueLimitationMeterValues,
+ }
+ ),
+ currentTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
)
- }
- for (
- let phase = 1;
- chargingStation.getNumberOfPhases() === 3 && phase <= chargingStation.getNumberOfPhases();
- phase++
- ) {
- const phaseLineToNeutralValue = `L${phase.toString()}-N`
- const voltagePhaseLineToNeutralSampledValueTemplate = getSampledValueTemplate(
- chargingStation,
- connectorId,
- MeterValueMeasurand.VOLTAGE,
- phaseLineToNeutralValue as MeterValuePhase
+ : undefined
+
+ const phase1Value = isNotEmptyString(perPhaseTemplates.L1?.value)
+ ? getRandomFloatFluctuatedRounded(
+ getLimitFromSampledValueTemplateCustomValue(
+ perPhaseTemplates.L1.value,
+ connectorMaximumAmperage,
+ connectorMinimumAmperage,
+ {
+ fallbackValue: connectorMinimumAmperage,
+ limitationEnabled: chargingStation.stationInfo.customValueLimitationMeterValues,
+ }
+ ),
+ perPhaseTemplates.L1.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
)
- let voltagePhaseLineToNeutralMeasurandValue: number | undefined
- if (voltagePhaseLineToNeutralSampledValueTemplate != null) {
- const voltagePhaseLineToNeutralSampledValueTemplateValue = isNotEmptyString(
- voltagePhaseLineToNeutralSampledValueTemplate.value
- )
- ? Number.parseInt(voltagePhaseLineToNeutralSampledValueTemplate.value)
- : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- chargingStation.stationInfo.voltageOut!
- const fluctuationPhaseToNeutralPercent =
- voltagePhaseLineToNeutralSampledValueTemplate.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT
- voltagePhaseLineToNeutralMeasurandValue = getRandomFloatFluctuatedRounded(
- voltagePhaseLineToNeutralSampledValueTemplateValue,
- fluctuationPhaseToNeutralPercent
- )
- }
- meterValue.sampledValue.push(
- buildSampledValue(
- chargingStation.stationInfo.ocppVersion,
- voltagePhaseLineToNeutralSampledValueTemplate ?? voltageSampledValueTemplate,
- voltagePhaseLineToNeutralMeasurandValue ?? voltageMeasurandValue,
- undefined,
- phaseLineToNeutralValue as MeterValuePhase
- )
+ : undefined
+
+ const phase2Value = isNotEmptyString(perPhaseTemplates.L2?.value)
+ ? getRandomFloatFluctuatedRounded(
+ getLimitFromSampledValueTemplateCustomValue(
+ perPhaseTemplates.L2.value,
+ connectorMaximumAmperage,
+ connectorMinimumAmperage,
+ {
+ fallbackValue: connectorMinimumAmperage,
+ limitationEnabled: chargingStation.stationInfo.customValueLimitationMeterValues,
+ }
+ ),
+ perPhaseTemplates.L2.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
)
- if (chargingStation.stationInfo.phaseLineToLineVoltageMeterValues === true) {
- const phaseLineToLineValue = `L${phase.toString()}-L${
- (phase + 1) % chargingStation.getNumberOfPhases() !== 0
- ? ((phase + 1) % chargingStation.getNumberOfPhases()).toString()
- : chargingStation.getNumberOfPhases().toString()
- }`
- const voltagePhaseLineToLineValueRounded = roundTo(
- Math.sqrt(chargingStation.getNumberOfPhases()) *
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- chargingStation.stationInfo.voltageOut!,
- 2
- )
- const voltagePhaseLineToLineSampledValueTemplate = getSampledValueTemplate(
- chargingStation,
- connectorId,
- MeterValueMeasurand.VOLTAGE,
- phaseLineToLineValue as MeterValuePhase
- )
- let voltagePhaseLineToLineMeasurandValue: number | undefined
- if (voltagePhaseLineToLineSampledValueTemplate != null) {
- const voltagePhaseLineToLineSampledValueTemplateValue = isNotEmptyString(
- voltagePhaseLineToLineSampledValueTemplate.value
- )
- ? Number.parseInt(voltagePhaseLineToLineSampledValueTemplate.value)
- : voltagePhaseLineToLineValueRounded
- const fluctuationPhaseLineToLinePercent =
- voltagePhaseLineToLineSampledValueTemplate.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT
- voltagePhaseLineToLineMeasurandValue = getRandomFloatFluctuatedRounded(
- voltagePhaseLineToLineSampledValueTemplateValue,
- fluctuationPhaseLineToLinePercent
- )
+ : undefined
+
+ const phase3Value = isNotEmptyString(perPhaseTemplates.L3?.value)
+ ? getRandomFloatFluctuatedRounded(
+ getLimitFromSampledValueTemplateCustomValue(
+ perPhaseTemplates.L3.value,
+ connectorMaximumAmperage,
+ connectorMinimumAmperage,
+ {
+ fallbackValue: connectorMinimumAmperage,
+ limitationEnabled: chargingStation.stationInfo.customValueLimitationMeterValues,
+ }
+ ),
+ perPhaseTemplates.L3.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
+ )
+ : undefined
+
+ currentValues.L1 =
+ phase1Value ??
+ defaultFluctuatedAmperagePerPhase ??
+ getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage)
+ currentValues.L2 =
+ phase2Value ??
+ defaultFluctuatedAmperagePerPhase ??
+ getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage)
+ currentValues.L3 =
+ phase3Value ??
+ defaultFluctuatedAmperagePerPhase ??
+ getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage)
+ } else {
+ currentValues.L1 = isNotEmptyString(currentTemplate.value)
+ ? getRandomFloatFluctuatedRounded(
+ getLimitFromSampledValueTemplateCustomValue(
+ currentTemplate.value,
+ connectorMaximumAmperage,
+ connectorMinimumAmperage,
+ {
+ fallbackValue: connectorMinimumAmperage,
+ limitationEnabled: chargingStation.stationInfo.customValueLimitationMeterValues,
+ }
+ ),
+ currentTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
+ )
+ : getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage)
+ currentValues.L2 = 0
+ currentValues.L3 = 0
+ }
+ currentValues.allPhases = roundTo(
+ (currentValues.L1 + currentValues.L2 + currentValues.L3) /
+ chargingStation.getNumberOfPhases(),
+ 2
+ )
+ break
+ case CurrentType.DC:
+ connectorMaximumAmperage = DCElectricUtils.amperage(
+ connectorMaximumAvailablePower,
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ chargingStation.stationInfo.voltageOut!
+ )
+ currentValues.allPhases = isNotEmptyString(currentTemplate.value)
+ ? getRandomFloatFluctuatedRounded(
+ getLimitFromSampledValueTemplateCustomValue(
+ currentTemplate.value,
+ connectorMaximumAmperage,
+ connectorMinimumAmperage,
+ {
+ fallbackValue: connectorMinimumAmperage,
+ limitationEnabled: chargingStation.stationInfo.customValueLimitationMeterValues,
}
- const defaultVoltagePhaseLineToLineMeasurandValue = getRandomFloatFluctuatedRounded(
- voltagePhaseLineToLineValueRounded,
- fluctuationPercent
- )
- meterValue.sampledValue.push(
- buildSampledValue(
- chargingStation.stationInfo.ocppVersion,
- voltagePhaseLineToLineSampledValueTemplate ?? voltageSampledValueTemplate,
- voltagePhaseLineToLineMeasurandValue ?? defaultVoltagePhaseLineToLineMeasurandValue,
- undefined,
- phaseLineToLineValue as MeterValuePhase
- )
- )
- }
- }
+ ),
+ currentTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
+ )
+ : getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage)
+ break
+ default: {
+ const errMsg = `MeterValues measurand ${
+ currentTemplate.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+ }: Unknown ${chargingStation.stationInfo?.currentOutType} currentOutType in template file ${
+ chargingStation.templateFile
+ }, cannot calculate ${
+ currentTemplate.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
+ } measurand value`
+ logger.error(`${chargingStation.logPrefix()} ${errMsg}`)
+ throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, RequestCommand.METER_VALUES)
+ }
+ }
+
+ return {
+ perPhaseTemplates,
+ template: currentTemplate,
+ values: currentValues,
+ }
+}
+
+export const buildMeterValue = (
+ chargingStation: ChargingStation,
+ connectorId: number,
+ transactionId: number,
+ interval: number,
+ debug = false
+): MeterValue => {
+ const connector = chargingStation.getConnectorStatus(connectorId)
+ let meterValue: MeterValue
+
+ switch (chargingStation.stationInfo?.ocppVersion) {
+ case OCPPVersion.VERSION_16: {
+ meterValue = {
+ sampledValue: [],
+ timestamp: new Date(),
}
- // Power.Active.Import measurand
- powerSampledValueTemplate = getSampledValueTemplate(
- chargingStation,
- connectorId,
- MeterValueMeasurand.POWER_ACTIVE_IMPORT
- )
- if (chargingStation.getNumberOfPhases() === 3) {
- powerPerPhaseSampledValueTemplates = {
- L1: getSampledValueTemplate(
- chargingStation,
- connectorId,
- MeterValueMeasurand.POWER_ACTIVE_IMPORT,
- MeterValuePhase.L1_N
- ),
- L2: getSampledValueTemplate(
+ // SoC measurand
+ const socMeasurand = buildSocMeasurandValue(chargingStation, connectorId)
+ if (socMeasurand != null) {
+ const socSampledValue = buildSampledValue(
+ chargingStation.stationInfo.ocppVersion,
+ socMeasurand.template,
+ socMeasurand.value
+ )
+ meterValue.sampledValue.push(socSampledValue)
+ validateSocMeasurandValue(
+ chargingStation,
+ connectorId,
+ socSampledValue,
+ socMeasurand.template.minimumValue ?? 0,
+ 100,
+ debug
+ )
+ }
+ // Voltage measurand
+ const voltageMeasurand = buildVoltageMeasurandValue(chargingStation, connectorId)
+ if (voltageMeasurand != null) {
+ addMainVoltageToMeterValue(chargingStation, meterValue, voltageMeasurand)
+ for (
+ let phase = 1;
+ chargingStation.getNumberOfPhases() === 3 && phase <= chargingStation.getNumberOfPhases();
+ phase++
+ ) {
+ addPhaseVoltageToMeterValue(
chargingStation,
connectorId,
- MeterValueMeasurand.POWER_ACTIVE_IMPORT,
- MeterValuePhase.L2_N
- ),
- L3: getSampledValueTemplate(
+ meterValue,
+ voltageMeasurand,
+ phase
+ )
+ addLineToLineVoltageToMeterValue(
chargingStation,
connectorId,
- MeterValueMeasurand.POWER_ACTIVE_IMPORT,
- MeterValuePhase.L3_N
- ),
+ meterValue,
+ voltageMeasurand,
+ phase
+ )
}
}
- if (powerSampledValueTemplate != null) {
- checkMeasurandPowerDivider(chargingStation, powerSampledValueTemplate.measurand)
- const errMsg = `MeterValues measurand ${
- powerSampledValueTemplate.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
- }: Unknown ${chargingStation.stationInfo.currentOutType} currentOutType in template file ${
- chargingStation.templateFile
- }, cannot calculate ${
- powerSampledValueTemplate.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- } measurand value`
- const powerMeasurandValues: MeasurandValues = {} as MeasurandValues
- const unitDivider = powerSampledValueTemplate.unit === MeterValueUnit.KILO_WATT ? 1000 : 1
- connectorMaximumAvailablePower =
+ // Power.Active.Import measurand
+ const powerMeasurand = buildPowerMeasurandValue(chargingStation, connectorId)
+ if (powerMeasurand != null) {
+ const unitDivider = powerMeasurand.template.unit === MeterValueUnit.KILO_WATT ? 1000 : 1
+ const connectorMaximumAvailablePower =
chargingStation.getConnectorMaximumAvailablePower(connectorId)
const connectorMaximumPower = Math.round(connectorMaximumAvailablePower)
- const connectorMaximumPowerPerPhase = Math.round(
- connectorMaximumAvailablePower / chargingStation.getNumberOfPhases()
- )
- const connectorMinimumPower = Math.round(powerSampledValueTemplate.minimumValue ?? 0)
- const connectorMinimumPowerPerPhase = Math.round(
- connectorMinimumPower / chargingStation.getNumberOfPhases()
- )
- switch (chargingStation.stationInfo.currentOutType) {
- case CurrentType.AC:
- if (chargingStation.getNumberOfPhases() === 3) {
- const defaultFluctuatedPowerPerPhase = isNotEmptyString(
- powerSampledValueTemplate.value
- )
- ? getRandomFloatFluctuatedRounded(
- getLimitFromSampledValueTemplateCustomValue(
- powerSampledValueTemplate.value,
- connectorMaximumPower / unitDivider,
- connectorMinimumPower / unitDivider,
- {
- fallbackValue: connectorMinimumPower / unitDivider,
- limitationEnabled:
- chargingStation.stationInfo.customValueLimitationMeterValues,
- }
- ) / chargingStation.getNumberOfPhases(),
- powerSampledValueTemplate.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT
- )
- : undefined
- const phase1FluctuatedValue = isNotEmptyString(
- powerPerPhaseSampledValueTemplates.L1?.value
- )
- ? getRandomFloatFluctuatedRounded(
- getLimitFromSampledValueTemplateCustomValue(
- powerPerPhaseSampledValueTemplates.L1.value,
- connectorMaximumPowerPerPhase / unitDivider,
- connectorMinimumPowerPerPhase / unitDivider,
- {
- fallbackValue: connectorMinimumPowerPerPhase / unitDivider,
- limitationEnabled:
- chargingStation.stationInfo.customValueLimitationMeterValues,
- }
- ),
- powerPerPhaseSampledValueTemplates.L1.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT
- )
- : undefined
- const phase2FluctuatedValue = isNotEmptyString(
- powerPerPhaseSampledValueTemplates.L2?.value
- )
- ? getRandomFloatFluctuatedRounded(
- getLimitFromSampledValueTemplateCustomValue(
- powerPerPhaseSampledValueTemplates.L2.value,
- connectorMaximumPowerPerPhase / unitDivider,
- connectorMinimumPowerPerPhase / unitDivider,
- {
- fallbackValue: connectorMinimumPowerPerPhase / unitDivider,
- limitationEnabled:
- chargingStation.stationInfo.customValueLimitationMeterValues,
- }
- ),
- powerPerPhaseSampledValueTemplates.L2.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT
- )
- : undefined
- const phase3FluctuatedValue = isNotEmptyString(
- powerPerPhaseSampledValueTemplates.L3?.value
- )
- ? getRandomFloatFluctuatedRounded(
- getLimitFromSampledValueTemplateCustomValue(
- powerPerPhaseSampledValueTemplates.L3.value,
- connectorMaximumPowerPerPhase / unitDivider,
- connectorMinimumPowerPerPhase / unitDivider,
- {
- fallbackValue: connectorMinimumPowerPerPhase / unitDivider,
- limitationEnabled:
- chargingStation.stationInfo.customValueLimitationMeterValues,
- }
- ),
- powerPerPhaseSampledValueTemplates.L3.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT
- )
- : undefined
- powerMeasurandValues.L1 =
- phase1FluctuatedValue ??
- defaultFluctuatedPowerPerPhase ??
- getRandomFloatRounded(
- connectorMaximumPowerPerPhase / unitDivider,
- connectorMinimumPowerPerPhase / unitDivider
- )
- powerMeasurandValues.L2 =
- phase2FluctuatedValue ??
- defaultFluctuatedPowerPerPhase ??
- getRandomFloatRounded(
- connectorMaximumPowerPerPhase / unitDivider,
- connectorMinimumPowerPerPhase / unitDivider
- )
- powerMeasurandValues.L3 =
- phase3FluctuatedValue ??
- defaultFluctuatedPowerPerPhase ??
- getRandomFloatRounded(
- connectorMaximumPowerPerPhase / unitDivider,
- connectorMinimumPowerPerPhase / unitDivider
- )
- } else {
- powerMeasurandValues.L1 = isNotEmptyString(powerSampledValueTemplate.value)
- ? getRandomFloatFluctuatedRounded(
- getLimitFromSampledValueTemplateCustomValue(
- powerSampledValueTemplate.value,
- connectorMaximumPower / unitDivider,
- connectorMinimumPower / unitDivider,
- {
- fallbackValue: connectorMinimumPower / unitDivider,
- limitationEnabled:
- chargingStation.stationInfo.customValueLimitationMeterValues,
- }
- ),
- powerSampledValueTemplate.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT
- )
- : getRandomFloatRounded(
- connectorMaximumPower / unitDivider,
- connectorMinimumPower / unitDivider
- )
- powerMeasurandValues.L2 = 0
- powerMeasurandValues.L3 = 0
- }
- powerMeasurandValues.allPhases = roundTo(
- powerMeasurandValues.L1 + powerMeasurandValues.L2 + powerMeasurandValues.L3,
- 2
- )
- break
- case CurrentType.DC:
- powerMeasurandValues.allPhases = isNotEmptyString(powerSampledValueTemplate.value)
- ? getRandomFloatFluctuatedRounded(
- getLimitFromSampledValueTemplateCustomValue(
- powerSampledValueTemplate.value,
- connectorMaximumPower / unitDivider,
- connectorMinimumPower / unitDivider,
- {
- fallbackValue: connectorMinimumPower / unitDivider,
- limitationEnabled:
- chargingStation.stationInfo.customValueLimitationMeterValues,
- }
- ),
- powerSampledValueTemplate.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT
- )
- : getRandomFloatRounded(
- connectorMaximumPower / unitDivider,
- connectorMinimumPower / unitDivider
- )
- break
- default:
- logger.error(`${chargingStation.logPrefix()} ${errMsg}`)
- throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, RequestCommand.METER_VALUES)
- }
+ const connectorMinimumPower = Math.round(powerMeasurand.template.minimumValue ?? 0)
+
meterValue.sampledValue.push(
buildSampledValue(
chargingStation.stationInfo.ocppVersion,
- powerSampledValueTemplate,
- powerMeasurandValues.allPhases
+ powerMeasurand.template,
+ powerMeasurand.values.allPhases
)
)
const sampledValuesIndex = meterValue.sampledValue.length - 1
- const connectorMaximumPowerRounded = roundTo(connectorMaximumPower / unitDivider, 2)
- const connectorMinimumPowerRounded = roundTo(connectorMinimumPower / unitDivider, 2)
- if (
- convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) >
- connectorMaximumPowerRounded ||
- convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) <
- connectorMinimumPowerRounded ||
+ validatePowerMeasurandValue(
+ chargingStation,
+ connectorId,
+ connector,
+ meterValue.sampledValue[sampledValuesIndex],
+ connectorMaximumPower / unitDivider,
+ connectorMinimumPower / unitDivider,
debug
- ) {
- logger.error(
- `${chargingStation.logPrefix()} MeterValues measurand ${
- meterValue.sampledValue[sampledValuesIndex].measurand ??
- MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
- }: connector id ${connectorId.toString()}, transaction id ${connector?.transactionId?.toString()}, value: ${connectorMinimumPowerRounded.toString()}/${meterValue.sampledValue[
- sampledValuesIndex
- ].value.toString()}/${connectorMaximumPowerRounded.toString()}`
- )
- }
- for (
- let phase = 1;
- chargingStation.getNumberOfPhases() === 3 && phase <= chargingStation.getNumberOfPhases();
- phase++
- ) {
- const phaseValue = `L${phase.toString()}-N`
- meterValue.sampledValue.push(
- buildSampledValue(
- chargingStation.stationInfo.ocppVersion,
- powerPerPhaseSampledValueTemplates[
- `L${phase.toString()}` as keyof MeasurandPerPhaseSampledValueTemplates
- ] ?? powerSampledValueTemplate,
- powerMeasurandValues[
- `L${phase.toString()}` as keyof MeasurandPerPhaseSampledValueTemplates
- ],
- undefined,
- phaseValue as MeterValuePhase
- )
- )
- const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1
- const connectorMaximumPowerPerPhaseRounded = roundTo(
- connectorMaximumPowerPerPhase / unitDivider,
- 2
+ )
+ if (chargingStation.getNumberOfPhases() === 3) {
+ const connectorMaximumPowerPerPhase = Math.round(
+ connectorMaximumAvailablePower / chargingStation.getNumberOfPhases()
)
- const connectorMinimumPowerPerPhaseRounded = roundTo(
- connectorMinimumPowerPerPhase / unitDivider,
- 2
+ const connectorMinimumPowerPerPhase = Math.round(
+ connectorMinimumPower / chargingStation.getNumberOfPhases()
)
- if (
- convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) >
- connectorMaximumPowerPerPhaseRounded ||
- convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) <
- connectorMinimumPowerPerPhaseRounded ||
- debug
- ) {
- logger.error(
- `${chargingStation.logPrefix()} MeterValues measurand ${
- meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ??
- MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- }: phase ${
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
- meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
- }, connector id ${connectorId.toString()}, transaction id ${connector?.transactionId?.toString()}, value: ${connectorMinimumPowerPerPhaseRounded.toString()}/${meterValue.sampledValue[
- sampledValuesPerPhaseIndex
- ].value.toString()}/${connectorMaximumPowerPerPhaseRounded.toString()}`
- )
+ for (let phase = 1; phase <= chargingStation.getNumberOfPhases(); phase++) {
+ const phaseTemplate =
+ powerMeasurand.perPhaseTemplates[
+ `L${phase.toString()}` as keyof MeasurandPerPhaseSampledValueTemplates
+ ]
+ if (phaseTemplate != null) {
+ const phaseValue = `L${phase.toString()}-N` as MeterValuePhase
+ const phasePowerValue =
+ powerMeasurand.values[`L${phase.toString()}` as keyof MeasurandValues]
+ meterValue.sampledValue.push(
+ buildSampledValue(
+ chargingStation.stationInfo.ocppVersion,
+ phaseTemplate,
+ phasePowerValue,
+ undefined,
+ phaseValue
+ )
+ )
+ const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1
+ validatePowerMeasurandValue(
+ chargingStation,
+ connectorId,
+ connector,
+ meterValue.sampledValue[sampledValuesPerPhaseIndex],
+ connectorMaximumPowerPerPhase / unitDivider,
+ connectorMinimumPowerPerPhase / unitDivider,
+ debug
+ )
+ }
}
}
}
// Current.Import measurand
- currentSampledValueTemplate = getSampledValueTemplate(
- chargingStation,
- connectorId,
- MeterValueMeasurand.CURRENT_IMPORT
- )
- if (chargingStation.getNumberOfPhases() === 3) {
- currentPerPhaseSampledValueTemplates = {
- L1: getSampledValueTemplate(
- chargingStation,
- connectorId,
- MeterValueMeasurand.CURRENT_IMPORT,
- MeterValuePhase.L1
- ),
- L2: getSampledValueTemplate(
- chargingStation,
- connectorId,
- MeterValueMeasurand.CURRENT_IMPORT,
- MeterValuePhase.L2
- ),
- L3: getSampledValueTemplate(
- chargingStation,
- connectorId,
- MeterValueMeasurand.CURRENT_IMPORT,
- MeterValuePhase.L3
- ),
- }
- }
- if (currentSampledValueTemplate != null) {
- checkMeasurandPowerDivider(chargingStation, currentSampledValueTemplate.measurand)
- const errMsg = `MeterValues measurand ${
- currentSampledValueTemplate.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
- }: Unknown ${chargingStation.stationInfo.currentOutType} currentOutType in template file ${
- chargingStation.templateFile
- }, cannot calculate ${
- currentSampledValueTemplate.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- } measurand value`
- const currentMeasurandValues: MeasurandValues = {} as MeasurandValues
- connectorMaximumAvailablePower == null &&
- (connectorMaximumAvailablePower =
- chargingStation.getConnectorMaximumAvailablePower(connectorId))
- const connectorMinimumAmperage = currentSampledValueTemplate.minimumValue ?? 0
- let connectorMaximumAmperage: number
- switch (chargingStation.stationInfo.currentOutType) {
- case CurrentType.AC:
- connectorMaximumAmperage = ACElectricUtils.amperagePerPhaseFromPower(
+ const currentMeasurand = buildCurrentMeasurandValue(chargingStation, connectorId)
+ if (currentMeasurand != null) {
+ const connectorMaximumAvailablePower =
+ chargingStation.getConnectorMaximumAvailablePower(connectorId)
+ const connectorMaximumAmperage =
+ chargingStation.stationInfo.currentOutType === CurrentType.AC
+ ? ACElectricUtils.amperagePerPhaseFromPower(
chargingStation.getNumberOfPhases(),
connectorMaximumAvailablePower,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
chargingStation.stationInfo.voltageOut!
)
- if (chargingStation.getNumberOfPhases() === 3) {
- const defaultFluctuatedAmperagePerPhase = isNotEmptyString(
- currentSampledValueTemplate.value
- )
- ? getRandomFloatFluctuatedRounded(
- getLimitFromSampledValueTemplateCustomValue(
- currentSampledValueTemplate.value,
- connectorMaximumAmperage,
- connectorMinimumAmperage,
- {
- fallbackValue: connectorMinimumAmperage,
- limitationEnabled:
- chargingStation.stationInfo.customValueLimitationMeterValues,
- }
- ),
- currentSampledValueTemplate.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT
- )
- : undefined
- const phase1FluctuatedValue = isNotEmptyString(
- currentPerPhaseSampledValueTemplates.L1?.value
- )
- ? getRandomFloatFluctuatedRounded(
- getLimitFromSampledValueTemplateCustomValue(
- currentPerPhaseSampledValueTemplates.L1.value,
- connectorMaximumAmperage,
- connectorMinimumAmperage,
- {
- fallbackValue: connectorMinimumAmperage,
- limitationEnabled:
- chargingStation.stationInfo.customValueLimitationMeterValues,
- }
- ),
- currentPerPhaseSampledValueTemplates.L1.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT
- )
- : undefined
- const phase2FluctuatedValue = isNotEmptyString(
- currentPerPhaseSampledValueTemplates.L2?.value
- )
- ? getRandomFloatFluctuatedRounded(
- getLimitFromSampledValueTemplateCustomValue(
- currentPerPhaseSampledValueTemplates.L2.value,
- connectorMaximumAmperage,
- connectorMinimumAmperage,
- {
- fallbackValue: connectorMinimumAmperage,
- limitationEnabled:
- chargingStation.stationInfo.customValueLimitationMeterValues,
- }
- ),
- currentPerPhaseSampledValueTemplates.L2.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT
- )
- : undefined
- const phase3FluctuatedValue = isNotEmptyString(
- currentPerPhaseSampledValueTemplates.L3?.value
- )
- ? getRandomFloatFluctuatedRounded(
- getLimitFromSampledValueTemplateCustomValue(
- currentPerPhaseSampledValueTemplates.L3.value,
- connectorMaximumAmperage,
- connectorMinimumAmperage,
- {
- fallbackValue: connectorMinimumAmperage,
- limitationEnabled:
- chargingStation.stationInfo.customValueLimitationMeterValues,
- }
- ),
- currentPerPhaseSampledValueTemplates.L3.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT
- )
- : undefined
- currentMeasurandValues.L1 =
- phase1FluctuatedValue ??
- defaultFluctuatedAmperagePerPhase ??
- getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage)
- currentMeasurandValues.L2 =
- phase2FluctuatedValue ??
- defaultFluctuatedAmperagePerPhase ??
- getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage)
- currentMeasurandValues.L3 =
- phase3FluctuatedValue ??
- defaultFluctuatedAmperagePerPhase ??
- getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage)
- } else {
- currentMeasurandValues.L1 = isNotEmptyString(currentSampledValueTemplate.value)
- ? getRandomFloatFluctuatedRounded(
- getLimitFromSampledValueTemplateCustomValue(
- currentSampledValueTemplate.value,
- connectorMaximumAmperage,
- connectorMinimumAmperage,
- {
- fallbackValue: connectorMinimumAmperage,
- limitationEnabled:
- chargingStation.stationInfo.customValueLimitationMeterValues,
- }
- ),
- currentSampledValueTemplate.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT
- )
- : getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage)
- currentMeasurandValues.L2 = 0
- currentMeasurandValues.L3 = 0
- }
- currentMeasurandValues.allPhases = roundTo(
- (currentMeasurandValues.L1 + currentMeasurandValues.L2 + currentMeasurandValues.L3) /
- chargingStation.getNumberOfPhases(),
- 2
- )
- break
- case CurrentType.DC:
- connectorMaximumAmperage = DCElectricUtils.amperage(
+ : DCElectricUtils.amperage(
connectorMaximumAvailablePower,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
chargingStation.stationInfo.voltageOut!
)
- currentMeasurandValues.allPhases = isNotEmptyString(currentSampledValueTemplate.value)
- ? getRandomFloatFluctuatedRounded(
- getLimitFromSampledValueTemplateCustomValue(
- currentSampledValueTemplate.value,
- connectorMaximumAmperage,
- connectorMinimumAmperage,
- {
- fallbackValue: connectorMinimumAmperage,
- limitationEnabled:
- chargingStation.stationInfo.customValueLimitationMeterValues,
- }
- ),
- currentSampledValueTemplate.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT
- )
- : getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage)
- break
- default:
- logger.error(`${chargingStation.logPrefix()} ${errMsg}`)
- throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, RequestCommand.METER_VALUES)
- }
+ const connectorMinimumAmperage = currentMeasurand.template.minimumValue ?? 0
+
meterValue.sampledValue.push(
buildSampledValue(
chargingStation.stationInfo.ocppVersion,
- currentSampledValueTemplate,
- currentMeasurandValues.allPhases
+ currentMeasurand.template,
+ currentMeasurand.values.allPhases
)
)
const sampledValuesIndex = meterValue.sampledValue.length - 1
- if (
- convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) >
- connectorMaximumAmperage ||
- convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) <
- connectorMinimumAmperage ||
+ validateCurrentMeasurandValue(
+ chargingStation,
+ connectorId,
+ connector,
+ meterValue.sampledValue[sampledValuesIndex],
+ connectorMaximumAmperage,
+ connectorMinimumAmperage,
debug
- ) {
- logger.error(
- `${chargingStation.logPrefix()} MeterValues measurand ${
- meterValue.sampledValue[sampledValuesIndex].measurand ??
- MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
- }: connector id ${connectorId.toString()}, transaction id ${connector?.transactionId?.toString()}, value: ${connectorMinimumAmperage.toString()}/${meterValue.sampledValue[
- sampledValuesIndex
- ].value.toString()}/${connectorMaximumAmperage.toString()}`
- )
- }
+ )
for (
let phase = 1;
chargingStation.getNumberOfPhases() === 3 && phase <= chargingStation.getNumberOfPhases();
phase++
) {
- const phaseValue = `L${phase.toString()}`
+ const phaseValue = `L${phase.toString()}` as MeterValuePhase
meterValue.sampledValue.push(
buildSampledValue(
chargingStation.stationInfo.ocppVersion,
- currentPerPhaseSampledValueTemplates[
+ currentMeasurand.perPhaseTemplates[
phaseValue as keyof MeasurandPerPhaseSampledValueTemplates
- ] ?? currentSampledValueTemplate,
- currentMeasurandValues[phaseValue as keyof MeasurandPerPhaseSampledValueTemplates],
+ ] ?? currentMeasurand.template,
+ currentMeasurand.values[phaseValue as keyof MeasurandPerPhaseSampledValueTemplates],
undefined,
- phaseValue as MeterValuePhase
+ phaseValue
)
)
const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1
- if (
- convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) >
- connectorMaximumAmperage ||
- convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) <
- connectorMinimumAmperage ||
+ validateCurrentMeasurandPhaseValue(
+ chargingStation,
+ connectorId,
+ connector,
+ meterValue.sampledValue[sampledValuesPerPhaseIndex],
+ connectorMaximumAmperage,
+ connectorMinimumAmperage,
debug
- ) {
- logger.error(
- `${chargingStation.logPrefix()} MeterValues measurand ${
- meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ??
- MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- }: phase ${
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
- meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
- }, connector id ${connectorId.toString()}, transaction id ${connector?.transactionId?.toString()}, value: ${connectorMinimumAmperage.toString()}/${meterValue.sampledValue[
- sampledValuesPerPhaseIndex
- ].value.toString()}/${connectorMaximumAmperage.toString()}`
- )
- }
+ )
}
}
// Energy.Active.Import.Register measurand (default)
- energySampledValueTemplate = getSampledValueTemplate(chargingStation, connectorId)
- if (energySampledValueTemplate != null) {
- checkMeasurandPowerDivider(chargingStation, energySampledValueTemplate.measurand)
+ const energyMeasurand = buildEnergyMeasurandValue(chargingStation, connectorId, interval)
+ if (energyMeasurand != null) {
+ updateConnectorEnergyValues(connector, energyMeasurand.value)
const unitDivider =
- energySampledValueTemplate.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1
- connectorMaximumAvailablePower == null &&
- (connectorMaximumAvailablePower =
- chargingStation.getConnectorMaximumAvailablePower(connectorId))
+ energyMeasurand.template.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1
+ const energySampledValue = buildSampledValue(
+ chargingStation.stationInfo.ocppVersion,
+ energyMeasurand.template,
+ roundTo(
+ chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId) /
+ unitDivider,
+ 2
+ )
+ )
+ meterValue.sampledValue.push(energySampledValue)
+ const connectorMaximumAvailablePower =
+ chargingStation.getConnectorMaximumAvailablePower(connectorId)
const connectorMaximumEnergyRounded = roundTo(
(connectorMaximumAvailablePower * interval) / (3600 * 1000),
2
)
- const connectorMinimumEnergyRounded = roundTo(
- energySampledValueTemplate.minimumValue ?? 0,
- 2
- )
- const energyValueRounded = isNotEmptyString(energySampledValueTemplate.value)
- ? getRandomFloatFluctuatedRounded(
- getLimitFromSampledValueTemplateCustomValue(
- energySampledValueTemplate.value,
- connectorMaximumEnergyRounded,
- connectorMinimumEnergyRounded,
- {
- fallbackValue: connectorMinimumEnergyRounded,
- limitationEnabled: chargingStation.stationInfo.customValueLimitationMeterValues,
- unitMultiplier: unitDivider,
- }
- ),
- energySampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
- )
- : getRandomFloatRounded(connectorMaximumEnergyRounded, connectorMinimumEnergyRounded)
- if (connector != null) {
- if (
- connector.energyActiveImportRegisterValue != null &&
- connector.energyActiveImportRegisterValue >= 0 &&
- connector.transactionEnergyActiveImportRegisterValue != null &&
- connector.transactionEnergyActiveImportRegisterValue >= 0
- ) {
- connector.energyActiveImportRegisterValue += energyValueRounded
- connector.transactionEnergyActiveImportRegisterValue += energyValueRounded
- } else {
- connector.energyActiveImportRegisterValue = 0
- connector.transactionEnergyActiveImportRegisterValue = 0
- }
- }
- meterValue.sampledValue.push(
- buildSampledValue(
- chargingStation.stationInfo.ocppVersion,
- energySampledValueTemplate,
- roundTo(
- chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId) /
- unitDivider,
- 2
- )
- )
- )
- const sampledValuesIndex = meterValue.sampledValue.length - 1
- if (
- energyValueRounded > connectorMaximumEnergyRounded ||
- energyValueRounded < connectorMinimumEnergyRounded ||
+ const connectorMinimumEnergyRounded = roundTo(energyMeasurand.template.minimumValue ?? 0, 2)
+ validateEnergyMeasurandValue(
+ chargingStation,
+ connectorId,
+ energySampledValue,
+ energyMeasurand.value,
+ connectorMinimumEnergyRounded,
+ connectorMaximumEnergyRounded,
+ interval,
debug
- ) {
- logger.error(
- `${chargingStation.logPrefix()} MeterValues measurand ${
- meterValue.sampledValue[sampledValuesIndex].measurand ??
- MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
- }: connector id ${connectorId.toString()}, transaction id ${connector?.transactionId?.toString()}, value: ${connectorMinimumEnergyRounded.toString()}/${energyValueRounded.toString()}/${connectorMaximumEnergyRounded.toString()}, duration: ${interval.toString()}ms`
- )
- }
+ )
}
return meterValue
+ }
case OCPPVersion.VERSION_20:
case OCPPVersion.VERSION_201: {
const meterValue: OCPP20MeterValue = {
timestamp: new Date(),
}
// SoC measurand
- socSampledValueTemplate = getSampledValueTemplate(
- chargingStation,
- connectorId,
- MeterValueMeasurand.STATE_OF_CHARGE
- )
- if (socSampledValueTemplate != null) {
- const socMaximumValue = 100
- const socMinimumValue = socSampledValueTemplate.minimumValue ?? 0
- const socSampledValueTemplateValue = isNotEmptyString(socSampledValueTemplate.value)
- ? getRandomFloatFluctuatedRounded(
- Number.parseInt(socSampledValueTemplate.value),
- socSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
- )
- : randomInt(socMinimumValue, socMaximumValue + 1)
- meterValue.sampledValue.push(
- buildSampledValue(
- chargingStation.stationInfo.ocppVersion,
- socSampledValueTemplate,
- socSampledValueTemplateValue
- )
+ const socMeasurand = buildSocMeasurandValue(chargingStation, connectorId)
+ if (socMeasurand != null) {
+ const socSampledValue = buildSampledValue(
+ chargingStation.stationInfo.ocppVersion,
+ socMeasurand.template,
+ socMeasurand.value
)
- const sampledValuesIndex = meterValue.sampledValue.length - 1
- if (
- convertToInt(meterValue.sampledValue[sampledValuesIndex].value) > socMaximumValue ||
- convertToInt(meterValue.sampledValue[sampledValuesIndex].value) < socMinimumValue ||
+ meterValue.sampledValue.push(socSampledValue)
+ validateSocMeasurandValue(
+ chargingStation,
+ connectorId,
+ socSampledValue,
+ socMeasurand.template.minimumValue ?? 0,
+ 100,
debug
- ) {
- logger.error(
- `${chargingStation.logPrefix()} MeterValues measurand ${
- meterValue.sampledValue[sampledValuesIndex].measurand ??
- MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
- }: connector id ${connectorId.toString()}, transaction id ${connector?.transactionId?.toString()}, value: ${socMinimumValue.toString()}/${meterValue.sampledValue[
- sampledValuesIndex
- ].value.toString()}/${socMaximumValue.toString()}`
- )
- }
+ )
}
// Voltage measurand
- voltageSampledValueTemplate = getSampledValueTemplate(
- chargingStation,
- connectorId,
- MeterValueMeasurand.VOLTAGE
- )
- if (voltageSampledValueTemplate != null) {
- const voltageSampledValueTemplateValue = isNotEmptyString(voltageSampledValueTemplate.value)
- ? Number.parseInt(voltageSampledValueTemplate.value)
- : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- chargingStation.stationInfo.voltageOut!
- const fluctuationPercent =
- voltageSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
- const voltageMeasurandValue = getRandomFloatFluctuatedRounded(
- voltageSampledValueTemplateValue,
- fluctuationPercent
- )
- if (
- chargingStation.getNumberOfPhases() !== 3 ||
- (chargingStation.getNumberOfPhases() === 3 &&
- chargingStation.stationInfo.mainVoltageMeterValues === true)
- ) {
- meterValue.sampledValue.push(
- buildSampledValue(
- chargingStation.stationInfo.ocppVersion,
- voltageSampledValueTemplate,
- voltageMeasurandValue
- )
- )
- }
+ const voltageMeasurand = buildVoltageMeasurandValue(chargingStation, connectorId)
+ if (voltageMeasurand != null) {
+ addMainVoltageToMeterValue(chargingStation, meterValue, voltageMeasurand)
for (
let phase = 1;
chargingStation.getNumberOfPhases() === 3 && phase <= chargingStation.getNumberOfPhases();
phase++
) {
- const phaseLineToNeutralValue = `L${phase.toString()}-N`
- const voltagePhaseLineToNeutralSampledValueTemplate = getSampledValueTemplate(
+ addPhaseVoltageToMeterValue(
chargingStation,
connectorId,
- MeterValueMeasurand.VOLTAGE,
- phaseLineToNeutralValue as MeterValuePhase
+ meterValue,
+ voltageMeasurand,
+ phase
)
- let voltagePhaseLineToNeutralMeasurandValue: number | undefined
- if (voltagePhaseLineToNeutralSampledValueTemplate != null) {
- const voltagePhaseLineToNeutralSampledValueTemplateValue = isNotEmptyString(
- voltagePhaseLineToNeutralSampledValueTemplate.value
- )
- ? Number.parseInt(voltagePhaseLineToNeutralSampledValueTemplate.value)
- : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- chargingStation.stationInfo.voltageOut!
- const fluctuationPhaseToNeutralPercent =
- voltagePhaseLineToNeutralSampledValueTemplate.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT
- voltagePhaseLineToNeutralMeasurandValue = getRandomFloatFluctuatedRounded(
- voltagePhaseLineToNeutralSampledValueTemplateValue,
- fluctuationPhaseToNeutralPercent
- )
- }
- meterValue.sampledValue.push(
- buildSampledValue(
- chargingStation.stationInfo.ocppVersion,
- voltagePhaseLineToNeutralSampledValueTemplate ?? voltageSampledValueTemplate,
- voltagePhaseLineToNeutralMeasurandValue ?? voltageMeasurandValue,
- undefined,
- phaseLineToNeutralValue as MeterValuePhase
- )
+ addLineToLineVoltageToMeterValue(
+ chargingStation,
+ connectorId,
+ meterValue,
+ voltageMeasurand,
+ phase
)
- if (chargingStation.stationInfo.phaseLineToLineVoltageMeterValues === true) {
- const phaseLineToLineValue = `L${phase.toString()}-L${
- (phase + 1) % chargingStation.getNumberOfPhases() !== 0
- ? ((phase + 1) % chargingStation.getNumberOfPhases()).toString()
- : chargingStation.getNumberOfPhases().toString()
- }`
- const voltagePhaseLineToLineValueRounded = roundTo(
- Math.sqrt(chargingStation.getNumberOfPhases()) *
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- chargingStation.stationInfo.voltageOut!,
- 2
- )
- const voltagePhaseLineToLineSampledValueTemplate = getSampledValueTemplate(
- chargingStation,
- connectorId,
- MeterValueMeasurand.VOLTAGE,
- phaseLineToLineValue as MeterValuePhase
- )
- let voltagePhaseLineToLineMeasurandValue: number | undefined
- if (voltagePhaseLineToLineSampledValueTemplate != null) {
- const voltagePhaseLineToLineSampledValueTemplateValue = isNotEmptyString(
- voltagePhaseLineToLineSampledValueTemplate.value
- )
- ? Number.parseInt(voltagePhaseLineToLineSampledValueTemplate.value)
- : voltagePhaseLineToLineValueRounded
- const fluctuationPhaseLineToLinePercent =
- voltagePhaseLineToLineSampledValueTemplate.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT
- voltagePhaseLineToLineMeasurandValue = getRandomFloatFluctuatedRounded(
- voltagePhaseLineToLineSampledValueTemplateValue,
- fluctuationPhaseLineToLinePercent
- )
- }
- const defaultVoltagePhaseLineToLineMeasurandValue = getRandomFloatFluctuatedRounded(
- voltagePhaseLineToLineValueRounded,
- fluctuationPercent
- )
- meterValue.sampledValue.push(
- buildSampledValue(
- chargingStation.stationInfo.ocppVersion,
- voltagePhaseLineToLineSampledValueTemplate ?? voltageSampledValueTemplate,
- voltagePhaseLineToLineMeasurandValue ?? defaultVoltagePhaseLineToLineMeasurandValue,
- undefined,
- phaseLineToLineValue as MeterValuePhase
- )
- )
- }
}
}
// Energy.Active.Import.Register measurand
- energySampledValueTemplate = getSampledValueTemplate(chargingStation, connectorId)
- if (energySampledValueTemplate != null) {
- checkMeasurandPowerDivider(chargingStation, energySampledValueTemplate.measurand)
+ const energyMeasurand = buildEnergyMeasurandValue(chargingStation, connectorId, interval)
+ if (energyMeasurand != null) {
+ updateConnectorEnergyValues(connector, energyMeasurand.value)
const unitDivider =
- energySampledValueTemplate.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1
- connectorMaximumAvailablePower == null &&
- (connectorMaximumAvailablePower =
- chargingStation.getConnectorMaximumAvailablePower(connectorId))
+ energyMeasurand.template.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1
+ const energySampledValue = buildSampledValue(
+ chargingStation.stationInfo.ocppVersion,
+ energyMeasurand.template,
+ roundTo(
+ chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId) /
+ unitDivider,
+ 2
+ )
+ )
+ meterValue.sampledValue.push(energySampledValue)
+ const connectorMaximumAvailablePower =
+ chargingStation.getConnectorMaximumAvailablePower(connectorId)
const connectorMaximumEnergyRounded = roundTo(
(connectorMaximumAvailablePower * interval) / (3600 * 1000),
2
)
- const connectorMinimumEnergyRounded = roundTo(
- energySampledValueTemplate.minimumValue ?? 0,
- 2
+ const connectorMinimumEnergyRounded = roundTo(energyMeasurand.template.minimumValue ?? 0, 2)
+ validateEnergyMeasurandValue(
+ chargingStation,
+ connectorId,
+ energySampledValue,
+ energyMeasurand.value,
+ connectorMinimumEnergyRounded,
+ connectorMaximumEnergyRounded,
+ interval,
+ debug
)
- const energyValueRounded = isNotEmptyString(energySampledValueTemplate.value)
- ? getRandomFloatFluctuatedRounded(
- getLimitFromSampledValueTemplateCustomValue(
- energySampledValueTemplate.value,
- connectorMaximumEnergyRounded,
- connectorMinimumEnergyRounded,
- {
- fallbackValue: connectorMinimumEnergyRounded,
- limitationEnabled: chargingStation.stationInfo.customValueLimitationMeterValues,
- unitMultiplier: unitDivider,
- }
- ),
- energySampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
- )
- : getRandomFloatRounded(connectorMaximumEnergyRounded, connectorMinimumEnergyRounded)
- if (connector != null) {
- if (
- connector.energyActiveImportRegisterValue != null &&
- connector.energyActiveImportRegisterValue >= 0 &&
- connector.transactionEnergyActiveImportRegisterValue != null &&
- connector.transactionEnergyActiveImportRegisterValue >= 0
- ) {
- connector.energyActiveImportRegisterValue += energyValueRounded
- connector.transactionEnergyActiveImportRegisterValue += energyValueRounded
- } else {
- connector.energyActiveImportRegisterValue = 0
- connector.transactionEnergyActiveImportRegisterValue = 0
- }
- }
- meterValue.sampledValue.push(
- buildSampledValue(
- chargingStation.stationInfo.ocppVersion,
- energySampledValueTemplate,
- roundTo(
- chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId) /
- unitDivider,
- 2
- )
- )
+ }
+ // Power.Active.Import measurand
+ const powerMeasurand = buildPowerMeasurandValue(chargingStation, connectorId)
+ if (powerMeasurand?.values.allPhases != null) {
+ const powerSampledValue = buildSampledValue(
+ chargingStation.stationInfo.ocppVersion,
+ powerMeasurand.template,
+ powerMeasurand.values.allPhases
)
- const sampledValuesIndex = meterValue.sampledValue.length - 1
- if (
- energyValueRounded > connectorMaximumEnergyRounded ||
- energyValueRounded < connectorMinimumEnergyRounded ||
- debug
- ) {
- logger.error(
- `${chargingStation.logPrefix()} MeterValues measurand ${
- meterValue.sampledValue[sampledValuesIndex].measurand ??
- MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
- }: connector id ${connectorId.toString()}, transaction id ${connector?.transactionId?.toString()}, value: ${connectorMinimumEnergyRounded.toString()}/${energyValueRounded.toString()}/${connectorMaximumEnergyRounded.toString()}, duration: ${interval.toString()}ms`
- )
- }
+ meterValue.sampledValue.push(powerSampledValue)
+ }
+ // Current.Import measurand
+ const currentMeasurand = buildCurrentMeasurandValue(chargingStation, connectorId)
+ if (currentMeasurand?.values.allPhases != null) {
+ const currentSampledValue = buildSampledValue(
+ chargingStation.stationInfo.ocppVersion,
+ currentMeasurand.template,
+ currentMeasurand.values.allPhases
+ )
+ meterValue.sampledValue.push(currentSampledValue)
}
return meterValue
}
context?: MeterValueContext,
phase?: MeterValuePhase
): SampledValue {
- const sampledValueContext = context ?? sampledValueTemplate.context
+ const sampledValueMeasurand = sampledValueTemplate.measurand ?? getMeasurandDefault()
+ const sampledValueUnit =
+ sampledValueTemplate.unit ?? getMeasurandDefaultUnit(sampledValueMeasurand)
+ const sampledValueContext =
+ context ?? sampledValueTemplate.context ?? getMeasurandDefaultContext(sampledValueMeasurand)
const sampledValueLocation =
- sampledValueTemplate.location ?? getMeasurandDefaultLocation(sampledValueTemplate.measurand)
+ sampledValueTemplate.location ?? getMeasurandDefaultLocation(sampledValueMeasurand)
const sampledValuePhase = phase ?? sampledValueTemplate.phase
switch (ocppVersion) {
case OCPPVersion.VERSION_16:
// OCPP 1.6 format
return {
- ...(sampledValueTemplate.unit != null && {
- unit: sampledValueTemplate.unit,
- }),
- ...(sampledValueContext != null && { context: sampledValueContext }),
- ...(sampledValueTemplate.measurand != null && {
- measurand: sampledValueTemplate.measurand,
- }),
- ...(sampledValueLocation != null && { location: sampledValueLocation }),
+ context: sampledValueContext,
+ location: sampledValueLocation,
+ measurand: sampledValueMeasurand,
+ unit: sampledValueUnit,
value: value.toString(), // OCPP 1.6 uses string
...(sampledValuePhase != null && { phase: sampledValuePhase }),
} as OCPP16SampledValue
case OCPPVersion.VERSION_201:
// OCPP 2.0 format
return {
- ...(sampledValueContext != null && { context: sampledValueContext }),
- ...(sampledValueTemplate.measurand != null && {
- measurand: sampledValueTemplate.measurand,
- }),
- ...(sampledValueLocation != null && { location: sampledValueLocation }),
+ context: sampledValueContext,
+ location: sampledValueLocation,
+ measurand: sampledValueMeasurand,
+ ...(sampledValueUnit !== undefined && { unitOfMeasure: { unit: sampledValueUnit } }),
value, // OCPP 2.0 uses number
...(sampledValuePhase != null && { phase: sampledValuePhase }),
- ...(sampledValueTemplate.unitOfMeasure != null && {
- unitOfMeasure: sampledValueTemplate.unitOfMeasure,
- }),
} as OCPP20SampledValue
default:
throw new OCPPError(
}
}
+const getMeasurandDefaultContext = (measurandType: MeterValueMeasurand): MeterValueContext => {
+ return MeterValueContext.SAMPLE_PERIODIC
+}
+
const getMeasurandDefaultLocation = (
measurandType: MeterValueMeasurand
): MeterValueLocation | undefined => {
switch (measurandType) {
+ case MeterValueMeasurand.CURRENT_EXPORT:
+ case MeterValueMeasurand.CURRENT_IMPORT:
+ case MeterValueMeasurand.CURRENT_OFFERED:
+ return MeterValueLocation.OUTLET
+
+ case MeterValueMeasurand.ENERGY_ACTIVE_EXPORT_INTERVAL:
+ case MeterValueMeasurand.ENERGY_ACTIVE_EXPORT_REGISTER:
+ case MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_INTERVAL:
+ case MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER:
+ case MeterValueMeasurand.ENERGY_ACTIVE_NET:
+ case MeterValueMeasurand.ENERGY_APPARENT_EXPORT:
+ case MeterValueMeasurand.ENERGY_APPARENT_IMPORT:
+ case MeterValueMeasurand.ENERGY_APPARENT_NET:
+ case MeterValueMeasurand.ENERGY_REACTIVE_EXPORT_INTERVAL:
+ case MeterValueMeasurand.ENERGY_REACTIVE_EXPORT_REGISTER:
+ case MeterValueMeasurand.ENERGY_REACTIVE_IMPORT_INTERVAL:
+ case MeterValueMeasurand.ENERGY_REACTIVE_IMPORT_REGISTER:
+ case MeterValueMeasurand.ENERGY_REACTIVE_NET:
+ return MeterValueLocation.OUTLET
+
+ case MeterValueMeasurand.FAN_RPM:
+ return MeterValueLocation.BODY
+
+ case MeterValueMeasurand.FREQUENCY:
+ return MeterValueLocation.OUTLET
+
+ case MeterValueMeasurand.POWER_ACTIVE_EXPORT:
+ case MeterValueMeasurand.POWER_ACTIVE_IMPORT:
+ case MeterValueMeasurand.POWER_FACTOR:
+ case MeterValueMeasurand.POWER_OFFERED:
+ case MeterValueMeasurand.POWER_REACTIVE_EXPORT:
+ case MeterValueMeasurand.POWER_REACTIVE_IMPORT:
+ return MeterValueLocation.OUTLET
+
case MeterValueMeasurand.STATE_OF_CHARGE:
return MeterValueLocation.EV
+
+ case MeterValueMeasurand.TEMPERATURE:
+ return MeterValueLocation.OUTLET
+
+ case MeterValueMeasurand.VOLTAGE:
+ return MeterValueLocation.OUTLET
+
+ default:
+ return undefined
}
}
-// const getMeasurandDefaultUnit = (
-// measurandType: MeterValueMeasurand
-// ): MeterValueUnit | undefined => {
-// switch (measurandType) {
-// case MeterValueMeasurand.CURRENT_EXPORT:
-// case MeterValueMeasurand.CURRENT_IMPORT:
-// case MeterValueMeasurand.CURRENT_OFFERED:
-// return MeterValueUnit.AMP
-// case MeterValueMeasurand.ENERGY_ACTIVE_EXPORT_REGISTER:
-// case MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER:
-// return MeterValueUnit.WATT_HOUR
-// case MeterValueMeasurand.POWER_ACTIVE_EXPORT:
-// case MeterValueMeasurand.POWER_ACTIVE_IMPORT:
-// case MeterValueMeasurand.POWER_OFFERED:
-// return MeterValueUnit.WATT
-// case MeterValueMeasurand.STATE_OF_CHARGE:
-// return MeterValueUnit.PERCENT
-// case MeterValueMeasurand.VOLTAGE:
-// return MeterValueUnit.VOLT
-// }
-// }
+const getMeasurandDefault = (): MeterValueMeasurand => {
+ return MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
+}
+
+const getMeasurandDefaultUnit = (
+ measurandType: MeterValueMeasurand
+): MeterValueUnit | undefined => {
+ switch (measurandType) {
+ case MeterValueMeasurand.CURRENT_EXPORT:
+ case MeterValueMeasurand.CURRENT_IMPORT:
+ case MeterValueMeasurand.CURRENT_OFFERED:
+ return MeterValueUnit.AMP
+
+ case MeterValueMeasurand.ENERGY_ACTIVE_EXPORT_INTERVAL:
+ case MeterValueMeasurand.ENERGY_ACTIVE_EXPORT_REGISTER:
+ case MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_INTERVAL:
+ case MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER:
+ case MeterValueMeasurand.ENERGY_ACTIVE_NET:
+ case MeterValueMeasurand.ENERGY_APPARENT_EXPORT:
+ case MeterValueMeasurand.ENERGY_APPARENT_IMPORT:
+ case MeterValueMeasurand.ENERGY_APPARENT_NET:
+ return MeterValueUnit.WATT_HOUR
+
+ case MeterValueMeasurand.ENERGY_REACTIVE_EXPORT_INTERVAL:
+ case MeterValueMeasurand.ENERGY_REACTIVE_EXPORT_REGISTER:
+ case MeterValueMeasurand.ENERGY_REACTIVE_IMPORT_INTERVAL:
+ case MeterValueMeasurand.ENERGY_REACTIVE_IMPORT_REGISTER:
+ case MeterValueMeasurand.ENERGY_REACTIVE_NET:
+ return MeterValueUnit.VAR_HOUR
+
+ case MeterValueMeasurand.FAN_RPM:
+ return MeterValueUnit.REVOLUTIONS_PER_MINUTE
+
+ case MeterValueMeasurand.FREQUENCY:
+ return MeterValueUnit.HERTZ
+
+ case MeterValueMeasurand.POWER_ACTIVE_EXPORT:
+ case MeterValueMeasurand.POWER_ACTIVE_IMPORT:
+ case MeterValueMeasurand.POWER_OFFERED:
+ return MeterValueUnit.WATT
+
+ case MeterValueMeasurand.POWER_FACTOR:
+ return undefined
+
+ case MeterValueMeasurand.POWER_REACTIVE_EXPORT:
+ case MeterValueMeasurand.POWER_REACTIVE_IMPORT:
+ return MeterValueUnit.VAR
+
+ case MeterValueMeasurand.STATE_OF_CHARGE:
+ return MeterValueUnit.PERCENT
+
+ case MeterValueMeasurand.TEMPERATURE:
+ return MeterValueUnit.TEMP_CELSIUS
+
+ case MeterValueMeasurand.VOLTAGE:
+ return MeterValueUnit.VOLT
+
+ default:
+ return undefined
+ }
+}
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class OCPPServiceUtils {