StandardParametersKey.WebSocketPingInterval
),
],
+ [
+ StandardParametersKey.MeterValuesAlignedData,
+ buildConfigKey(OCPP20ComponentName.AlignedDataCtrlr, 'Measurands'),
+ ],
+ [
+ StandardParametersKey.ClockAlignedDataInterval,
+ buildConfigKey(
+ OCPP20ComponentName.AlignedDataCtrlr,
+ StandardParametersKey.AlignedDataInterval
+ ),
+ ],
+ [
+ StandardParametersKey.StopTxnSampledData,
+ buildConfigKey(
+ OCPP20ComponentName.SampledDataCtrlr,
+ StandardParametersKey.TxEndedMeasurands
+ ),
+ ],
+ [
+ StandardParametersKey.StopTxnAlignedData,
+ buildConfigKey(
+ OCPP20ComponentName.AlignedDataCtrlr,
+ StandardParametersKey.TxEndedMeasurands
+ ),
+ ],
] as [ConfigurationKeyType, ConfigurationKeyType][]
).map(([from, to]) => [
from,
if (response.status === RequestStartStopStatusEnumType.Accepted) {
const connectorId = chargingStation.getConnectorIdByTransactionId(response.transactionId)
if (connectorId != null && response.transactionId != null) {
- const connectorStatus = chargingStation.getConnectorStatus(connectorId)
- const startedMeterValues =
- connectorStatus != null
- ? OCPP20ServiceUtils.buildTransactionStartedMeterValues(connectorStatus)
- : []
+ const startedMeterValues = OCPP20ServiceUtils.buildTransactionStartedMeterValues(
+ chargingStation,
+ response.transactionId
+ )
OCPP20ServiceUtils.sendTransactionEvent(
chargingStation,
OCPP20TransactionEventEnumType.Started,
OCPP20ComponentName,
type OCPP20EVSEType,
OCPP20IncomingRequestCommand,
- OCPP20MeasurandEnumType,
type OCPP20MeterValue,
OCPP20OptionalVariableName,
OCPP20ReadingContextEnumType,
OCPP20TriggerReasonEnumType,
OCPPVersion,
ReasonCodeEnumType,
+ StandardParametersKey,
type UUIDv4,
} from '../../../types/index.js'
import {
[OCPP20RequestCommand.TRANSACTION_EVENT, 'TransactionEvent'],
]
- static buildTransactionStartedMeterValues (connectorStatus: ConnectorStatus): OCPP20MeterValue[] {
- return OCPP20ServiceUtils.buildEnergyMeterValues(
- connectorStatus,
- OCPP20ReadingContextEnumType.TRANSACTION_BEGIN
- )
+ static buildTransactionStartedMeterValues (
+ chargingStation: ChargingStation,
+ transactionId: number | string
+ ): OCPP20MeterValue[] {
+ try {
+ const measurandsKey = buildConfigKey(
+ OCPP20ComponentName.SampledDataCtrlr,
+ StandardParametersKey.TxStartedMeasurands
+ )
+ const meterValue = buildMeterValue(
+ chargingStation,
+ transactionId,
+ 0,
+ false,
+ measurandsKey,
+ OCPP20ReadingContextEnumType.TRANSACTION_BEGIN
+ ) as OCPP20MeterValue
+ return meterValue.sampledValue.length > 0 ? [meterValue] : []
+ } catch (error) {
+ logger.warn(
+ `${chargingStation.logPrefix()} ${moduleName}.buildTransactionStartedMeterValues: ${(error as Error).message}`
+ )
+ return []
+ }
}
public static async cleanupEndedTransaction (
}
}
- private static buildEnergyMeterValues (
- connectorStatus: ConnectorStatus,
- context: OCPP20ReadingContextEnumType
- ): OCPP20MeterValue[] {
- const meterValues: OCPP20MeterValue[] = []
- const energyValue = connectorStatus.transactionEnergyActiveImportRegisterValue ?? 0
- if (energyValue >= 0) {
- meterValues.push({
- sampledValue: [
- {
- context,
- measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
- value: energyValue,
- },
- ],
- timestamp: new Date(),
- })
- }
- return meterValues
- }
-
private static buildTransactionEndedMeterValues (
- connectorStatus: ConnectorStatus
+ chargingStation: ChargingStation,
+ transactionId: number | string
): OCPP20MeterValue[] {
- return OCPP20ServiceUtils.buildEnergyMeterValues(
- connectorStatus,
- OCPP20ReadingContextEnumType.TRANSACTION_END
- )
+ try {
+ const measurandsKey = buildConfigKey(
+ OCPP20ComponentName.SampledDataCtrlr,
+ StandardParametersKey.TxEndedMeasurands
+ )
+ const meterValue = buildMeterValue(
+ chargingStation,
+ transactionId,
+ 0,
+ false,
+ measurandsKey,
+ OCPP20ReadingContextEnumType.TRANSACTION_END
+ ) as OCPP20MeterValue
+ return meterValue.sampledValue.length > 0 ? [meterValue] : []
+ } catch (error) {
+ logger.warn(
+ `${chargingStation.logPrefix()} ${moduleName}.buildTransactionEndedMeterValues: ${(error as Error).message}`
+ )
+ return []
+ }
}
private static readVariableAsBoolean (
stoppedReason: OCPP20ReasonEnumType,
evseId?: number
): Promise<OCPP20TransactionEventResponse> {
- const endedMeterValues = this.buildTransactionEndedMeterValues(connectorStatus)
+ const endedMeterValues = this.buildTransactionEndedMeterValues(chargingStation, transactionId)
const response = await this.sendTransactionEvent(
chargingStation,
type AuthorizeRequest,
ChargePointErrorCode,
ChargingStationEvents,
+ type ConfigurationKeyType,
type ConnectorStatus,
ConnectorStatusEnum,
CurrentType,
}
OCPP20ServiceUtils.resetTransactionSequenceNumber(chargingStation, connectorId)
}
- const startedMeterValues =
- connectorStatus != null
- ? OCPP20ServiceUtils.buildTransactionStartedMeterValues(connectorStatus)
- : []
+ const startedMeterValues = OCPP20ServiceUtils.buildTransactionStartedMeterValues(
+ chargingStation,
+ transactionId
+ )
const response = await OCPP20ServiceUtils.sendTransactionEvent(
chargingStation,
OCPP20TransactionEventEnumType.Started,
const buildSocMeasurandValue = (
chargingStation: ChargingStation,
connectorId: number,
- evseId?: number
+ evseId?: number,
+ measurandsKey?: ConfigurationKeyType
): null | SingleValueMeasurandData => {
const socSampledValueTemplate = getSampledValueTemplate(
chargingStation,
connectorId,
+ measurandsKey,
MeterValueMeasurand.STATE_OF_CHARGE,
evseId
)
const buildVoltageMeasurandValue = (
chargingStation: ChargingStation,
connectorId: number,
- evseId?: number
+ evseId?: number,
+ measurandsKey?: ConfigurationKeyType
): null | SingleValueMeasurandData => {
const voltageSampledValueTemplate = getSampledValueTemplate(
chargingStation,
connectorId,
+ measurandsKey,
MeterValueMeasurand.VOLTAGE,
evseId
)
value: number,
context?: MeterValueContext,
phase?: MeterValuePhase
- ) => TSampledValue
+ ) => TSampledValue,
+ context?: MeterValueContext
): void => {
const stationInfo = chargingStation.stationInfo
if (stationInfo == null) {
(chargingStation.getNumberOfPhases() === 3 && stationInfo.mainVoltageMeterValues === true)
) {
meterValue.sampledValue.push(
- buildVersionedSampledValue(voltageData.template, voltageData.value)
+ buildVersionedSampledValue(voltageData.template, voltageData.value, context)
)
}
}
value: number,
context?: MeterValueContext,
phase?: MeterValuePhase
- ) => TSampledValue
+ ) => TSampledValue,
+ measurandsKey?: ConfigurationKeyType,
+ context?: MeterValueContext
): void => {
const stationInfo = chargingStation.stationInfo
if (stationInfo == null) {
const voltagePhaseLineToNeutralSampledValueTemplate = getSampledValueTemplate(
chargingStation,
connectorId,
+ measurandsKey,
MeterValueMeasurand.VOLTAGE,
undefined,
phaseLineToNeutralValue
buildVersionedSampledValue(
voltagePhaseLineToNeutralSampledValueTemplate ?? mainVoltageData.template,
voltagePhaseLineToNeutralMeasurandValue ?? mainVoltageData.value,
- undefined,
+ context,
phaseLineToNeutralValue
)
)
value: number,
context?: MeterValueContext,
phase?: MeterValuePhase
- ) => TSampledValue
+ ) => TSampledValue,
+ measurandsKey?: ConfigurationKeyType,
+ context?: MeterValueContext
): void => {
const stationInfo = chargingStation.stationInfo
if (stationInfo?.phaseLineToLineVoltageMeterValues !== true) {
const voltagePhaseLineToLineSampledValueTemplate = getSampledValueTemplate(
chargingStation,
connectorId,
+ measurandsKey,
MeterValueMeasurand.VOLTAGE,
undefined,
phaseLineToLineValue
buildVersionedSampledValue(
voltagePhaseLineToLineSampledValueTemplate ?? mainVoltageData.template,
voltagePhaseLineToLineMeasurandValue ?? voltagePhaseLineToLineValueRounded,
- undefined,
+ context,
phaseLineToLineValue
)
)
chargingStation: ChargingStation,
connectorId: number,
interval: number,
- evseId?: number
+ evseId?: number,
+ measurandsKey?: ConfigurationKeyType
): null | SingleValueMeasurandData => {
- const energyTemplate = getSampledValueTemplate(chargingStation, connectorId, undefined, evseId)
+ const energyTemplate = getSampledValueTemplate(
+ chargingStation,
+ connectorId,
+ measurandsKey,
+ undefined,
+ evseId
+ )
if (energyTemplate == null) {
return null
}
const buildPowerMeasurandValue = (
chargingStation: ChargingStation,
connectorId: number,
- evseId?: number
+ evseId?: number,
+ measurandsKey?: ConfigurationKeyType
): MultiPhaseMeasurandData | null => {
const powerTemplate = getSampledValueTemplate(
chargingStation,
connectorId,
+ measurandsKey,
MeterValueMeasurand.POWER_ACTIVE_IMPORT,
evseId
)
L1: getSampledValueTemplate(
chargingStation,
connectorId,
+ measurandsKey,
MeterValueMeasurand.POWER_ACTIVE_IMPORT,
evseId,
MeterValuePhase.L1_N
L2: getSampledValueTemplate(
chargingStation,
connectorId,
+ measurandsKey,
MeterValueMeasurand.POWER_ACTIVE_IMPORT,
evseId,
MeterValuePhase.L2_N
L3: getSampledValueTemplate(
chargingStation,
connectorId,
+ measurandsKey,
MeterValueMeasurand.POWER_ACTIVE_IMPORT,
evseId,
MeterValuePhase.L3_N
const buildCurrentMeasurandValue = (
chargingStation: ChargingStation,
connectorId: number,
- evseId?: number
+ evseId?: number,
+ measurandsKey?: ConfigurationKeyType
): MultiPhaseMeasurandData | null => {
const currentTemplate = getSampledValueTemplate(
chargingStation,
connectorId,
+ measurandsKey,
MeterValueMeasurand.CURRENT_IMPORT,
evseId
)
L1: getSampledValueTemplate(
chargingStation,
connectorId,
+ measurandsKey,
MeterValueMeasurand.CURRENT_IMPORT,
evseId,
MeterValuePhase.L1
L2: getSampledValueTemplate(
chargingStation,
connectorId,
+ measurandsKey,
MeterValueMeasurand.CURRENT_IMPORT,
evseId,
MeterValuePhase.L2
L3: getSampledValueTemplate(
chargingStation,
connectorId,
+ measurandsKey,
MeterValueMeasurand.CURRENT_IMPORT,
evseId,
MeterValuePhase.L3
chargingStation: ChargingStation,
transactionId: number | string | undefined,
interval: number,
- debug = false
+ debug = false,
+ measurandsKey?: ConfigurationKeyType,
+ context?: MeterValueContext
): MeterValue => {
if (transactionId == null) {
return buildEmptyMeterValue()
return buildSampledValueForOCPP16(sampledValueTemplate, value, context, phase)
}
// SoC measurand
- const socMeasurand = buildSocMeasurandValue(chargingStation, connectorId)
+ const socMeasurand = buildSocMeasurandValue(
+ chargingStation,
+ connectorId,
+ undefined,
+ measurandsKey
+ )
if (socMeasurand != null) {
const socSampledValue = buildVersionedSampledValue(
socMeasurand.template,
)
}
// Voltage measurand
- const voltageMeasurand = buildVoltageMeasurandValue(chargingStation, connectorId)
+ const voltageMeasurand = buildVoltageMeasurandValue(
+ chargingStation,
+ connectorId,
+ undefined,
+ measurandsKey
+ )
if (voltageMeasurand != null) {
addMainVoltageToMeterValue(
chargingStation,
}
}
// Power.Active.Import measurand
- const powerMeasurand = buildPowerMeasurandValue(chargingStation, connectorId)
+ const powerMeasurand = buildPowerMeasurandValue(
+ chargingStation,
+ connectorId,
+ undefined,
+ measurandsKey
+ )
if (powerMeasurand != null) {
const unitDivider = powerMeasurand.template.unit === MeterValueUnit.KILO_WATT ? 1000 : 1
const connectorMaximumAvailablePower =
}
}
// Current.Import measurand
- const currentMeasurand = buildCurrentMeasurandValue(chargingStation, connectorId)
+ const currentMeasurand = buildCurrentMeasurandValue(
+ chargingStation,
+ connectorId,
+ undefined,
+ measurandsKey
+ )
if (currentMeasurand != null) {
const connectorMaximumAvailablePower =
chargingStation.getConnectorMaximumAvailablePower(connectorId)
}
}
// Energy.Active.Import.Register measurand (default)
- const energyMeasurand = buildEnergyMeasurandValue(chargingStation, connectorId, interval)
+ const energyMeasurand = buildEnergyMeasurandValue(
+ chargingStation,
+ connectorId,
+ interval,
+ undefined,
+ measurandsKey
+ )
if (energyMeasurand != null) {
updateConnectorEnergyValues(connectorStatus, energyMeasurand.value)
const unitDivider =
return buildSampledValueForOCPP20(sampledValueTemplate, value, context, phase)
}
// SoC measurand
- const socMeasurand = buildSocMeasurandValue(chargingStation, connectorId, evseId)
+ const socMeasurand = buildSocMeasurandValue(
+ chargingStation,
+ connectorId,
+ evseId,
+ measurandsKey
+ )
if (socMeasurand != null) {
const socSampledValue = buildVersionedSampledValue(
socMeasurand.template,
- socMeasurand.value
+ socMeasurand.value,
+ context
)
meterValue.sampledValue.push(socSampledValue)
validateSocMeasurandValue(
)
}
// Voltage measurand
- const voltageMeasurand = buildVoltageMeasurandValue(chargingStation, connectorId, evseId)
+ const voltageMeasurand = buildVoltageMeasurandValue(
+ chargingStation,
+ connectorId,
+ evseId,
+ measurandsKey
+ )
if (voltageMeasurand != null) {
addMainVoltageToMeterValue(
chargingStation,
meterValue,
voltageMeasurand,
- buildVersionedSampledValue
+ buildVersionedSampledValue,
+ context
)
for (
let phase = 1;
meterValue,
voltageMeasurand,
phase,
- buildVersionedSampledValue
+ buildVersionedSampledValue,
+ measurandsKey,
+ context
)
addLineToLineVoltageToMeterValue(
chargingStation,
meterValue,
voltageMeasurand,
phase,
- buildVersionedSampledValue
+ buildVersionedSampledValue,
+ measurandsKey,
+ context
)
}
}
chargingStation,
connectorId,
interval,
- evseId
+ evseId,
+ measurandsKey
)
if (energyMeasurand != null) {
updateConnectorEnergyValues(connectorStatus, energyMeasurand.value)
chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId) /
unitDivider,
2
- )
+ ),
+ context
)
meterValue.sampledValue.push(energySampledValue)
const connectorMaximumAvailablePower =
)
}
// Power.Active.Import measurand
- const powerMeasurand = buildPowerMeasurandValue(chargingStation, connectorId, evseId)
+ const powerMeasurand = buildPowerMeasurandValue(
+ chargingStation,
+ connectorId,
+ evseId,
+ measurandsKey
+ )
if (powerMeasurand?.values.allPhases != null) {
const powerSampledValue = buildVersionedSampledValue(
powerMeasurand.template,
- powerMeasurand.values.allPhases
+ powerMeasurand.values.allPhases,
+ context
)
meterValue.sampledValue.push(powerSampledValue)
}
// Current.Import measurand
- const currentMeasurand = buildCurrentMeasurandValue(chargingStation, connectorId, evseId)
+ const currentMeasurand = buildCurrentMeasurandValue(
+ chargingStation,
+ connectorId,
+ evseId,
+ measurandsKey
+ )
if (currentMeasurand?.values.allPhases != null) {
const currentSampledValue = buildVersionedSampledValue(
currentMeasurand.template,
- currentMeasurand.values.allPhases
+ currentMeasurand.values.allPhases,
+ context
)
meterValue.sampledValue.push(currentSampledValue)
}
const getSampledValueTemplate = (
chargingStation: ChargingStation,
connectorId: number,
+ measurandsKey: ConfigurationKeyType = StandardParametersKey.MeterValuesSampledData,
measurand: MeterValueMeasurand = MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER,
evseId?: number,
phase?: MeterValuePhase
}
if (
measurand !== MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER &&
- getConfigurationKey(
- chargingStation,
- StandardParametersKey.MeterValuesSampledData
- )?.value?.includes(measurand) === false
+ getConfigurationKey(chargingStation, measurandsKey)?.value?.includes(measurand) === false
) {
logger.debug(
`${chargingStation.logPrefix()} Trying to get MeterValues measurand '${measurand}' ${onPhaseStr}in template on connector id ${connectorId.toString()} not found in sampled data OCPP parameter`
phase != null &&
sampledValueTemplates[index].phase === phase &&
sampledValueTemplates[index].measurand === measurand &&
- getConfigurationKey(
- chargingStation,
- StandardParametersKey.MeterValuesSampledData
- )?.value?.includes(measurand) === true
+ getConfigurationKey(chargingStation, measurandsKey)?.value?.includes(measurand) === true
) {
return sampledValueTemplates[index]
} else if (
phase == null &&
sampledValueTemplates[index].phase == null &&
sampledValueTemplates[index].measurand === measurand &&
- getConfigurationKey(
- chargingStation,
- StandardParametersKey.MeterValuesSampledData
- )?.value?.includes(measurand) === true
+ getConfigurationKey(chargingStation, measurandsKey)?.value?.includes(measurand) === true
) {
return sampledValueTemplates[index]
} else if (
// Check the type
if (typeof value === 'boolean') {
return value
- } else if (
- typeof value === 'string' &&
- (value.trim().toLowerCase() === 'true' || value === '1')
- ) {
- result = true
+ } else if (typeof value === 'string') {
+ const normalized = value.trim().toLowerCase()
+ result = normalized === 'true' || normalized === '1'
} else if (typeof value === 'number' && value === 1) {
result = true
}
OCPP20ChargingProfilePurposeEnumType,
OCPP20IdTokenEnumType,
OCPP20IncomingRequestCommand,
- OCPP20MeasurandEnumType,
- OCPP20ReadingContextEnumType,
OCPP20RequestCommand,
OCPP20TransactionEventEnumType,
OCPP20TriggerReasonEnumType,
]
assert.strictEqual(args[1], OCPP20RequestCommand.TRANSACTION_EVENT)
assert.strictEqual(args[2].eventType, OCPP20TransactionEventEnumType.Started)
- assert.ok(
- Array.isArray(args[2].meterValue) && args[2].meterValue.length > 0,
- 'TransactionEvent(Started) should include non-empty meterValue array'
- )
- assert.strictEqual(
- args[2].meterValue[0].sampledValue[0].context,
- OCPP20ReadingContextEnumType.TRANSACTION_BEGIN
- )
- assert.strictEqual(
- args[2].meterValue[0].sampledValue[0].measurand,
- OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER
- )
})
await it('should NOT call TransactionEvent when response is Rejected', () => {
import {
OCPP20IdTokenEnumType,
OCPP20IncomingRequestCommand,
- OCPP20MeasurandEnumType,
- OCPP20ReadingContextEnumType,
OCPP20ReasonEnumType,
OCPP20RequestCommand,
OCPP20TransactionEventEnumType,
const transactionEvent = args[2]
assert.strictEqual(transactionEvent.eventType, OCPP20TransactionEventEnumType.Ended)
-
- assert.notStrictEqual(transactionEvent.meterValue, undefined)
- if (transactionEvent.meterValue == null) {
- assert.fail('Expected meterValue to be defined')
- }
- assert.strictEqual(transactionEvent.meterValue.length, 1)
-
- const meterValue = transactionEvent.meterValue[0]
- assert.notStrictEqual(meterValue, undefined)
- assert.ok(meterValue.timestamp instanceof Date)
- assert.notStrictEqual(meterValue.sampledValue, undefined)
- assert.strictEqual(meterValue.sampledValue.length, 1)
-
- const sampledValue = meterValue.sampledValue[0]
- assert.strictEqual(sampledValue.value, 12345.67)
- assert.strictEqual(sampledValue.context, OCPP20ReadingContextEnumType.TRANSACTION_END)
- assert.strictEqual(
- sampledValue.measurand,
- OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER
- )
})
})
})
import type { ConnectorStatus } from '../../../../src/types/ConnectorStatus.js'
import type { EmptyObject } from '../../../../src/types/index.js'
+import { addConfigurationKey } from '../../../../src/charging-station/ConfigurationKeyUtils.js'
import {
buildTransactionEvent,
OCPP20ServiceUtils,
OCPP20ComponentName,
OCPP20IdTokenEnumType,
type OCPP20IdTokenType,
- OCPP20MeasurandEnumType,
- OCPP20OperationalStatusEnumType,
- OCPP20ReadingContextEnumType,
OCPP20ReasonEnumType,
OCPP20RequestCommand,
OCPP20RequiredVariableName,
assert.strictEqual(txEvents.length, 2)
const endedPayload = txEvents[1].payload
- assert.notStrictEqual(endedPayload.meterValue, undefined)
- const meterValues = endedPayload.meterValue as {
- sampledValue: { context: string; measurand: string; value: number }[]
- timestamp: Date
- }[]
- assert.strictEqual(meterValues.length, 1)
- assert.strictEqual(meterValues[0].sampledValue.length, 1)
- assert.strictEqual(
- meterValues[0].sampledValue[0].measurand,
- OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER
- )
- assert.strictEqual(meterValues[0].sampledValue[0].value, 1500)
- assert.strictEqual(
- meterValues[0].sampledValue[0].context,
- OCPP20ReadingContextEnumType.TRANSACTION_END
- )
+ assert.strictEqual(endedPayload.stoppedReason, OCPP20ReasonEnumType.DeAuthorized)
})
await it('should reset connector status after deauthorization', async () => {
})
await describe('buildTransactionStartedMeterValues', async () => {
- await it('should build meter value with Transaction.Begin context and energy register', () => {
- const connectorStatus = {
- availability: OCPP20OperationalStatusEnumType.Operative,
- MeterValues: [],
- transactionEnergyActiveImportRegisterValue: 1234,
- } as unknown as ConnectorStatus
+ await it('should build meter values using TxStartedMeasurands config key', () => {
+ const { station } = createMockChargingStation({
+ baseName: TEST_CHARGING_STATION_BASE_NAME,
+ connectorsCount: 3,
+ evseConfiguration: { evsesCount: 3 },
+ heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL,
+ ocppRequestService: {
+ requestHandler: async () => Promise.resolve({} as EmptyObject),
+ },
+ stationInfo: {
+ ocppStrictCompliance: true,
+ ocppVersion: OCPPVersion.VERSION_201,
+ },
+ websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL,
+ })
+ resetLimits(station)
- const result = OCPP20ServiceUtils.buildTransactionStartedMeterValues(connectorStatus)
+ // Set up energy MeterValues template on EVSE
+ const evseStatus = station.getEvseStatus(1)
+ if (evseStatus != null) {
+ evseStatus.MeterValues = [{ unit: 'Wh' }] as unknown as ConnectorStatus['MeterValues']
+ }
- assert.strictEqual(result.length, 1)
- assert.strictEqual(result[0].sampledValue.length, 1)
- assert.strictEqual(
- result[0].sampledValue[0].context,
- OCPP20ReadingContextEnumType.TRANSACTION_BEGIN
- )
- assert.strictEqual(
- result[0].sampledValue[0].measurand,
- OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER
- )
- assert.strictEqual(result[0].sampledValue[0].value, 1234)
- assert.ok(result[0].timestamp instanceof Date)
- })
+ // Set up transaction
+ const transactionId = generateUUID()
+ const connectorStatus = station.getConnectorStatus(1)
+ if (connectorStatus != null) {
+ connectorStatus.transactionId = transactionId
+ connectorStatus.transactionStarted = true
+ connectorStatus.transactionEnergyActiveImportRegisterValue = 1234
+ }
- await it('should include meter value with 0 energy when register value is undefined (zero is a valid begin reading)', () => {
- const connectorStatus = {
- availability: OCPP20OperationalStatusEnumType.Operative,
- MeterValues: [],
- } as unknown as ConnectorStatus
+ // Add TxStartedMeasurands config key with energy measurand
+ addConfigurationKey(
+ station,
+ `${OCPP20ComponentName.SampledDataCtrlr}.${OCPP20RequiredVariableName.TxStartedMeasurands}`,
+ 'Energy.Active.Import.Register',
+ undefined,
+ { save: false }
+ )
- const result = OCPP20ServiceUtils.buildTransactionStartedMeterValues(connectorStatus)
+ const result = OCPP20ServiceUtils.buildTransactionStartedMeterValues(station, transactionId)
assert.strictEqual(result.length, 1)
- assert.strictEqual(result[0].sampledValue[0].value, 0)
+ assert.ok(result[0].sampledValue.length >= 1)
+ assert.ok(result[0].timestamp instanceof Date)
})
- await it('should return empty array when energy register value is negative', () => {
- const connectorStatus = {
- availability: OCPP20OperationalStatusEnumType.Operative,
- MeterValues: [],
- transactionEnergyActiveImportRegisterValue: -1,
- } as unknown as ConnectorStatus
+ await it('should return empty array when no transaction found for transactionId', () => {
+ const { station } = createMockChargingStation({
+ baseName: TEST_CHARGING_STATION_BASE_NAME,
+ connectorsCount: 3,
+ evseConfiguration: { evsesCount: 3 },
+ heartbeatInterval: Constants.DEFAULT_HEARTBEAT_INTERVAL,
+ ocppRequestService: {
+ requestHandler: async () => Promise.resolve({} as EmptyObject),
+ },
+ stationInfo: {
+ ocppStrictCompliance: true,
+ ocppVersion: OCPPVersion.VERSION_201,
+ },
+ websocketPingInterval: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL,
+ })
+ resetLimits(station)
- const result = OCPP20ServiceUtils.buildTransactionStartedMeterValues(connectorStatus)
+ // No transaction set up - transactionId won't resolve
+ const result = OCPP20ServiceUtils.buildTransactionStartedMeterValues(
+ station,
+ 'non-existent-tx'
+ )
+ // buildMeterValue returns empty meter value when transactionId can't be resolved
assert.strictEqual(result.length, 0)
})
})
// Check the type
if (typeof value === 'boolean') {
return value
- } else if (
- typeof value === 'string' &&
- (value.trim().toLowerCase() === 'true' || value === '1')
- ) {
- result = true
+ } else if (typeof value === 'string') {
+ const normalized = value.trim().toLowerCase()
+ result = normalized === 'true' || normalized === '1'
} else if (typeof value === 'number' && value === 1) {
result = true
}