"http-status-codes": "^2.3.0",
"just-merge": "^3.2.0",
"logform": "^2.6.0",
- "mnemonist": "^0.39.5",
+ "mnemonist": "^0.39.6",
"mongodb": "^6.3.0",
"poolifier": "^3.0.9",
"tar": "^6.2.0",
specifier: ^2.6.0
version: 2.6.0
mnemonist:
- specifier: ^0.39.5
- version: 0.39.5
+ specifier: ^0.39.6
+ version: 0.39.6
mongodb:
specifier: ^6.3.0
version: 6.3.0
'@octokit/graphql': 7.0.2
'@octokit/request': 8.1.6
'@octokit/request-error': 5.0.1
- '@octokit/types': 12.3.0
+ '@octokit/types': 12.4.0
before-after-hook: 2.2.3
universal-user-agent: 6.0.1
dev: true
resolution: {integrity: sha512-DWPLtr1Kz3tv8L0UvXTDP1fNwM0S+z6EJpRcvH66orY6Eld4XBMCSYsaWp4xIm61jTWxK68BrR7ibO+vSDnZqw==}
engines: {node: '>= 18'}
dependencies:
- '@octokit/types': 12.3.0
+ '@octokit/types': 12.4.0
universal-user-agent: 6.0.1
dev: true
engines: {node: '>= 18'}
dependencies:
'@octokit/request': 8.1.6
- '@octokit/types': 12.3.0
+ '@octokit/types': 12.4.0
universal-user-agent: 6.0.1
dev: true
resolution: {integrity: sha512-6G+ywGClliGQwRsjvqVYpklIfa7oRPA0vyhPQG/1Feh+B+wU0vGH1JiJ5T25d3g1JZYBHzR2qefLi9x8Gt+cpw==}
dev: true
- /@octokit/plugin-paginate-rest@9.1.4(@octokit/core@5.0.2):
- resolution: {integrity: sha512-MvZx4WvfhBnt7PtH5XE7HORsO7bBk4er1FgRIUr1qJ89NR2I6bWjGyKsxk8z42FPQ34hFQm0Baanh4gzdZR4gQ==}
+ /@octokit/plugin-paginate-rest@9.1.5(@octokit/core@5.0.2):
+ resolution: {integrity: sha512-WKTQXxK+bu49qzwv4qKbMMRXej1DU2gq017euWyKVudA6MldaSSQuxtz+vGbhxV4CjxpUxjZu6rM2wfc1FiWVg==}
engines: {node: '>= 18'}
peerDependencies:
'@octokit/core': '>=5'
dependencies:
'@octokit/core': 5.0.2
- '@octokit/types': 12.3.0
+ '@octokit/types': 12.4.0
dev: true
/@octokit/plugin-request-log@4.0.0(@octokit/core@5.0.2):
'@octokit/core': '>=5'
dependencies:
'@octokit/core': 5.0.2
- '@octokit/types': 12.3.0
+ '@octokit/types': 12.4.0
dev: true
/@octokit/request-error@5.0.1:
resolution: {integrity: sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==}
engines: {node: '>= 18'}
dependencies:
- '@octokit/types': 12.3.0
+ '@octokit/types': 12.4.0
deprecation: 2.3.1
once: 1.4.0
dev: true
dependencies:
'@octokit/endpoint': 9.0.4
'@octokit/request-error': 5.0.1
- '@octokit/types': 12.3.0
+ '@octokit/types': 12.4.0
universal-user-agent: 6.0.1
dev: true
engines: {node: '>= 18'}
dependencies:
'@octokit/core': 5.0.2
- '@octokit/plugin-paginate-rest': 9.1.4(@octokit/core@5.0.2)
+ '@octokit/plugin-paginate-rest': 9.1.5(@octokit/core@5.0.2)
'@octokit/plugin-request-log': 4.0.0(@octokit/core@5.0.2)
'@octokit/plugin-rest-endpoint-methods': 10.2.0(@octokit/core@5.0.2)
dev: true
- /@octokit/types@12.3.0:
- resolution: {integrity: sha512-nJ8X2HRr234q3w/FcovDlA+ttUU4m1eJAourvfUUtwAWeqL8AsyRqfnLvVnYn3NFbUnsmzQCzLNdFerPwdmcDQ==}
+ /@octokit/types@12.4.0:
+ resolution: {integrity: sha512-FLWs/AvZllw/AGVs+nJ+ELCDZZJk+kY0zMen118xhL2zD0s1etIUHm1odgjP7epxYU1ln7SZxEUWYop5bhsdgQ==}
dependencies:
'@octokit/openapi-types': 19.1.0
dev: true
resolution: {integrity: sha512-VoAYUqmPRmzKbbqRejjqceGFp3VF81Qe8XXFGU0UXLxB7Mf4GGvyGq5Qn3k4AiQgDEV6WzobqlPOd+j0+m6IrA==}
dev: true
- /mnemonist@0.39.5:
- resolution: {integrity: sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==}
+ /mnemonist@0.39.6:
+ resolution: {integrity: sha512-A/0v5Z59y63US00cRSLiloEIw3t5G+MiKz4BhX21FI+YBJXBOGW0ohFxTxO08dsOYlzxo87T7vGfZKYp2bcAWA==}
dependencies:
obliterator: 2.0.4
dev: false
OCPP16IncomingRequestService,
OCPP16RequestService,
OCPP16ResponseService,
- OCPP16ServiceUtils,
OCPP20IncomingRequestService,
OCPP20RequestService,
OCPP20ResponseService,
type OCPPIncomingRequestService,
type OCPPRequestService,
+ buildMeterValue,
buildStatusNotificationRequest,
+ buildTransactionEndMeterValue,
getMessageTypeString,
sendAndSetConnectorStatus,
} from './ocpp';
}
if (interval > 0) {
this.getConnectorStatus(connectorId)!.transactionSetInterval = setInterval(() => {
- // FIXME: Implement OCPP version agnostic helpers
- const meterValue: MeterValue = OCPP16ServiceUtils.buildMeterValue(
+ const meterValue: MeterValue = buildMeterValue(
this,
connectorId,
this.getConnectorStatus(connectorId)!.transactionId!,
this.stationInfo?.ocppStrictCompliance === true &&
this.stationInfo?.outOfOrderEndMeterValues === false
) {
- // FIXME: Implement OCPP version agnostic helpers
- const transactionEndMeterValue = OCPP16ServiceUtils.buildTransactionEndMeterValue(
+ const transactionEndMeterValue = buildTransactionEndMeterValue(
this,
connectorId,
this.getEnergyActiveImportRegisterByTransactionId(transactionId!),
import { Constants, convertToInt, isEmptyObject, isNullOrUndefined, logger } from '../../utils';
import type { ChargingStation } from '../ChargingStation';
import { getConfigurationKey } from '../ConfigurationKeyUtils';
-import { OCPP16ServiceUtils } from '../ocpp';
+import { buildMeterValue } from '../ocpp';
const moduleName = 'ChargingStationWorkerBroadcastChannel';
RequestCommand.METER_VALUES,
{
meterValue: [
- // FIXME: Implement OCPP version agnostic helpers
- OCPP16ServiceUtils.buildMeterValue(
+ buildMeterValue(
this.chargingStation,
requestPayload!.connectorId!,
this.chargingStation.getConnectorStatus(requestPayload!.connectorId!)!
import {
type ChangeConfigurationRequest,
type ChangeConfigurationResponse,
- type ClearChargingProfileRequest,
- type ClearChargingProfileResponse,
ErrorType,
type GenericResponse,
GenericStatus,
OCPP16ChargingProfilePurposeType,
type OCPP16ChargingSchedule,
type OCPP16ClearCacheRequest,
+ type OCPP16ClearChargingProfileRequest,
+ type OCPP16ClearChargingProfileResponse,
type OCPP16DataTransferRequest,
type OCPP16DataTransferResponse,
OCPP16DataTransferVendorId,
],
[
OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE,
- OCPP16ServiceUtils.parseJsonSchemaFile<ClearChargingProfileRequest>(
+ OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16ClearChargingProfileRequest>(
'assets/json-schemas/ocpp/1.6/ClearChargingProfile.json',
moduleName,
'constructor',
private handleRequestClearChargingProfile(
chargingStation: ChargingStation,
- commandPayload: ClearChargingProfileRequest,
- ): ClearChargingProfileResponse {
+ commandPayload: OCPP16ClearChargingProfileRequest,
+ ): OCPP16ClearChargingProfileResponse {
if (
OCPP16ServiceUtils.checkFeatureProfile(
chargingStation,
import { OCPPError } from '../../../exception';
import {
type ChangeConfigurationResponse,
- type ClearChargingProfileResponse,
ErrorType,
type GenericResponse,
type GetConfigurationResponse,
type OCPP16BootNotificationResponse,
type OCPP16ChangeAvailabilityResponse,
OCPP16ChargePointStatus,
+ type OCPP16ClearChargingProfileResponse,
type OCPP16DataTransferResponse,
type OCPP16DiagnosticsStatusNotificationResponse,
type OCPP16FirmwareStatusNotificationResponse,
],
[
OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE,
- OCPP16ServiceUtils.parseJsonSchemaFile<ClearChargingProfileResponse>(
+ OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16ClearChargingProfileResponse>(
'assets/json-schemas/ocpp/1.6/ClearChargingProfileResponse.json',
moduleName,
'constructor',
hasFeatureProfile,
hasReservationExpired,
} from '../../../charging-station';
-import { OCPPError } from '../../../exception';
import {
- type ClearChargingProfileRequest,
- CurrentType,
- ErrorType,
type GenericResponse,
type JsonType,
- type MeasurandPerPhaseSampledValueTemplates,
- type MeasurandValues,
- MeterValueContext,
- MeterValueLocation,
- MeterValueUnit,
OCPP16AuthorizationStatus,
OCPP16AvailabilityType,
type OCPP16ChangeAvailabilityResponse,
OCPP16ChargePointStatus,
type OCPP16ChargingProfile,
type OCPP16ChargingSchedule,
+ type OCPP16ClearChargingProfileRequest,
type OCPP16IncomingRequestCommand,
type OCPP16MeterValue,
- OCPP16MeterValueMeasurand,
- OCPP16MeterValuePhase,
+ OCPP16MeterValueContext,
+ OCPP16MeterValueUnit,
OCPP16RequestCommand,
- type OCPP16SampledValue,
OCPP16StandardParametersKey,
OCPP16StopTransactionReason,
type OCPP16SupportedFeatureProfiles,
OCPPVersion,
- type SampledValueTemplate,
} from '../../../types';
-import {
- ACElectricUtils,
- Constants,
- DCElectricUtils,
- convertToFloat,
- convertToInt,
- getRandomFloatFluctuatedRounded,
- getRandomFloatRounded,
- getRandomInteger,
- isNotEmptyArray,
- isNotEmptyString,
- isNullOrUndefined,
- isUndefined,
- logger,
- roundTo,
-} from '../../../utils';
+import { isNotEmptyArray, isNullOrUndefined, logger, roundTo } from '../../../utils';
import { OCPPServiceUtils } from '../OCPPServiceUtils';
export class OCPP16ServiceUtils extends OCPPServiceUtils {
return true;
}
- public static buildMeterValue(
- chargingStation: ChargingStation,
- connectorId: number,
- transactionId: number,
- interval: number,
- debug = false,
- ): OCPP16MeterValue {
- const meterValue: OCPP16MeterValue = {
- timestamp: new Date(),
- sampledValue: [],
- };
- const connector = chargingStation.getConnectorStatus(connectorId);
- // SoC measurand
- const socSampledValueTemplate = OCPP16ServiceUtils.getSampledValueTemplate(
- chargingStation,
- connectorId,
- OCPP16MeterValueMeasurand.STATE_OF_CHARGE,
- );
- if (socSampledValueTemplate) {
- const socMaximumValue = 100;
- const socMinimumValue = socSampledValueTemplate.minimumValue ?? 0;
- const socSampledValueTemplateValue = isNotEmptyString(socSampledValueTemplate.value)
- ? getRandomFloatFluctuatedRounded(
- parseInt(socSampledValueTemplate.value),
- socSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT,
- )
- : getRandomInteger(socMaximumValue, socMinimumValue);
- meterValue.sampledValue.push(
- OCPP16ServiceUtils.buildSampledValue(socSampledValueTemplate, socSampledValueTemplateValue),
- );
- 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 ??
- OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- }: connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${socMinimumValue}/${
- meterValue.sampledValue[sampledValuesIndex].value
- }/${socMaximumValue}`,
- );
- }
- }
- // Voltage measurand
- const voltageSampledValueTemplate = OCPP16ServiceUtils.getSampledValueTemplate(
- chargingStation,
- connectorId,
- OCPP16MeterValueMeasurand.VOLTAGE,
- );
- if (voltageSampledValueTemplate) {
- const voltageSampledValueTemplateValue = isNotEmptyString(voltageSampledValueTemplate.value)
- ? parseInt(voltageSampledValueTemplate.value)
- : 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)
- ) {
- meterValue.sampledValue.push(
- OCPP16ServiceUtils.buildSampledValue(voltageSampledValueTemplate, voltageMeasurandValue),
- );
- }
- for (
- let phase = 1;
- chargingStation.getNumberOfPhases() === 3 && phase <= chargingStation.getNumberOfPhases();
- phase++
- ) {
- const phaseLineToNeutralValue = `L${phase}-N`;
- const voltagePhaseLineToNeutralSampledValueTemplate =
- OCPP16ServiceUtils.getSampledValueTemplate(
- chargingStation,
- connectorId,
- OCPP16MeterValueMeasurand.VOLTAGE,
- phaseLineToNeutralValue as OCPP16MeterValuePhase,
- );
- let voltagePhaseLineToNeutralMeasurandValue: number | undefined;
- if (voltagePhaseLineToNeutralSampledValueTemplate) {
- const voltagePhaseLineToNeutralSampledValueTemplateValue = isNotEmptyString(
- voltagePhaseLineToNeutralSampledValueTemplate.value,
- )
- ? parseInt(voltagePhaseLineToNeutralSampledValueTemplate.value)
- : chargingStation.stationInfo.voltageOut!;
- const fluctuationPhaseToNeutralPercent =
- voltagePhaseLineToNeutralSampledValueTemplate.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT;
- voltagePhaseLineToNeutralMeasurandValue = getRandomFloatFluctuatedRounded(
- voltagePhaseLineToNeutralSampledValueTemplateValue,
- fluctuationPhaseToNeutralPercent,
- );
- }
- meterValue.sampledValue.push(
- OCPP16ServiceUtils.buildSampledValue(
- voltagePhaseLineToNeutralSampledValueTemplate ?? voltageSampledValueTemplate,
- voltagePhaseLineToNeutralMeasurandValue ?? voltageMeasurandValue,
- undefined,
- phaseLineToNeutralValue as OCPP16MeterValuePhase,
- ),
- );
- if (chargingStation.stationInfo?.phaseLineToLineVoltageMeterValues) {
- const phaseLineToLineValue = `L${phase}-L${
- (phase + 1) % chargingStation.getNumberOfPhases() !== 0
- ? (phase + 1) % chargingStation.getNumberOfPhases()
- : chargingStation.getNumberOfPhases()
- }`;
- const voltagePhaseLineToLineValueRounded = roundTo(
- Math.sqrt(chargingStation.getNumberOfPhases()) *
- chargingStation.stationInfo.voltageOut!,
- 2,
- );
- const voltagePhaseLineToLineSampledValueTemplate =
- OCPP16ServiceUtils.getSampledValueTemplate(
- chargingStation,
- connectorId,
- OCPP16MeterValueMeasurand.VOLTAGE,
- phaseLineToLineValue as OCPP16MeterValuePhase,
- );
- let voltagePhaseLineToLineMeasurandValue: number | undefined;
- if (voltagePhaseLineToLineSampledValueTemplate) {
- const voltagePhaseLineToLineSampledValueTemplateValue = isNotEmptyString(
- voltagePhaseLineToLineSampledValueTemplate.value,
- )
- ? parseInt(voltagePhaseLineToLineSampledValueTemplate.value)
- : voltagePhaseLineToLineValueRounded;
- const fluctuationPhaseLineToLinePercent =
- voltagePhaseLineToLineSampledValueTemplate.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT;
- voltagePhaseLineToLineMeasurandValue = getRandomFloatFluctuatedRounded(
- voltagePhaseLineToLineSampledValueTemplateValue,
- fluctuationPhaseLineToLinePercent,
- );
- }
- const defaultVoltagePhaseLineToLineMeasurandValue = getRandomFloatFluctuatedRounded(
- voltagePhaseLineToLineValueRounded,
- fluctuationPercent,
- );
- meterValue.sampledValue.push(
- OCPP16ServiceUtils.buildSampledValue(
- voltagePhaseLineToLineSampledValueTemplate ?? voltageSampledValueTemplate,
- voltagePhaseLineToLineMeasurandValue ?? defaultVoltagePhaseLineToLineMeasurandValue,
- undefined,
- phaseLineToLineValue as OCPP16MeterValuePhase,
- ),
- );
- }
- }
- }
- // Power.Active.Import measurand
- const powerSampledValueTemplate = OCPP16ServiceUtils.getSampledValueTemplate(
- chargingStation,
- connectorId,
- OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT,
- );
- let powerPerPhaseSampledValueTemplates: MeasurandPerPhaseSampledValueTemplates = {};
- if (chargingStation.getNumberOfPhases() === 3) {
- powerPerPhaseSampledValueTemplates = {
- L1: OCPP16ServiceUtils.getSampledValueTemplate(
- chargingStation,
- connectorId,
- OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT,
- OCPP16MeterValuePhase.L1_N,
- ),
- L2: OCPP16ServiceUtils.getSampledValueTemplate(
- chargingStation,
- connectorId,
- OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT,
- OCPP16MeterValuePhase.L2_N,
- ),
- L3: OCPP16ServiceUtils.getSampledValueTemplate(
- chargingStation,
- connectorId,
- OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT,
- OCPP16MeterValuePhase.L3_N,
- ),
- };
- }
- if (powerSampledValueTemplate) {
- OCPP16ServiceUtils.checkMeasurandPowerDivider(
- chargingStation,
- powerSampledValueTemplate.measurand!,
- );
- const errMsg = `MeterValues measurand ${
- powerSampledValueTemplate.measurand ??
- OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- }: Unknown ${chargingStation.stationInfo?.currentOutType} currentOutType in template file ${
- chargingStation.templateFile
- }, cannot calculate ${
- powerSampledValueTemplate.measurand ??
- OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- } measurand value`;
- const powerMeasurandValues: MeasurandValues = {} as MeasurandValues;
- const unitDivider = powerSampledValueTemplate?.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(
- OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
- powerSampledValueTemplate.value,
- connectorMaximumPower / unitDivider,
- connectorMinimumPower / unitDivider,
- {
- limitationEnabled:
- chargingStation.stationInfo?.customValueLimitationMeterValues,
- fallbackValue: connectorMinimumPower / unitDivider,
- },
- ) / chargingStation.getNumberOfPhases(),
- powerSampledValueTemplate.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT,
- )
- : undefined;
- const phase1FluctuatedValue = isNotEmptyString(
- powerPerPhaseSampledValueTemplates.L1?.value,
- )
- ? getRandomFloatFluctuatedRounded(
- OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
- powerPerPhaseSampledValueTemplates.L1?.value,
- connectorMaximumPowerPerPhase / unitDivider,
- connectorMinimumPowerPerPhase / unitDivider,
- {
- limitationEnabled:
- chargingStation.stationInfo?.customValueLimitationMeterValues,
- fallbackValue: connectorMinimumPowerPerPhase / unitDivider,
- },
- ),
- powerPerPhaseSampledValueTemplates.L1?.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT,
- )
- : undefined;
- const phase2FluctuatedValue = isNotEmptyString(
- powerPerPhaseSampledValueTemplates.L2?.value,
- )
- ? getRandomFloatFluctuatedRounded(
- OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
- powerPerPhaseSampledValueTemplates.L2?.value,
- connectorMaximumPowerPerPhase / unitDivider,
- connectorMinimumPowerPerPhase / unitDivider,
- {
- limitationEnabled:
- chargingStation.stationInfo?.customValueLimitationMeterValues,
- fallbackValue: connectorMinimumPowerPerPhase / unitDivider,
- },
- ),
- powerPerPhaseSampledValueTemplates.L2?.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT,
- )
- : undefined;
- const phase3FluctuatedValue = isNotEmptyString(
- powerPerPhaseSampledValueTemplates.L3?.value,
- )
- ? getRandomFloatFluctuatedRounded(
- OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
- powerPerPhaseSampledValueTemplates.L3?.value,
- connectorMaximumPowerPerPhase / unitDivider,
- connectorMinimumPowerPerPhase / unitDivider,
- {
- limitationEnabled:
- chargingStation.stationInfo?.customValueLimitationMeterValues,
- fallbackValue: connectorMinimumPowerPerPhase / unitDivider,
- },
- ),
- 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(
- OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
- powerSampledValueTemplate.value,
- connectorMaximumPower / unitDivider,
- connectorMinimumPower / unitDivider,
- {
- limitationEnabled:
- chargingStation.stationInfo?.customValueLimitationMeterValues,
- fallbackValue: connectorMinimumPower / unitDivider,
- },
- ),
- 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(
- OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
- powerSampledValueTemplate.value,
- connectorMaximumPower / unitDivider,
- connectorMinimumPower / unitDivider,
- {
- limitationEnabled:
- chargingStation.stationInfo?.customValueLimitationMeterValues,
- fallbackValue: connectorMinimumPower / unitDivider,
- },
- ),
- 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, OCPP16RequestCommand.METER_VALUES);
- }
- meterValue.sampledValue.push(
- OCPP16ServiceUtils.buildSampledValue(
- powerSampledValueTemplate,
- powerMeasurandValues.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 ||
- debug
- ) {
- logger.error(
- `${chargingStation.logPrefix()} MeterValues measurand ${
- meterValue.sampledValue[sampledValuesIndex].measurand ??
- OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- }: connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${connectorMinimumPowerRounded}/${
- meterValue.sampledValue[sampledValuesIndex].value
- }/${connectorMaximumPowerRounded}`,
- );
- }
- for (
- let phase = 1;
- chargingStation.getNumberOfPhases() === 3 && phase <= chargingStation.getNumberOfPhases();
- phase++
- ) {
- const phaseValue = `L${phase}-N`;
- meterValue.sampledValue.push(
- OCPP16ServiceUtils.buildSampledValue(
- powerPerPhaseSampledValueTemplates[
- `L${phase}` as keyof MeasurandPerPhaseSampledValueTemplates
- ] ?? powerSampledValueTemplate,
- powerMeasurandValues[`L${phase}` as keyof MeasurandPerPhaseSampledValueTemplates],
- undefined,
- phaseValue as OCPP16MeterValuePhase,
- ),
- );
- const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1;
- const connectorMaximumPowerPerPhaseRounded = roundTo(
- connectorMaximumPowerPerPhase / unitDivider,
- 2,
- );
- const connectorMinimumPowerPerPhaseRounded = roundTo(
- connectorMinimumPowerPerPhase / unitDivider,
- 2,
- );
- if (
- convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) >
- connectorMaximumPowerPerPhaseRounded ||
- convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) <
- connectorMinimumPowerPerPhaseRounded ||
- debug
- ) {
- logger.error(
- `${chargingStation.logPrefix()} MeterValues measurand ${
- meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ??
- OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- }: phase ${
- meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
- }, connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${connectorMinimumPowerPerPhaseRounded}/${
- meterValue.sampledValue[sampledValuesPerPhaseIndex].value
- }/${connectorMaximumPowerPerPhaseRounded}`,
- );
- }
- }
- }
- // Current.Import measurand
- const currentSampledValueTemplate = OCPP16ServiceUtils.getSampledValueTemplate(
- chargingStation,
- connectorId,
- OCPP16MeterValueMeasurand.CURRENT_IMPORT,
- );
- let currentPerPhaseSampledValueTemplates: MeasurandPerPhaseSampledValueTemplates = {};
- if (chargingStation.getNumberOfPhases() === 3) {
- currentPerPhaseSampledValueTemplates = {
- L1: OCPP16ServiceUtils.getSampledValueTemplate(
- chargingStation,
- connectorId,
- OCPP16MeterValueMeasurand.CURRENT_IMPORT,
- OCPP16MeterValuePhase.L1,
- ),
- L2: OCPP16ServiceUtils.getSampledValueTemplate(
- chargingStation,
- connectorId,
- OCPP16MeterValueMeasurand.CURRENT_IMPORT,
- OCPP16MeterValuePhase.L2,
- ),
- L3: OCPP16ServiceUtils.getSampledValueTemplate(
- chargingStation,
- connectorId,
- OCPP16MeterValueMeasurand.CURRENT_IMPORT,
- OCPP16MeterValuePhase.L3,
- ),
- };
- }
- if (currentSampledValueTemplate) {
- OCPP16ServiceUtils.checkMeasurandPowerDivider(
- chargingStation,
- currentSampledValueTemplate.measurand!,
- );
- const errMsg = `MeterValues measurand ${
- currentSampledValueTemplate.measurand ??
- OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- }: Unknown ${chargingStation.stationInfo?.currentOutType} currentOutType in template file ${
- chargingStation.templateFile
- }, cannot calculate ${
- currentSampledValueTemplate.measurand ??
- OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- } measurand value`;
- const currentMeasurandValues: MeasurandValues = {} as MeasurandValues;
- const connectorMaximumAvailablePower =
- chargingStation.getConnectorMaximumAvailablePower(connectorId);
- const connectorMinimumAmperage = currentSampledValueTemplate.minimumValue ?? 0;
- let connectorMaximumAmperage: number;
- switch (chargingStation.stationInfo?.currentOutType) {
- case CurrentType.AC:
- connectorMaximumAmperage = ACElectricUtils.amperagePerPhaseFromPower(
- chargingStation.getNumberOfPhases(),
- connectorMaximumAvailablePower,
- chargingStation.stationInfo.voltageOut!,
- );
- if (chargingStation.getNumberOfPhases() === 3) {
- const defaultFluctuatedAmperagePerPhase = isNotEmptyString(
- currentSampledValueTemplate.value,
- )
- ? getRandomFloatFluctuatedRounded(
- OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
- currentSampledValueTemplate.value,
- connectorMaximumAmperage,
- connectorMinimumAmperage,
- {
- limitationEnabled:
- chargingStation.stationInfo?.customValueLimitationMeterValues,
- fallbackValue: connectorMinimumAmperage,
- },
- ),
- currentSampledValueTemplate.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT,
- )
- : undefined;
- const phase1FluctuatedValue = isNotEmptyString(
- currentPerPhaseSampledValueTemplates.L1?.value,
- )
- ? getRandomFloatFluctuatedRounded(
- OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
- currentPerPhaseSampledValueTemplates.L1?.value,
- connectorMaximumAmperage,
- connectorMinimumAmperage,
- {
- limitationEnabled:
- chargingStation.stationInfo?.customValueLimitationMeterValues,
- fallbackValue: connectorMinimumAmperage,
- },
- ),
- currentPerPhaseSampledValueTemplates.L1?.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT,
- )
- : undefined;
- const phase2FluctuatedValue = isNotEmptyString(
- currentPerPhaseSampledValueTemplates.L2?.value,
- )
- ? getRandomFloatFluctuatedRounded(
- OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
- currentPerPhaseSampledValueTemplates.L2?.value,
- connectorMaximumAmperage,
- connectorMinimumAmperage,
- {
- limitationEnabled:
- chargingStation.stationInfo?.customValueLimitationMeterValues,
- fallbackValue: connectorMinimumAmperage,
- },
- ),
- currentPerPhaseSampledValueTemplates.L2?.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT,
- )
- : undefined;
- const phase3FluctuatedValue = isNotEmptyString(
- currentPerPhaseSampledValueTemplates.L3?.value,
- )
- ? getRandomFloatFluctuatedRounded(
- OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
- currentPerPhaseSampledValueTemplates.L3?.value,
- connectorMaximumAmperage,
- connectorMinimumAmperage,
- {
- limitationEnabled:
- chargingStation.stationInfo?.customValueLimitationMeterValues,
- fallbackValue: connectorMinimumAmperage,
- },
- ),
- 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(
- OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
- currentSampledValueTemplate.value,
- connectorMaximumAmperage,
- connectorMinimumAmperage,
- {
- limitationEnabled:
- chargingStation.stationInfo?.customValueLimitationMeterValues,
- fallbackValue: connectorMinimumAmperage,
- },
- ),
- 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(
- connectorMaximumAvailablePower,
- chargingStation.stationInfo.voltageOut!,
- );
- currentMeasurandValues.allPhases = isNotEmptyString(currentSampledValueTemplate.value)
- ? getRandomFloatFluctuatedRounded(
- OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
- currentSampledValueTemplate.value,
- connectorMaximumAmperage,
- connectorMinimumAmperage,
- {
- limitationEnabled:
- chargingStation.stationInfo?.customValueLimitationMeterValues,
- fallbackValue: connectorMinimumAmperage,
- },
- ),
- currentSampledValueTemplate.fluctuationPercent ??
- Constants.DEFAULT_FLUCTUATION_PERCENT,
- )
- : getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage);
- break;
- default:
- logger.error(`${chargingStation.logPrefix()} ${errMsg}`);
- throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, OCPP16RequestCommand.METER_VALUES);
- }
- meterValue.sampledValue.push(
- OCPP16ServiceUtils.buildSampledValue(
- currentSampledValueTemplate,
- currentMeasurandValues.allPhases,
- ),
- );
- const sampledValuesIndex = meterValue.sampledValue.length - 1;
- if (
- convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) >
- connectorMaximumAmperage ||
- convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) <
- connectorMinimumAmperage ||
- debug
- ) {
- logger.error(
- `${chargingStation.logPrefix()} MeterValues measurand ${
- meterValue.sampledValue[sampledValuesIndex].measurand ??
- OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- }: connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${connectorMinimumAmperage}/${
- meterValue.sampledValue[sampledValuesIndex].value
- }/${connectorMaximumAmperage}`,
- );
- }
- for (
- let phase = 1;
- chargingStation.getNumberOfPhases() === 3 && phase <= chargingStation.getNumberOfPhases();
- phase++
- ) {
- const phaseValue = `L${phase}`;
- meterValue.sampledValue.push(
- OCPP16ServiceUtils.buildSampledValue(
- currentPerPhaseSampledValueTemplates[
- phaseValue as keyof MeasurandPerPhaseSampledValueTemplates
- ] ?? currentSampledValueTemplate,
- currentMeasurandValues[phaseValue as keyof MeasurandPerPhaseSampledValueTemplates],
- undefined,
- phaseValue as OCPP16MeterValuePhase,
- ),
- );
- const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1;
- if (
- convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) >
- connectorMaximumAmperage ||
- convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) <
- connectorMinimumAmperage ||
- debug
- ) {
- logger.error(
- `${chargingStation.logPrefix()} MeterValues measurand ${
- meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ??
- OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- }: phase ${
- meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
- }, connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${connectorMinimumAmperage}/${
- meterValue.sampledValue[sampledValuesPerPhaseIndex].value
- }/${connectorMaximumAmperage}`,
- );
- }
- }
- }
- // Energy.Active.Import.Register measurand (default)
- const energySampledValueTemplate = OCPP16ServiceUtils.getSampledValueTemplate(
- chargingStation,
- connectorId,
- );
- if (energySampledValueTemplate) {
- OCPP16ServiceUtils.checkMeasurandPowerDivider(
- chargingStation,
- energySampledValueTemplate.measurand!,
- );
- const unitDivider =
- energySampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1;
- 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(
- OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
- energySampledValueTemplate.value,
- connectorMaximumEnergyRounded,
- connectorMinimumEnergyRounded,
- {
- limitationEnabled: chargingStation.stationInfo?.customValueLimitationMeterValues,
- fallbackValue: connectorMinimumEnergyRounded,
- unitMultiplier: unitDivider,
- },
- ),
- energySampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT,
- )
- : getRandomFloatRounded(connectorMaximumEnergyRounded, connectorMinimumEnergyRounded);
- // Persist previous value on connector
- if (connector) {
- if (
- isNullOrUndefined(connector.energyActiveImportRegisterValue) === false &&
- connector.energyActiveImportRegisterValue! >= 0 &&
- isNullOrUndefined(connector.transactionEnergyActiveImportRegisterValue) === false &&
- connector.transactionEnergyActiveImportRegisterValue! >= 0
- ) {
- connector.energyActiveImportRegisterValue! += energyValueRounded;
- connector.transactionEnergyActiveImportRegisterValue! += energyValueRounded;
- } else {
- connector.energyActiveImportRegisterValue = 0;
- connector.transactionEnergyActiveImportRegisterValue = 0;
- }
- }
- meterValue.sampledValue.push(
- OCPP16ServiceUtils.buildSampledValue(
- energySampledValueTemplate,
- roundTo(
- chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId) /
- unitDivider,
- 2,
- ),
- ),
- );
- const sampledValuesIndex = meterValue.sampledValue.length - 1;
- if (
- energyValueRounded > connectorMaximumEnergyRounded ||
- energyValueRounded < connectorMinimumEnergyRounded ||
- debug
- ) {
- logger.error(
- `${chargingStation.logPrefix()} MeterValues measurand ${
- meterValue.sampledValue[sampledValuesIndex].measurand ??
- OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- }: connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${connectorMinimumEnergyRounded}/${energyValueRounded}/${connectorMaximumEnergyRounded}, duration: ${interval}ms`,
- );
- }
- }
- return meterValue;
- }
-
public static buildTransactionBeginMeterValue(
chargingStation: ChargingStation,
connectorId: number,
chargingStation,
connectorId,
);
- const unitDivider = sampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1;
+ const unitDivider =
+ sampledValueTemplate?.unit === OCPP16MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1;
meterValue.sampledValue.push(
OCPP16ServiceUtils.buildSampledValue(
sampledValueTemplate!,
roundTo((meterStart ?? 0) / unitDivider, 4),
- MeterValueContext.TRANSACTION_BEGIN,
- ),
- );
- return meterValue;
- }
-
- public static buildTransactionEndMeterValue(
- chargingStation: ChargingStation,
- connectorId: number,
- meterStop: number,
- ): OCPP16MeterValue {
- const meterValue: OCPP16MeterValue = {
- timestamp: new Date(),
- sampledValue: [],
- };
- // Energy.Active.Import.Register measurand (default)
- const sampledValueTemplate = OCPP16ServiceUtils.getSampledValueTemplate(
- chargingStation,
- connectorId,
- );
- const unitDivider = sampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1;
- meterValue.sampledValue.push(
- OCPP16ServiceUtils.buildSampledValue(
- sampledValueTemplate!,
- roundTo((meterStop ?? 0) / unitDivider, 4),
- MeterValueContext.TRANSACTION_END,
+ OCPP16MeterValueContext.TRANSACTION_BEGIN,
),
);
return meterValue;
public static clearChargingProfiles = (
chargingStation: ChargingStation,
- commandPayload: ClearChargingProfileRequest,
+ commandPayload: OCPP16ClearChargingProfileRequest,
chargingProfiles: OCPP16ChargingProfile[] | undefined,
): boolean => {
const { id, chargingProfilePurpose, stackLevel } = commandPayload;
return chargingSchedule;
}
};
-
- private static buildSampledValue(
- sampledValueTemplate: SampledValueTemplate,
- value: number,
- context?: MeterValueContext,
- phase?: OCPP16MeterValuePhase,
- ): OCPP16SampledValue {
- const sampledValueContext = context ?? sampledValueTemplate?.context;
- const sampledValueLocation =
- sampledValueTemplate?.location ??
- OCPP16ServiceUtils.getMeasurandDefaultLocation(sampledValueTemplate.measurand!);
- const sampledValuePhase = phase ?? sampledValueTemplate?.phase;
- return {
- ...(!isNullOrUndefined(sampledValueTemplate.unit) && {
- unit: sampledValueTemplate.unit,
- }),
- ...(!isNullOrUndefined(sampledValueContext) && { context: sampledValueContext }),
- ...(!isNullOrUndefined(sampledValueTemplate.measurand) && {
- measurand: sampledValueTemplate.measurand,
- }),
- ...(!isNullOrUndefined(sampledValueLocation) && { location: sampledValueLocation }),
- ...(!isNullOrUndefined(value) && { value: value.toString() }),
- ...(!isNullOrUndefined(sampledValuePhase) && { phase: sampledValuePhase }),
- } as OCPP16SampledValue;
- }
-
- private static checkMeasurandPowerDivider(
- chargingStation: ChargingStation,
- measurandType: OCPP16MeterValueMeasurand,
- ): void {
- if (isUndefined(chargingStation.powerDivider)) {
- const errMsg = `MeterValues measurand ${
- measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- }: powerDivider is undefined`;
- logger.error(`${chargingStation.logPrefix()} ${errMsg}`);
- throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, OCPP16RequestCommand.METER_VALUES);
- } else if (chargingStation?.powerDivider <= 0) {
- const errMsg = `MeterValues measurand ${
- measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- }: powerDivider have zero or below value ${chargingStation.powerDivider}`;
- logger.error(`${chargingStation.logPrefix()} ${errMsg}`);
- throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, OCPP16RequestCommand.METER_VALUES);
- }
- }
-
- private static getMeasurandDefaultLocation(
- measurandType: OCPP16MeterValueMeasurand,
- ): MeterValueLocation | undefined {
- switch (measurandType) {
- case OCPP16MeterValueMeasurand.STATE_OF_CHARGE:
- return MeterValueLocation.EV;
- }
- }
-
- // private static getMeasurandDefaultUnit(
- // measurandType: OCPP16MeterValueMeasurand,
- // ): MeterValueUnit | undefined {
- // switch (measurandType) {
- // case OCPP16MeterValueMeasurand.CURRENT_EXPORT:
- // case OCPP16MeterValueMeasurand.CURRENT_IMPORT:
- // case OCPP16MeterValueMeasurand.CURRENT_OFFERED:
- // return MeterValueUnit.AMP;
- // case OCPP16MeterValueMeasurand.ENERGY_ACTIVE_EXPORT_REGISTER:
- // case OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER:
- // return MeterValueUnit.WATT_HOUR;
- // case OCPP16MeterValueMeasurand.POWER_ACTIVE_EXPORT:
- // case OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT:
- // case OCPP16MeterValueMeasurand.POWER_OFFERED:
- // return MeterValueUnit.WATT;
- // case OCPP16MeterValueMeasurand.STATE_OF_CHARGE:
- // return MeterValueUnit.PERCENT;
- // case OCPP16MeterValueMeasurand.VOLTAGE:
- // return MeterValueUnit.VOLT;
- // }
- // }
}
import { OCPP20Constants } from './2.0/OCPP20Constants';
import { OCPPConstants } from './OCPPConstants';
import { type ChargingStation, getConfigurationKey, getIdTagsFile } from '../../charging-station';
-import { BaseError } from '../../exception';
+import { BaseError, OCPPError } from '../../exception';
import {
AuthorizationStatus,
type AuthorizeRequest,
ChargingStationEvents,
type ConnectorStatus,
type ConnectorStatusEnum,
+ CurrentType,
ErrorType,
FileType,
IncomingRequestCommand,
type JsonType,
+ type MeasurandPerPhaseSampledValueTemplates,
+ type MeasurandValues,
MessageTrigger,
MessageType,
+ type MeterValue,
+ MeterValueContext,
+ MeterValueLocation,
MeterValueMeasurand,
- type MeterValuePhase,
+ MeterValuePhase,
+ MeterValueUnit,
type OCPP16StatusNotificationRequest,
type OCPP20StatusNotificationRequest,
OCPPVersion,
RequestCommand,
+ type SampledValue,
type SampledValueTemplate,
StandardParametersKey,
type StatusNotificationRequest,
type StatusNotificationResponse,
} from '../../types';
import {
+ ACElectricUtils,
+ Constants,
+ DCElectricUtils,
+ convertToFloat,
+ convertToInt,
+ getRandomFloatFluctuatedRounded,
+ getRandomFloatRounded,
+ getRandomInteger,
handleFileException,
isNotEmptyArray,
isNotEmptyString,
+ isNullOrUndefined,
+ isUndefined,
logPrefix,
logger,
max,
min,
+ roundTo,
} from '../../utils';
export const getMessageTypeString = (messageType: MessageType): string => {
return transitionAllowed;
};
+export const buildMeterValue = (
+ chargingStation: ChargingStation,
+ connectorId: number,
+ transactionId: number,
+ interval: number,
+ debug = false,
+): MeterValue => {
+ const connector = chargingStation.getConnectorStatus(connectorId);
+ let meterValue: MeterValue;
+ 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 = {
+ timestamp: new Date(),
+ sampledValue: [],
+ };
+ // SoC measurand
+ socSampledValueTemplate = getSampledValueTemplate(
+ chargingStation,
+ connectorId,
+ MeterValueMeasurand.STATE_OF_CHARGE,
+ );
+ if (socSampledValueTemplate) {
+ const socMaximumValue = 100;
+ const socMinimumValue = socSampledValueTemplate.minimumValue ?? 0;
+ const socSampledValueTemplateValue = isNotEmptyString(socSampledValueTemplate.value)
+ ? getRandomFloatFluctuatedRounded(
+ parseInt(socSampledValueTemplate.value),
+ socSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT,
+ )
+ : getRandomInteger(socMaximumValue, socMinimumValue);
+ meterValue.sampledValue.push(
+ buildSampledValue(socSampledValueTemplate, socSampledValueTemplateValue),
+ );
+ 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
+ }: connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${socMinimumValue}/${
+ meterValue.sampledValue[sampledValuesIndex].value
+ }/${socMaximumValue}`,
+ );
+ }
+ }
+ // Voltage measurand
+ voltageSampledValueTemplate = getSampledValueTemplate(
+ chargingStation,
+ connectorId,
+ MeterValueMeasurand.VOLTAGE,
+ );
+ if (voltageSampledValueTemplate) {
+ const voltageSampledValueTemplateValue = isNotEmptyString(voltageSampledValueTemplate.value)
+ ? parseInt(voltageSampledValueTemplate.value)
+ : 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)
+ ) {
+ meterValue.sampledValue.push(
+ buildSampledValue(voltageSampledValueTemplate, voltageMeasurandValue),
+ );
+ }
+ for (
+ let phase = 1;
+ chargingStation.getNumberOfPhases() === 3 && phase <= chargingStation.getNumberOfPhases();
+ phase++
+ ) {
+ const phaseLineToNeutralValue = `L${phase}-N`;
+ const voltagePhaseLineToNeutralSampledValueTemplate = getSampledValueTemplate(
+ chargingStation,
+ connectorId,
+ MeterValueMeasurand.VOLTAGE,
+ phaseLineToNeutralValue as MeterValuePhase,
+ );
+ let voltagePhaseLineToNeutralMeasurandValue: number | undefined;
+ if (voltagePhaseLineToNeutralSampledValueTemplate) {
+ const voltagePhaseLineToNeutralSampledValueTemplateValue = isNotEmptyString(
+ voltagePhaseLineToNeutralSampledValueTemplate.value,
+ )
+ ? parseInt(voltagePhaseLineToNeutralSampledValueTemplate.value)
+ : chargingStation.stationInfo.voltageOut!;
+ const fluctuationPhaseToNeutralPercent =
+ voltagePhaseLineToNeutralSampledValueTemplate.fluctuationPercent ??
+ Constants.DEFAULT_FLUCTUATION_PERCENT;
+ voltagePhaseLineToNeutralMeasurandValue = getRandomFloatFluctuatedRounded(
+ voltagePhaseLineToNeutralSampledValueTemplateValue,
+ fluctuationPhaseToNeutralPercent,
+ );
+ }
+ meterValue.sampledValue.push(
+ buildSampledValue(
+ voltagePhaseLineToNeutralSampledValueTemplate ?? voltageSampledValueTemplate,
+ voltagePhaseLineToNeutralMeasurandValue ?? voltageMeasurandValue,
+ undefined,
+ phaseLineToNeutralValue as MeterValuePhase,
+ ),
+ );
+ if (chargingStation.stationInfo?.phaseLineToLineVoltageMeterValues) {
+ const phaseLineToLineValue = `L${phase}-L${
+ (phase + 1) % chargingStation.getNumberOfPhases() !== 0
+ ? (phase + 1) % chargingStation.getNumberOfPhases()
+ : chargingStation.getNumberOfPhases()
+ }`;
+ const voltagePhaseLineToLineValueRounded = roundTo(
+ Math.sqrt(chargingStation.getNumberOfPhases()) *
+ chargingStation.stationInfo.voltageOut!,
+ 2,
+ );
+ const voltagePhaseLineToLineSampledValueTemplate = getSampledValueTemplate(
+ chargingStation,
+ connectorId,
+ MeterValueMeasurand.VOLTAGE,
+ phaseLineToLineValue as MeterValuePhase,
+ );
+ let voltagePhaseLineToLineMeasurandValue: number | undefined;
+ if (voltagePhaseLineToLineSampledValueTemplate) {
+ const voltagePhaseLineToLineSampledValueTemplateValue = isNotEmptyString(
+ voltagePhaseLineToLineSampledValueTemplate.value,
+ )
+ ? 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(
+ voltagePhaseLineToLineSampledValueTemplate ?? voltageSampledValueTemplate,
+ voltagePhaseLineToLineMeasurandValue ?? defaultVoltagePhaseLineToLineMeasurandValue,
+ undefined,
+ phaseLineToLineValue as MeterValuePhase,
+ ),
+ );
+ }
+ }
+ }
+ // 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(
+ chargingStation,
+ connectorId,
+ MeterValueMeasurand.POWER_ACTIVE_IMPORT,
+ MeterValuePhase.L2_N,
+ ),
+ L3: getSampledValueTemplate(
+ chargingStation,
+ connectorId,
+ MeterValueMeasurand.POWER_ACTIVE_IMPORT,
+ MeterValuePhase.L3_N,
+ ),
+ };
+ }
+ if (powerSampledValueTemplate) {
+ checkMeasurandPowerDivider(chargingStation, powerSampledValueTemplate.measurand!);
+ const errMsg = `MeterValues measurand ${
+ powerSampledValueTemplate.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
+ }: 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;
+ 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,
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ fallbackValue: connectorMinimumPower / unitDivider,
+ },
+ ) / chargingStation.getNumberOfPhases(),
+ powerSampledValueTemplate.fluctuationPercent ??
+ Constants.DEFAULT_FLUCTUATION_PERCENT,
+ )
+ : undefined;
+ const phase1FluctuatedValue = isNotEmptyString(
+ powerPerPhaseSampledValueTemplates.L1?.value,
+ )
+ ? getRandomFloatFluctuatedRounded(
+ getLimitFromSampledValueTemplateCustomValue(
+ powerPerPhaseSampledValueTemplates.L1?.value,
+ connectorMaximumPowerPerPhase / unitDivider,
+ connectorMinimumPowerPerPhase / unitDivider,
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ fallbackValue: connectorMinimumPowerPerPhase / unitDivider,
+ },
+ ),
+ powerPerPhaseSampledValueTemplates.L1?.fluctuationPercent ??
+ Constants.DEFAULT_FLUCTUATION_PERCENT,
+ )
+ : undefined;
+ const phase2FluctuatedValue = isNotEmptyString(
+ powerPerPhaseSampledValueTemplates.L2?.value,
+ )
+ ? getRandomFloatFluctuatedRounded(
+ getLimitFromSampledValueTemplateCustomValue(
+ powerPerPhaseSampledValueTemplates.L2?.value,
+ connectorMaximumPowerPerPhase / unitDivider,
+ connectorMinimumPowerPerPhase / unitDivider,
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ fallbackValue: connectorMinimumPowerPerPhase / unitDivider,
+ },
+ ),
+ powerPerPhaseSampledValueTemplates.L2?.fluctuationPercent ??
+ Constants.DEFAULT_FLUCTUATION_PERCENT,
+ )
+ : undefined;
+ const phase3FluctuatedValue = isNotEmptyString(
+ powerPerPhaseSampledValueTemplates.L3?.value,
+ )
+ ? getRandomFloatFluctuatedRounded(
+ getLimitFromSampledValueTemplateCustomValue(
+ powerPerPhaseSampledValueTemplates.L3?.value,
+ connectorMaximumPowerPerPhase / unitDivider,
+ connectorMinimumPowerPerPhase / unitDivider,
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ fallbackValue: connectorMinimumPowerPerPhase / unitDivider,
+ },
+ ),
+ 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,
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ fallbackValue: connectorMinimumPower / unitDivider,
+ },
+ ),
+ 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,
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ fallbackValue: connectorMinimumPower / unitDivider,
+ },
+ ),
+ 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);
+ }
+ meterValue.sampledValue.push(
+ buildSampledValue(powerSampledValueTemplate, powerMeasurandValues.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 ||
+ debug
+ ) {
+ logger.error(
+ `${chargingStation.logPrefix()} MeterValues measurand ${
+ meterValue.sampledValue[sampledValuesIndex].measurand ??
+ MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
+ }: connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${connectorMinimumPowerRounded}/${
+ meterValue.sampledValue[sampledValuesIndex].value
+ }/${connectorMaximumPowerRounded}`,
+ );
+ }
+ for (
+ let phase = 1;
+ chargingStation.getNumberOfPhases() === 3 && phase <= chargingStation.getNumberOfPhases();
+ phase++
+ ) {
+ const phaseValue = `L${phase}-N`;
+ meterValue.sampledValue.push(
+ buildSampledValue(
+ powerPerPhaseSampledValueTemplates[
+ `L${phase}` as keyof MeasurandPerPhaseSampledValueTemplates
+ ] ?? powerSampledValueTemplate,
+ powerMeasurandValues[`L${phase}` as keyof MeasurandPerPhaseSampledValueTemplates],
+ undefined,
+ phaseValue as MeterValuePhase,
+ ),
+ );
+ const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1;
+ const connectorMaximumPowerPerPhaseRounded = roundTo(
+ connectorMaximumPowerPerPhase / unitDivider,
+ 2,
+ );
+ const connectorMinimumPowerPerPhaseRounded = roundTo(
+ connectorMinimumPowerPerPhase / unitDivider,
+ 2,
+ );
+ 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 ${
+ meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
+ }, connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${connectorMinimumPowerPerPhaseRounded}/${
+ meterValue.sampledValue[sampledValuesPerPhaseIndex].value
+ }/${connectorMaximumPowerPerPhaseRounded}`,
+ );
+ }
+ }
+ }
+ // 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) {
+ checkMeasurandPowerDivider(chargingStation, currentSampledValueTemplate.measurand!);
+ const errMsg = `MeterValues measurand ${
+ currentSampledValueTemplate.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
+ }: 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;
+ const connectorMaximumAvailablePower =
+ chargingStation.getConnectorMaximumAvailablePower(connectorId);
+ const connectorMinimumAmperage = currentSampledValueTemplate.minimumValue ?? 0;
+ let connectorMaximumAmperage: number;
+ switch (chargingStation.stationInfo?.currentOutType) {
+ case CurrentType.AC:
+ connectorMaximumAmperage = ACElectricUtils.amperagePerPhaseFromPower(
+ chargingStation.getNumberOfPhases(),
+ connectorMaximumAvailablePower,
+ chargingStation.stationInfo.voltageOut!,
+ );
+ if (chargingStation.getNumberOfPhases() === 3) {
+ const defaultFluctuatedAmperagePerPhase = isNotEmptyString(
+ currentSampledValueTemplate.value,
+ )
+ ? getRandomFloatFluctuatedRounded(
+ getLimitFromSampledValueTemplateCustomValue(
+ currentSampledValueTemplate.value,
+ connectorMaximumAmperage,
+ connectorMinimumAmperage,
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ fallbackValue: connectorMinimumAmperage,
+ },
+ ),
+ currentSampledValueTemplate.fluctuationPercent ??
+ Constants.DEFAULT_FLUCTUATION_PERCENT,
+ )
+ : undefined;
+ const phase1FluctuatedValue = isNotEmptyString(
+ currentPerPhaseSampledValueTemplates.L1?.value,
+ )
+ ? getRandomFloatFluctuatedRounded(
+ getLimitFromSampledValueTemplateCustomValue(
+ currentPerPhaseSampledValueTemplates.L1?.value,
+ connectorMaximumAmperage,
+ connectorMinimumAmperage,
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ fallbackValue: connectorMinimumAmperage,
+ },
+ ),
+ currentPerPhaseSampledValueTemplates.L1?.fluctuationPercent ??
+ Constants.DEFAULT_FLUCTUATION_PERCENT,
+ )
+ : undefined;
+ const phase2FluctuatedValue = isNotEmptyString(
+ currentPerPhaseSampledValueTemplates.L2?.value,
+ )
+ ? getRandomFloatFluctuatedRounded(
+ getLimitFromSampledValueTemplateCustomValue(
+ currentPerPhaseSampledValueTemplates.L2?.value,
+ connectorMaximumAmperage,
+ connectorMinimumAmperage,
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ fallbackValue: connectorMinimumAmperage,
+ },
+ ),
+ currentPerPhaseSampledValueTemplates.L2?.fluctuationPercent ??
+ Constants.DEFAULT_FLUCTUATION_PERCENT,
+ )
+ : undefined;
+ const phase3FluctuatedValue = isNotEmptyString(
+ currentPerPhaseSampledValueTemplates.L3?.value,
+ )
+ ? getRandomFloatFluctuatedRounded(
+ getLimitFromSampledValueTemplateCustomValue(
+ currentPerPhaseSampledValueTemplates.L3?.value,
+ connectorMaximumAmperage,
+ connectorMinimumAmperage,
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ fallbackValue: connectorMinimumAmperage,
+ },
+ ),
+ 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,
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ fallbackValue: connectorMinimumAmperage,
+ },
+ ),
+ 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(
+ connectorMaximumAvailablePower,
+ chargingStation.stationInfo.voltageOut!,
+ );
+ currentMeasurandValues.allPhases = isNotEmptyString(currentSampledValueTemplate.value)
+ ? getRandomFloatFluctuatedRounded(
+ getLimitFromSampledValueTemplateCustomValue(
+ currentSampledValueTemplate.value,
+ connectorMaximumAmperage,
+ connectorMinimumAmperage,
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ fallbackValue: connectorMinimumAmperage,
+ },
+ ),
+ 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);
+ }
+ meterValue.sampledValue.push(
+ buildSampledValue(currentSampledValueTemplate, currentMeasurandValues.allPhases),
+ );
+ const sampledValuesIndex = meterValue.sampledValue.length - 1;
+ if (
+ convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) >
+ connectorMaximumAmperage ||
+ convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) <
+ connectorMinimumAmperage ||
+ debug
+ ) {
+ logger.error(
+ `${chargingStation.logPrefix()} MeterValues measurand ${
+ meterValue.sampledValue[sampledValuesIndex].measurand ??
+ MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
+ }: connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${connectorMinimumAmperage}/${
+ meterValue.sampledValue[sampledValuesIndex].value
+ }/${connectorMaximumAmperage}`,
+ );
+ }
+ for (
+ let phase = 1;
+ chargingStation.getNumberOfPhases() === 3 && phase <= chargingStation.getNumberOfPhases();
+ phase++
+ ) {
+ const phaseValue = `L${phase}`;
+ meterValue.sampledValue.push(
+ buildSampledValue(
+ currentPerPhaseSampledValueTemplates[
+ phaseValue as keyof MeasurandPerPhaseSampledValueTemplates
+ ] ?? currentSampledValueTemplate,
+ currentMeasurandValues[phaseValue as keyof MeasurandPerPhaseSampledValueTemplates],
+ undefined,
+ phaseValue as MeterValuePhase,
+ ),
+ );
+ const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1;
+ if (
+ convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) >
+ connectorMaximumAmperage ||
+ convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) <
+ connectorMinimumAmperage ||
+ debug
+ ) {
+ logger.error(
+ `${chargingStation.logPrefix()} MeterValues measurand ${
+ meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ??
+ MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
+ }: phase ${
+ meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
+ }, connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${connectorMinimumAmperage}/${
+ meterValue.sampledValue[sampledValuesPerPhaseIndex].value
+ }/${connectorMaximumAmperage}`,
+ );
+ }
+ }
+ }
+ // Energy.Active.Import.Register measurand (default)
+ energySampledValueTemplate = getSampledValueTemplate(chargingStation, connectorId);
+ if (energySampledValueTemplate) {
+ checkMeasurandPowerDivider(chargingStation, energySampledValueTemplate.measurand!);
+ const unitDivider =
+ energySampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1;
+ 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,
+ {
+ limitationEnabled: chargingStation.stationInfo?.customValueLimitationMeterValues,
+ fallbackValue: connectorMinimumEnergyRounded,
+ unitMultiplier: unitDivider,
+ },
+ ),
+ energySampledValueTemplate.fluctuationPercent ??
+ Constants.DEFAULT_FLUCTUATION_PERCENT,
+ )
+ : getRandomFloatRounded(connectorMaximumEnergyRounded, connectorMinimumEnergyRounded);
+ // Persist previous value on connector
+ if (connector) {
+ if (
+ isNullOrUndefined(connector.energyActiveImportRegisterValue) === false &&
+ connector.energyActiveImportRegisterValue! >= 0 &&
+ isNullOrUndefined(connector.transactionEnergyActiveImportRegisterValue) === false &&
+ connector.transactionEnergyActiveImportRegisterValue! >= 0
+ ) {
+ connector.energyActiveImportRegisterValue! += energyValueRounded;
+ connector.transactionEnergyActiveImportRegisterValue! += energyValueRounded;
+ } else {
+ connector.energyActiveImportRegisterValue = 0;
+ connector.transactionEnergyActiveImportRegisterValue = 0;
+ }
+ }
+ meterValue.sampledValue.push(
+ buildSampledValue(
+ energySampledValueTemplate,
+ roundTo(
+ chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId) /
+ unitDivider,
+ 2,
+ ),
+ ),
+ );
+ 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
+ }: connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${connectorMinimumEnergyRounded}/${energyValueRounded}/${connectorMaximumEnergyRounded}, duration: ${interval}ms`,
+ );
+ }
+ }
+ return meterValue;
+ case OCPPVersion.VERSION_20:
+ case OCPPVersion.VERSION_201:
+ default:
+ throw new BaseError(
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+ `Cannot build meterValue: OCPP version ${chargingStation.stationInfo?.ocppVersion} not supported`,
+ );
+ }
+};
+
+export const buildTransactionEndMeterValue = (
+ chargingStation: ChargingStation,
+ connectorId: number,
+ meterStop: number,
+): MeterValue => {
+ let meterValue: MeterValue;
+ let sampledValueTemplate: SampledValueTemplate | undefined;
+ let unitDivider: number;
+ switch (chargingStation.stationInfo?.ocppVersion) {
+ case OCPPVersion.VERSION_16:
+ meterValue = {
+ timestamp: new Date(),
+ sampledValue: [],
+ };
+ // Energy.Active.Import.Register measurand (default)
+ sampledValueTemplate = getSampledValueTemplate(chargingStation, connectorId);
+ unitDivider = sampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1;
+ meterValue.sampledValue.push(
+ buildSampledValue(
+ sampledValueTemplate!,
+ roundTo((meterStop ?? 0) / unitDivider, 4),
+ MeterValueContext.TRANSACTION_END,
+ ),
+ );
+ return meterValue;
+ case OCPPVersion.VERSION_20:
+ case OCPPVersion.VERSION_201:
+ default:
+ throw new BaseError(
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+ `Cannot build meterValue: OCPP version ${chargingStation.stationInfo?.ocppVersion} not supported`,
+ );
+ }
+};
+
+const checkMeasurandPowerDivider = (
+ chargingStation: ChargingStation,
+ measurandType: MeterValueMeasurand,
+): void => {
+ if (isUndefined(chargingStation.powerDivider)) {
+ const errMsg = `MeterValues measurand ${
+ measurandType ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
+ }: powerDivider is undefined`;
+ logger.error(`${chargingStation.logPrefix()} ${errMsg}`);
+ throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, RequestCommand.METER_VALUES);
+ } else if (chargingStation?.powerDivider <= 0) {
+ const errMsg = `MeterValues measurand ${
+ measurandType ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
+ }: powerDivider have zero or below value ${chargingStation.powerDivider}`;
+ logger.error(`${chargingStation.logPrefix()} ${errMsg}`);
+ throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, RequestCommand.METER_VALUES);
+ }
+};
+
+const getLimitFromSampledValueTemplateCustomValue = (
+ value: string | undefined,
+ maxLimit: number,
+ minLimit: number,
+ options?: { limitationEnabled?: boolean; fallbackValue?: number; unitMultiplier?: number },
+): number => {
+ options = {
+ ...{
+ limitationEnabled: false,
+ unitMultiplier: 1,
+ fallbackValue: 0,
+ },
+ ...options,
+ };
+ const parsedValue = parseInt(value ?? '');
+ if (options?.limitationEnabled) {
+ return max(
+ min((!isNaN(parsedValue) ? parsedValue : Infinity) * options.unitMultiplier!, maxLimit),
+ minLimit,
+ );
+ }
+ return (!isNaN(parsedValue) ? parsedValue : options.fallbackValue!) * options.unitMultiplier!;
+};
+
+const getSampledValueTemplate = (
+ chargingStation: ChargingStation,
+ connectorId: number,
+ measurand: MeterValueMeasurand = MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER,
+ phase?: MeterValuePhase,
+): SampledValueTemplate | undefined => {
+ const onPhaseStr = phase ? `on phase ${phase} ` : '';
+ if (OCPPConstants.OCPP_MEASURANDS_SUPPORTED.includes(measurand) === false) {
+ logger.warn(
+ `${chargingStation.logPrefix()} Trying to get unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connector id ${connectorId}`,
+ );
+ return;
+ }
+ if (
+ measurand !== MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER &&
+ getConfigurationKey(
+ chargingStation,
+ StandardParametersKey.MeterValuesSampledData,
+ )?.value?.includes(measurand) === false
+ ) {
+ logger.debug(
+ `${chargingStation.logPrefix()} Trying to get MeterValues measurand '${measurand}' ${onPhaseStr}in template on connector id ${connectorId} not found in '${
+ StandardParametersKey.MeterValuesSampledData
+ }' OCPP parameter`,
+ );
+ return;
+ }
+ const sampledValueTemplates: SampledValueTemplate[] =
+ chargingStation.getConnectorStatus(connectorId)!.MeterValues;
+ for (
+ let index = 0;
+ isNotEmptyArray(sampledValueTemplates) === true && index < sampledValueTemplates.length;
+ index++
+ ) {
+ if (
+ OCPPConstants.OCPP_MEASURANDS_SUPPORTED.includes(
+ sampledValueTemplates[index]?.measurand ??
+ MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER,
+ ) === false
+ ) {
+ logger.warn(
+ `${chargingStation.logPrefix()} Unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connector id ${connectorId}`,
+ );
+ } else if (
+ phase &&
+ sampledValueTemplates[index]?.phase === phase &&
+ sampledValueTemplates[index]?.measurand === measurand &&
+ getConfigurationKey(
+ chargingStation,
+ StandardParametersKey.MeterValuesSampledData,
+ )?.value?.includes(measurand) === true
+ ) {
+ return sampledValueTemplates[index];
+ } else if (
+ !phase &&
+ !sampledValueTemplates[index]?.phase &&
+ sampledValueTemplates[index]?.measurand === measurand &&
+ getConfigurationKey(
+ chargingStation,
+ StandardParametersKey.MeterValuesSampledData,
+ )?.value?.includes(measurand) === true
+ ) {
+ return sampledValueTemplates[index];
+ } else if (
+ measurand === MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER &&
+ (!sampledValueTemplates[index]?.measurand ||
+ sampledValueTemplates[index]?.measurand === measurand)
+ ) {
+ return sampledValueTemplates[index];
+ }
+ }
+ if (measurand === MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER) {
+ const errorMsg = `Missing MeterValues for default measurand '${measurand}' in template on connector id ${connectorId}`;
+ logger.error(`${chargingStation.logPrefix()} ${errorMsg}`);
+ throw new BaseError(errorMsg);
+ }
+ logger.debug(
+ `${chargingStation.logPrefix()} No MeterValues for measurand '${measurand}' ${onPhaseStr}in template on connector id ${connectorId}`,
+ );
+};
+
+const buildSampledValue = (
+ sampledValueTemplate: SampledValueTemplate,
+ value: number,
+ context?: MeterValueContext,
+ phase?: MeterValuePhase,
+): SampledValue => {
+ const sampledValueContext = context ?? sampledValueTemplate?.context;
+ const sampledValueLocation =
+ sampledValueTemplate?.location ?? getMeasurandDefaultLocation(sampledValueTemplate.measurand!);
+ const sampledValuePhase = phase ?? sampledValueTemplate?.phase;
+ return {
+ ...(!isNullOrUndefined(sampledValueTemplate.unit) && {
+ unit: sampledValueTemplate.unit,
+ }),
+ ...(!isNullOrUndefined(sampledValueContext) && { context: sampledValueContext }),
+ ...(!isNullOrUndefined(sampledValueTemplate.measurand) && {
+ measurand: sampledValueTemplate.measurand,
+ }),
+ ...(!isNullOrUndefined(sampledValueLocation) && { location: sampledValueLocation }),
+ ...(!isNullOrUndefined(value) && { value: value.toString() }),
+ ...(!isNullOrUndefined(sampledValuePhase) && { phase: sampledValuePhase }),
+ } as SampledValue;
+};
+
+const getMeasurandDefaultLocation = (
+ measurandType: MeterValueMeasurand,
+): MeterValueLocation | undefined => {
+ switch (measurandType) {
+ case MeterValueMeasurand.STATE_OF_CHARGE:
+ return MeterValueLocation.EV;
+ }
+};
+
+// 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;
+// }
+// };
+
export class OCPPServiceUtils {
public static getMessageTypeString = getMessageTypeString;
public static sendAndSetConnectorStatus = sendAndSetConnectorStatus;
public static isIdTagAuthorized = isIdTagAuthorized;
+ public static buildTransactionEndMeterValue = buildTransactionEndMeterValue;
+ protected static getSampledValueTemplate = getSampledValueTemplate;
+ protected static buildSampledValue = buildSampledValue;
protected constructor() {
// This is intentional
}
}
- protected static getSampledValueTemplate(
- chargingStation: ChargingStation,
- connectorId: number,
- measurand: MeterValueMeasurand = MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER,
- phase?: MeterValuePhase,
- ): SampledValueTemplate | undefined {
- const onPhaseStr = phase ? `on phase ${phase} ` : '';
- if (OCPPConstants.OCPP_MEASURANDS_SUPPORTED.includes(measurand) === false) {
- logger.warn(
- `${chargingStation.logPrefix()} Trying to get unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connector id ${connectorId}`,
- );
- return;
- }
- if (
- measurand !== MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER &&
- getConfigurationKey(
- chargingStation,
- StandardParametersKey.MeterValuesSampledData,
- )?.value?.includes(measurand) === false
- ) {
- logger.debug(
- `${chargingStation.logPrefix()} Trying to get MeterValues measurand '${measurand}' ${onPhaseStr}in template on connector id ${connectorId} not found in '${
- StandardParametersKey.MeterValuesSampledData
- }' OCPP parameter`,
- );
- return;
- }
- const sampledValueTemplates: SampledValueTemplate[] =
- chargingStation.getConnectorStatus(connectorId)!.MeterValues;
- for (
- let index = 0;
- isNotEmptyArray(sampledValueTemplates) === true && index < sampledValueTemplates.length;
- index++
- ) {
- if (
- OCPPConstants.OCPP_MEASURANDS_SUPPORTED.includes(
- sampledValueTemplates[index]?.measurand ??
- MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER,
- ) === false
- ) {
- logger.warn(
- `${chargingStation.logPrefix()} Unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connector id ${connectorId}`,
- );
- } else if (
- phase &&
- sampledValueTemplates[index]?.phase === phase &&
- sampledValueTemplates[index]?.measurand === measurand &&
- getConfigurationKey(
- chargingStation,
- StandardParametersKey.MeterValuesSampledData,
- )?.value?.includes(measurand) === true
- ) {
- return sampledValueTemplates[index];
- } else if (
- !phase &&
- !sampledValueTemplates[index]?.phase &&
- sampledValueTemplates[index]?.measurand === measurand &&
- getConfigurationKey(
- chargingStation,
- StandardParametersKey.MeterValuesSampledData,
- )?.value?.includes(measurand) === true
- ) {
- return sampledValueTemplates[index];
- } else if (
- measurand === MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER &&
- (!sampledValueTemplates[index]?.measurand ||
- sampledValueTemplates[index]?.measurand === measurand)
- ) {
- return sampledValueTemplates[index];
- }
- }
- if (measurand === MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER) {
- const errorMsg = `Missing MeterValues for default measurand '${measurand}' in template on connector id ${connectorId}`;
- logger.error(`${chargingStation.logPrefix()} ${errorMsg}`);
- throw new BaseError(errorMsg);
- }
- logger.debug(
- `${chargingStation.logPrefix()} No MeterValues for measurand '${measurand}' ${onPhaseStr}in template on connector id ${connectorId}`,
- );
- }
-
- protected static getLimitFromSampledValueTemplateCustomValue(
- value: string | undefined,
- maxLimit: number,
- minLimit: number,
- options?: { limitationEnabled?: boolean; fallbackValue?: number; unitMultiplier?: number },
- ): number {
- options = {
- ...{
- limitationEnabled: false,
- unitMultiplier: 1,
- fallbackValue: 0,
- },
- ...options,
- };
- const parsedValue = parseInt(value ?? '');
- if (options?.limitationEnabled) {
- return max(
- min((!isNaN(parsedValue) ? parsedValue : Infinity) * options.unitMultiplier!, maxLimit),
- minLimit,
- );
- }
- return (!isNaN(parsedValue) ? parsedValue : options.fallbackValue!) * options.unitMultiplier!;
- }
-
private static logPrefix = (
ocppVersion: OCPPVersion,
moduleName?: string,
export { OCPP16IncomingRequestService } from './1.6/OCPP16IncomingRequestService';
export { OCPP16RequestService } from './1.6/OCPP16RequestService';
export { OCPP16ResponseService } from './1.6/OCPP16ResponseService';
-// FIXME: shall not be exported
-export { OCPP16ServiceUtils } from './1.6/OCPP16ServiceUtils';
export { OCPP20IncomingRequestService } from './2.0/OCPP20IncomingRequestService';
export { OCPP20RequestService } from './2.0/OCPP20RequestService';
export { OCPP20ResponseService } from './2.0/OCPP20ResponseService';
export { OCPPIncomingRequestService } from './OCPPIncomingRequestService';
export { OCPPRequestService } from './OCPPRequestService';
export {
+ buildMeterValue,
buildStatusNotificationRequest,
+ buildTransactionEndMeterValue,
getMessageTypeString,
isIdTagAuthorized,
sendAndSetConnectorStatus,
} from './WorkerBroadcastChannel';
export {
type ChangeConfigurationRequest,
- type ClearChargingProfileRequest,
type GetConfigurationRequest,
type GetDiagnosticsRequest,
OCPP16AvailabilityType,
type OCPP16CancelReservationRequest,
type OCPP16ChangeAvailabilityRequest,
type OCPP16ClearCacheRequest,
+ type OCPP16ClearChargingProfileRequest,
type OCPP16DataTransferRequest,
OCPP16DataTransferVendorId,
type OCPP16DiagnosticsStatusNotificationRequest,
} from './ocpp/1.6/Requests';
export {
type ChangeConfigurationResponse,
- type ClearChargingProfileResponse,
type GetConfigurationResponse,
type GetDiagnosticsResponse,
type OCPP16BootNotificationResponse,
type OCPP16ChangeAvailabilityResponse,
+ type OCPP16ClearChargingProfileResponse,
type OCPP16DataTransferResponse,
OCPP16DataTransferStatus,
type OCPP16DiagnosticsStatusNotificationResponse,
} from './MeasurandPerPhaseSampledValueTemplates';
export type { MeasurandValues } from './MeasurandValues';
export { MessageType } from './ocpp/MessageType';
-export { type MeterValue, MeterValueMeasurand, MeterValuePhase } from './ocpp/MeterValues';
export {
+ type MeterValue,
MeterValueContext,
MeterValueLocation,
+ MeterValueMeasurand,
+ MeterValuePhase,
MeterValueUnit,
+ type SampledValue,
+} from './ocpp/MeterValues';
+export {
type OCPP16MeterValue,
+ OCPP16MeterValueContext,
+ OCPP16MeterValueLocation,
OCPP16MeterValueMeasurand,
OCPP16MeterValuePhase,
+ OCPP16MeterValueUnit,
type OCPP16MeterValuesRequest,
type OCPP16MeterValuesResponse,
type OCPP16SampledValue,
import type { EmptyObject } from '../../EmptyObject';
import type { JsonObject } from '../../JsonType';
-export enum MeterValueUnit {
+export enum OCPP16MeterValueUnit {
WATT_HOUR = 'Wh',
KILO_WATT_HOUR = 'kWh',
VAR_HOUR = 'varh',
PERCENT = 'Percent',
}
-export enum MeterValueContext {
+export enum OCPP16MeterValueContext {
INTERRUPTION_BEGIN = 'Interruption.Begin',
INTERRUPTION_END = 'Interruption.End',
OTHER = 'Other',
VOLTAGE = 'Voltage',
}
-export enum MeterValueLocation {
+export enum OCPP16MeterValueLocation {
BODY = 'Body',
CABLE = 'Cable',
EV = 'EV',
L3_L1 = 'L3-L1',
}
-enum MeterValueFormat {
+enum OCPP16MeterValueFormat {
RAW = 'Raw',
SIGNED_DATA = 'SignedData',
}
export interface OCPP16SampledValue extends JsonObject {
value: string;
- unit?: MeterValueUnit;
- context?: MeterValueContext;
+ unit?: OCPP16MeterValueUnit;
+ context?: OCPP16MeterValueContext;
measurand?: OCPP16MeterValueMeasurand;
phase?: OCPP16MeterValuePhase;
- location?: MeterValueLocation;
- format?: MeterValueFormat;
+ location?: OCPP16MeterValueLocation;
+ format?: OCPP16MeterValueFormat;
}
export interface OCPP16MeterValue extends JsonObject {
type: OCPP16AvailabilityType;
}
-export interface ClearChargingProfileRequest extends JsonObject {
+export interface OCPP16ClearChargingProfileRequest extends JsonObject {
id?: number;
connectorId?: number;
chargingProfilePurpose?: OCPP16ChargingProfilePurposeType;
UNKNOWN = 'Unknown',
}
-export interface ClearChargingProfileResponse extends JsonObject {
+export interface OCPP16ClearChargingProfileResponse extends JsonObject {
status: OCPP16ClearChargingProfileStatus;
}
import {
type OCPP16MeterValue,
+ OCPP16MeterValueContext,
+ OCPP16MeterValueLocation,
OCPP16MeterValueMeasurand,
OCPP16MeterValuePhase,
+ OCPP16MeterValueUnit,
type OCPP16SampledValue,
} from './1.6/MeterValues';
+export const MeterValueUnit = {
+ ...OCPP16MeterValueUnit,
+} as const;
+export type MeterValueUnit = OCPP16MeterValueUnit;
+
+export const MeterValueContext = {
+ ...OCPP16MeterValueContext,
+} as const;
+export type MeterValueContext = OCPP16MeterValueContext;
+
export const MeterValueMeasurand = {
...OCPP16MeterValueMeasurand,
} as const;
export type MeterValueMeasurand = OCPP16MeterValueMeasurand;
+export const MeterValueLocation = {
+ ...OCPP16MeterValueLocation,
+} as const;
+export type MeterValueLocation = OCPP16MeterValueLocation;
+
export const MeterValuePhase = {
...OCPP16MeterValuePhase,
} as const;
hasBin: true
dependencies:
caniuse-lite: 1.0.30001566
- electron-to-chromium: 1.4.601
+ electron-to-chromium: 1.4.603
node-releases: 2.0.14
update-browserslist-db: 1.0.13(browserslist@4.22.2)
dev: true
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
dev: false
- /electron-to-chromium@1.4.601:
- resolution: {integrity: sha512-SpwUMDWe9tQu8JX5QCO1+p/hChAi9AE9UpoC3rcHVc+gdCGlbT3SGb5I1klgb952HRIyvt9wZhSz9bNBYz9swA==}
+ /electron-to-chromium@1.4.603:
+ resolution: {integrity: sha512-Dvo5OGjnl7AZTU632dFJtWj0uJK835eeOVQIuRcmBmsFsTNn3cL05FqOyHAfGQDIoHfLhyJ1Tya3PJ0ceMz54g==}
dev: true
/emoji-regex@8.0.0: