type OCPP16BootNotificationRequest,
type OCPP16MeterValue,
type OCPP16SampledValue,
- OCPPVersion,
RequestCommand,
type SampledValueTemplate,
} from '../../../types/index.js'
buildEmptyMeterValue,
buildEnergyMeasurandValue,
buildPowerMeasurandValue,
- buildSampledValue,
buildSocMeasurandValue,
buildVoltageMeasurandValue,
+ resolveSampledValueFields,
updateConnectorEnergyValues,
validateCurrentMeasurandPhaseValue,
validateCurrentMeasurandValue,
context?: MeterValueContext,
phase?: MeterValuePhase
): OCPP16SampledValue {
- return buildSampledValue(
- OCPPVersion.VERSION_16,
- sampledValueTemplate,
- value,
- context,
- phase
- ) as OCPP16SampledValue
+ const fields = resolveSampledValueFields(sampledValueTemplate, value, context, phase)
+ return {
+ context: fields.context,
+ location: fields.location,
+ measurand: fields.measurand,
+ unit: fields.unit,
+ value: fields.value.toString(),
+ ...(fields.phase != null && { phase: fields.phase }),
+ } as OCPP16SampledValue
}
OCPP16MeterValueMeasurand,
OCPP16MeterValueUnit,
OCPP16RequestCommand,
- type OCPP16SampledValue,
OCPP16StandardParametersKey,
type OCPP16StatusNotificationRequest,
OCPP16StopTransactionReason,
import {
buildEmptyMeterValue,
buildMeterValue,
- buildSampledValue,
createPayloadConfigs,
getSampledValueTemplate,
PayloadValidatorOptions,
} from '../OCPPServiceUtils.js'
import { OCPP16Constants } from './OCPP16Constants.js'
+import { buildOCPP16SampledValue } from './OCPP16RequestBuilders.js'
const moduleName = 'OCPP16ServiceUtils'
const unitDivider =
sampledValueTemplate.unit === OCPP16MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1
meterValue.sampledValue.push(
- buildSampledValue(
- chargingStation.stationInfo?.ocppVersion,
+ buildOCPP16SampledValue(
sampledValueTemplate,
roundTo((meterStart ?? 0) / unitDivider, 4),
OCPP16MeterValueContext.TRANSACTION_BEGIN
- ) as OCPP16SampledValue
+ )
)
}
return meterValue
const unitDivider = sampledValueTemplate.unit === OCPP16MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1
const meterValue = buildEmptyMeterValue() as OCPP16MeterValue
meterValue.sampledValue.push(
- buildSampledValue(
- OCPPVersion.VERSION_16,
+ buildOCPP16SampledValue(
sampledValueTemplate,
roundTo((meterStop ?? 0) / unitDivider, 4),
OCPP16MeterValueContext.TRANSACTION_END
- ) as OCPP16SampledValue
+ )
)
return meterValue
}
import type { ChargingStation } from '../../../charging-station/index.js'
+import type { StopTransactionReason } from '../../../types/index.js'
import { OCPPError } from '../../../exception/index.js'
import {
type MeterValueContext,
type MeterValuePhase,
MeterValueUnit,
+ OCPP16StopTransactionReason,
type OCPP20BootNotificationRequest,
type OCPP20MeterValue,
+ OCPP20ReasonEnumType,
type OCPP20SampledValue,
- OCPPVersion,
+ OCPP20TriggerReasonEnumType,
RequestCommand,
type SampledValueTemplate,
} from '../../../types/index.js'
buildEmptyMeterValue,
buildEnergyMeasurandValue,
buildPowerMeasurandValue,
- buildSampledValue,
buildSocMeasurandValue,
buildVoltageMeasurandValue,
+ resolveSampledValueFields,
updateConnectorEnergyValues,
validateEnergyMeasurandValue,
validateSocMeasurandValue,
context?: MeterValueContext,
phase?: MeterValuePhase
): OCPP20SampledValue {
- return buildSampledValue(
- OCPPVersion.VERSION_20,
- sampledValueTemplate,
- value,
- context,
- phase
- ) as OCPP20SampledValue
+ const fields = resolveSampledValueFields(sampledValueTemplate, value, context, phase)
+ return {
+ context: fields.context,
+ location: fields.location,
+ measurand: fields.measurand,
+ ...(fields.unit !== undefined && { unitOfMeasure: { unit: fields.unit } }),
+ value: fields.value,
+ ...(fields.phase != null && { phase: fields.phase }),
+ } as OCPP20SampledValue
+}
+
+export const mapStopReasonToOCPP20 = (
+ reason?: StopTransactionReason
+): {
+ stoppedReason: OCPP20ReasonEnumType
+ triggerReason: OCPP20TriggerReasonEnumType
+} => {
+ switch (reason) {
+ case OCPP16StopTransactionReason.DE_AUTHORIZED:
+ case OCPP20ReasonEnumType.DeAuthorized:
+ return {
+ stoppedReason: OCPP20ReasonEnumType.DeAuthorized,
+ triggerReason: OCPP20TriggerReasonEnumType.Deauthorized,
+ }
+ case OCPP16StopTransactionReason.EMERGENCY_STOP:
+ case OCPP20ReasonEnumType.EmergencyStop:
+ return {
+ stoppedReason: OCPP20ReasonEnumType.EmergencyStop,
+ triggerReason: OCPP20TriggerReasonEnumType.AbnormalCondition,
+ }
+ case OCPP16StopTransactionReason.EV_DISCONNECTED:
+ case OCPP20ReasonEnumType.EVDisconnected:
+ return {
+ stoppedReason: OCPP20ReasonEnumType.EVDisconnected,
+ triggerReason: OCPP20TriggerReasonEnumType.EVDeparted,
+ }
+ case OCPP16StopTransactionReason.HARD_RESET:
+ case OCPP16StopTransactionReason.REBOOT:
+ case OCPP16StopTransactionReason.SOFT_RESET:
+ case OCPP20ReasonEnumType.ImmediateReset:
+ case OCPP20ReasonEnumType.Reboot:
+ return {
+ stoppedReason: OCPP20ReasonEnumType.ImmediateReset,
+ triggerReason: OCPP20TriggerReasonEnumType.ResetCommand,
+ }
+ case OCPP16StopTransactionReason.OTHER:
+ case OCPP20ReasonEnumType.Other:
+ return {
+ stoppedReason: OCPP20ReasonEnumType.Other,
+ triggerReason: OCPP20TriggerReasonEnumType.AbnormalCondition,
+ }
+ case OCPP16StopTransactionReason.POWER_LOSS:
+ case OCPP20ReasonEnumType.PowerLoss:
+ return {
+ stoppedReason: OCPP20ReasonEnumType.PowerLoss,
+ triggerReason: OCPP20TriggerReasonEnumType.AbnormalCondition,
+ }
+ case OCPP16StopTransactionReason.REMOTE:
+ case OCPP20ReasonEnumType.Remote:
+ return {
+ stoppedReason: OCPP20ReasonEnumType.Remote,
+ triggerReason: OCPP20TriggerReasonEnumType.RemoteStop,
+ }
+ case OCPP20ReasonEnumType.TimeLimitReached:
+ return {
+ stoppedReason: OCPP20ReasonEnumType.TimeLimitReached,
+ triggerReason: OCPP20TriggerReasonEnumType.TimeLimitReached,
+ }
+ case OCPP16StopTransactionReason.LOCAL:
+ case OCPP20ReasonEnumType.Local:
+ case undefined:
+ default:
+ return {
+ stoppedReason: OCPP20ReasonEnumType.Local,
+ triggerReason: OCPP20TriggerReasonEnumType.StopAuthorized,
+ }
+ }
}
import {
buildMeterValue,
createPayloadConfigs,
- mapStopReasonToOCPP20,
PayloadValidatorOptions,
} from '../OCPPServiceUtils.js'
+import { mapStopReasonToOCPP20 } from './OCPP20RequestBuilders.js'
import { OCPP20VariableManager } from './OCPP20VariableManager.js'
const moduleName = 'OCPP20ServiceUtils'
} from '../../types/index.js'
import { logger, truncateId } from '../../utils/index.js'
import { OCPP16ServiceUtils } from './1.6/OCPP16ServiceUtils.js'
+import { mapStopReasonToOCPP20 } from './2.0/OCPP20RequestBuilders.js'
import { OCPP20ServiceUtils } from './2.0/OCPP20ServiceUtils.js'
import {
AuthContext,
IdentifierType,
OCPPAuthServiceFactory,
} from './auth/index.js'
-import { mapStopReasonToOCPP20 } from './OCPPServiceUtils.js'
/**
* Starts a transaction on a specific connector using the appropriate OCPP version handler.
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
-import type { BootReasonEnumType, StopTransactionReason } from '../../types/index.js'
+import type { BootReasonEnumType } from '../../types/index.js'
import { type ChargingStation, getConfigurationKey } from '../../charging-station/index.js'
import { BaseError, OCPPError } from '../../exception/index.js'
MeterValueMeasurand,
MeterValuePhase,
MeterValueUnit,
- type OCPP16SampledValue,
- OCPP16StopTransactionReason,
- OCPP20ReasonEnumType,
- type OCPP20SampledValue,
- OCPP20TriggerReasonEnumType,
OCPPVersion,
RequestCommand,
type SampledValue,
}
}
-/**
- * Maps an OCPP 1.6 or generic stop transaction reason to OCPP 2.0 stopped and trigger reasons.
- * @param reason - Stop transaction reason to map
- * @returns Object containing the OCPP 2.0 stoppedReason and triggerReason
- */
-export const mapStopReasonToOCPP20 = (
- reason?: StopTransactionReason
-): {
- stoppedReason: OCPP20ReasonEnumType
- triggerReason: OCPP20TriggerReasonEnumType
-} => {
- switch (reason) {
- case OCPP16StopTransactionReason.DE_AUTHORIZED:
- case OCPP20ReasonEnumType.DeAuthorized:
- return {
- stoppedReason: OCPP20ReasonEnumType.DeAuthorized,
- triggerReason: OCPP20TriggerReasonEnumType.Deauthorized,
- }
- case OCPP16StopTransactionReason.EMERGENCY_STOP:
- case OCPP20ReasonEnumType.EmergencyStop:
- return {
- stoppedReason: OCPP20ReasonEnumType.EmergencyStop,
- triggerReason: OCPP20TriggerReasonEnumType.AbnormalCondition,
- }
- case OCPP16StopTransactionReason.EV_DISCONNECTED:
- case OCPP20ReasonEnumType.EVDisconnected:
- return {
- stoppedReason: OCPP20ReasonEnumType.EVDisconnected,
- triggerReason: OCPP20TriggerReasonEnumType.EVDeparted,
- }
- case OCPP16StopTransactionReason.HARD_RESET:
- case OCPP16StopTransactionReason.REBOOT:
- case OCPP16StopTransactionReason.SOFT_RESET:
- case OCPP20ReasonEnumType.ImmediateReset:
- case OCPP20ReasonEnumType.Reboot:
- return {
- stoppedReason: OCPP20ReasonEnumType.ImmediateReset,
- triggerReason: OCPP20TriggerReasonEnumType.ResetCommand,
- }
- case OCPP16StopTransactionReason.OTHER:
- case OCPP20ReasonEnumType.Other:
- return {
- stoppedReason: OCPP20ReasonEnumType.Other,
- triggerReason: OCPP20TriggerReasonEnumType.AbnormalCondition,
- }
- case OCPP16StopTransactionReason.POWER_LOSS:
- case OCPP20ReasonEnumType.PowerLoss:
- return {
- stoppedReason: OCPP20ReasonEnumType.PowerLoss,
- triggerReason: OCPP20TriggerReasonEnumType.AbnormalCondition,
- }
- case OCPP16StopTransactionReason.REMOTE:
- case OCPP20ReasonEnumType.Remote:
- return {
- stoppedReason: OCPP20ReasonEnumType.Remote,
- triggerReason: OCPP20TriggerReasonEnumType.RemoteStop,
- }
- case OCPP20ReasonEnumType.TimeLimitReached:
- return {
- stoppedReason: OCPP20ReasonEnumType.TimeLimitReached,
- triggerReason: OCPP20TriggerReasonEnumType.TimeLimitReached,
- }
- case OCPP16StopTransactionReason.LOCAL:
- case OCPP20ReasonEnumType.Local:
- case undefined:
- default:
- return {
- stoppedReason: OCPP20ReasonEnumType.Local,
- triggerReason: OCPP20TriggerReasonEnumType.StopAuthorized,
- }
- }
-}
-
/**
* Converts Ajv validation errors to the corresponding OCPP error type.
* @param errors - Array of Ajv validation error objects
}
/**
- * Builds a sampled value object according to the specified OCPP version.
- * @param ocppVersion - The OCPP version to use for formatting the sampled value
+ * Resolves the common sampled value fields from a template and optional overrides.
* @param sampledValueTemplate - Template containing measurement configuration and metadata
* @param value - The measured numeric value to be included in the sampled value
* @param context - Optional context specifying when the measurement was taken (e.g., Sample.Periodic)
* @param phase - Optional phase information for multi-phase electrical measurements
- * @returns A sampled value object formatted according to the specified OCPP version
+ * @returns An object containing the resolved sampled value fields
*/
-export function buildSampledValue (
- ocppVersion: OCPPVersion | undefined,
+export const resolveSampledValueFields = (
sampledValueTemplate: SampledValueTemplate,
value: number,
context?: MeterValueContext,
phase?: MeterValuePhase
-): SampledValue {
- const sampledValueMeasurand = sampledValueTemplate.measurand ?? getMeasurandDefault()
- const sampledValueUnit =
- sampledValueTemplate.unit ?? getMeasurandDefaultUnit(sampledValueMeasurand)
- const sampledValueContext =
- context ?? sampledValueTemplate.context ?? getMeasurandDefaultContext(sampledValueMeasurand)
- const sampledValueLocation =
- sampledValueTemplate.location ?? getMeasurandDefaultLocation(sampledValueMeasurand)
- const sampledValuePhase = phase ?? sampledValueTemplate.phase
-
- switch (ocppVersion) {
- case OCPPVersion.VERSION_16:
- // OCPP 1.6 format
- return {
- 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_20:
- case OCPPVersion.VERSION_201:
- // OCPP 2.0 format
- return {
- context: sampledValueContext,
- location: sampledValueLocation,
- measurand: sampledValueMeasurand,
- ...(sampledValueUnit !== undefined && { unitOfMeasure: { unit: sampledValueUnit } }),
- value, // OCPP 2.0 uses number
- ...(sampledValuePhase != null && { phase: sampledValuePhase }),
- } as OCPP20SampledValue
- default:
- throw new OCPPError(
- ErrorType.INTERNAL_ERROR,
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
- `Cannot build sampledValue: OCPP version ${ocppVersion} not supported`,
- RequestCommand.METER_VALUES
- )
+): {
+ context: MeterValueContext
+ location: MeterValueLocation | undefined
+ measurand: MeterValueMeasurand
+ phase: MeterValuePhase | undefined
+ unit: MeterValueUnit | undefined
+ value: number
+} => {
+ const sampledValueMeasurand =
+ (sampledValueTemplate.measurand as MeterValueMeasurand | undefined) ?? getMeasurandDefault()
+ return {
+ context:
+ context ??
+ (sampledValueTemplate.context as MeterValueContext | undefined) ??
+ getMeasurandDefaultContext(sampledValueMeasurand),
+ location:
+ (sampledValueTemplate.location as MeterValueLocation | undefined) ??
+ getMeasurandDefaultLocation(sampledValueMeasurand),
+ measurand: sampledValueMeasurand,
+ phase: phase ?? (sampledValueTemplate.phase as MeterValuePhase | undefined),
+ unit:
+ (sampledValueTemplate.unit as MeterValueUnit | undefined) ??
+ getMeasurandDefaultUnit(sampledValueMeasurand),
+ value,
}
}
* - buildBootNotificationRequest — builds version-specific boot notification payloads
* - convertDateToISOString — recursively converts Date objects to ISO strings in-place
* - isConnectorIdValid — validates connector ID ranges
- * - mapStopReasonToOCPP20 — maps OCPP 1.6 stop reasons to OCPP 2.0 equivalents
+ * - mapStopReasonToOCPP20 — maps OCPP 1.6 stop reasons to OCPP 2.0 equivalents (from OCPP20RequestBuilders)
*/
import type { ErrorObject } from 'ajv'
import type { ChargingStation } from '../../../src/charging-station/index.js'
+import { mapStopReasonToOCPP20 } from '../../../src/charging-station/ocpp/2.0/OCPP20RequestBuilders.js'
import {
ajvErrorsToErrorType,
buildBootNotificationRequest,
convertDateToISOString,
isConnectorIdValid,
- mapStopReasonToOCPP20,
} from '../../../src/charging-station/ocpp/OCPPServiceUtils.js'
import {
BootReasonEnumType,