X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2Focpp%2F1.6%2FOCPP16ServiceUtils.ts;h=de9c8548926477a3c73f8de0bd3c5c0bced4aa7c;hb=7164966d863b4539243b473c5e2e9d22fb9b5fd1;hp=f5c4df7321b7a8c60be62ead9b9df9bf0d5a125c;hpb=83e00df1c1ba02de8b637ca4cb0464eb909ebb18;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts index f5c4df73..de9c8548 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts @@ -1,12 +1,19 @@ -// Partial Copyright Jerome Benoit. 2021. All Rights Reserved. +// Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. + +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import type { JSONSchemaType } from 'ajv'; import OCPPError from '../../../exception/OCPPError'; import { CurrentType, Voltage } from '../../../types/ChargingStationTemplate'; +import type { JsonType } from '../../../types/JsonType'; import type { MeasurandPerPhaseSampledValueTemplates, SampledValueTemplate, } from '../../../types/MeasurandPerPhaseSampledValueTemplates'; import type { MeasurandValues } from '../../../types/MeasurandValues'; +import type { OCPP16ChargingProfile } from '../../../types/ocpp/1.6/ChargingProfile'; import { OCPP16StandardParametersKey, OCPP16SupportedFeatureProfiles, @@ -15,22 +22,22 @@ import { MeterValueContext, MeterValueLocation, MeterValueUnit, - OCPP16MeterValue, + type OCPP16MeterValue, OCPP16MeterValueMeasurand, OCPP16MeterValuePhase, - OCPP16SampledValue, + type OCPP16SampledValue, } from '../../../types/ocpp/1.6/MeterValues'; import { - OCPP16IncomingRequestCommand, + type OCPP16IncomingRequestCommand, OCPP16RequestCommand, } from '../../../types/ocpp/1.6/Requests'; import { ErrorType } from '../../../types/ocpp/ErrorType'; +import { OCPPVersion } from '../../../types/ocpp/OCPPVersion'; import Constants from '../../../utils/Constants'; import { ACElectricUtils, DCElectricUtils } from '../../../utils/ElectricUtils'; import logger from '../../../utils/Logger'; import Utils from '../../../utils/Utils'; import type ChargingStation from '../../ChargingStation'; -import { ChargingStationUtils } from '../../ChargingStationUtils'; import { OCPPServiceUtils } from '../OCPPServiceUtils'; export class OCPP16ServiceUtils extends OCPPServiceUtils { @@ -58,12 +65,12 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { debug = false ): OCPP16MeterValue { const meterValue: OCPP16MeterValue = { - timestamp: new Date().toISOString(), + timestamp: new Date(), sampledValue: [], }; const connector = chargingStation.getConnectorStatus(connectorId); // SoC measurand - const socSampledValueTemplate = ChargingStationUtils.getSampledValueTemplate( + const socSampledValueTemplate = OCPP16ServiceUtils.getSampledValueTemplate( chargingStation, connectorId, OCPP16MeterValueMeasurand.STATE_OF_CHARGE @@ -84,14 +91,14 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { `${chargingStation.logPrefix()} MeterValues measurand ${ meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER - }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${ + }: connectorId ${connectorId}, transaction ${connector?.transactionId}, value: ${ meterValue.sampledValue[sampledValuesIndex].value }/100` ); } } // Voltage measurand - const voltageSampledValueTemplate = ChargingStationUtils.getSampledValueTemplate( + const voltageSampledValueTemplate = OCPP16ServiceUtils.getSampledValueTemplate( chargingStation, connectorId, OCPP16MeterValueMeasurand.VOLTAGE @@ -121,7 +128,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { ) { const phaseLineToNeutralValue = `L${phase}-N`; const voltagePhaseLineToNeutralSampledValueTemplate = - ChargingStationUtils.getSampledValueTemplate( + OCPP16ServiceUtils.getSampledValueTemplate( chargingStation, connectorId, OCPP16MeterValueMeasurand.VOLTAGE, @@ -145,7 +152,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { OCPP16ServiceUtils.buildSampledValue( voltagePhaseLineToNeutralSampledValueTemplate ?? voltageSampledValueTemplate, voltagePhaseLineToNeutralMeasurandValue ?? voltageMeasurandValue, - null, + undefined, phaseLineToNeutralValue as OCPP16MeterValuePhase ) ); @@ -156,7 +163,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { : chargingStation.getNumberOfPhases() }`; const voltagePhaseLineToLineSampledValueTemplate = - ChargingStationUtils.getSampledValueTemplate( + OCPP16ServiceUtils.getSampledValueTemplate( chargingStation, connectorId, OCPP16MeterValueMeasurand.VOLTAGE, @@ -184,7 +191,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { OCPP16ServiceUtils.buildSampledValue( voltagePhaseLineToLineSampledValueTemplate ?? voltageSampledValueTemplate, voltagePhaseLineToLineMeasurandValue ?? defaultVoltagePhaseLineToLineMeasurandValue, - null, + undefined, phaseLineToLineValue as OCPP16MeterValuePhase ) ); @@ -192,7 +199,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { } } // Power.Active.Import measurand - const powerSampledValueTemplate = ChargingStationUtils.getSampledValueTemplate( + const powerSampledValueTemplate = OCPP16ServiceUtils.getSampledValueTemplate( chargingStation, connectorId, OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT @@ -200,19 +207,19 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { let powerPerPhaseSampledValueTemplates: MeasurandPerPhaseSampledValueTemplates = {}; if (chargingStation.getNumberOfPhases() === 3) { powerPerPhaseSampledValueTemplates = { - L1: ChargingStationUtils.getSampledValueTemplate( + L1: OCPP16ServiceUtils.getSampledValueTemplate( chargingStation, connectorId, OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT, OCPP16MeterValuePhase.L1_N ), - L2: ChargingStationUtils.getSampledValueTemplate( + L2: OCPP16ServiceUtils.getSampledValueTemplate( chargingStation, connectorId, OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT, OCPP16MeterValuePhase.L2_N ), - L3: ChargingStationUtils.getSampledValueTemplate( + L3: OCPP16ServiceUtils.getSampledValueTemplate( chargingStation, connectorId, OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT, @@ -355,7 +362,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { `${chargingStation.logPrefix()} MeterValues measurand ${ meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER - }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${ + }: connectorId ${connectorId}, transaction ${connector?.transactionId}, value: ${ meterValue.sampledValue[sampledValuesIndex].value }/${connectorMaximumPowerRounded}` ); @@ -371,7 +378,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { (powerPerPhaseSampledValueTemplates[`L${phase}`] as SampledValueTemplate) ?? powerSampledValueTemplate, powerMeasurandValues[`L${phase}`] as number, - null, + undefined, phaseValue as OCPP16MeterValuePhase ) ); @@ -391,7 +398,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER }: phase ${ meterValue.sampledValue[sampledValuesPerPhaseIndex].phase - }, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${ + }, connectorId ${connectorId}, transaction ${connector?.transactionId}, value: ${ meterValue.sampledValue[sampledValuesPerPhaseIndex].value }/${connectorMaximumPowerPerPhaseRounded}` ); @@ -399,7 +406,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { } } // Current.Import measurand - const currentSampledValueTemplate = ChargingStationUtils.getSampledValueTemplate( + const currentSampledValueTemplate = OCPP16ServiceUtils.getSampledValueTemplate( chargingStation, connectorId, OCPP16MeterValueMeasurand.CURRENT_IMPORT @@ -407,19 +414,19 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { let currentPerPhaseSampledValueTemplates: MeasurandPerPhaseSampledValueTemplates = {}; if (chargingStation.getNumberOfPhases() === 3) { currentPerPhaseSampledValueTemplates = { - L1: ChargingStationUtils.getSampledValueTemplate( + L1: OCPP16ServiceUtils.getSampledValueTemplate( chargingStation, connectorId, OCPP16MeterValueMeasurand.CURRENT_IMPORT, OCPP16MeterValuePhase.L1 ), - L2: ChargingStationUtils.getSampledValueTemplate( + L2: OCPP16ServiceUtils.getSampledValueTemplate( chargingStation, connectorId, OCPP16MeterValueMeasurand.CURRENT_IMPORT, OCPP16MeterValuePhase.L2 ), - L3: ChargingStationUtils.getSampledValueTemplate( + L3: OCPP16ServiceUtils.getSampledValueTemplate( chargingStation, connectorId, OCPP16MeterValueMeasurand.CURRENT_IMPORT, @@ -567,7 +574,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { `${chargingStation.logPrefix()} MeterValues measurand ${ meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER - }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${ + }: connectorId ${connectorId}, transaction ${connector?.transactionId}, value: ${ meterValue.sampledValue[sampledValuesIndex].value }/${connectorMaximumAmperage}` ); @@ -583,7 +590,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { (currentPerPhaseSampledValueTemplates[phaseValue] as SampledValueTemplate) ?? currentSampledValueTemplate, currentMeasurandValues[phaseValue] as number, - null, + undefined, phaseValue as OCPP16MeterValuePhase ) ); @@ -599,7 +606,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER }: phase ${ meterValue.sampledValue[sampledValuesPerPhaseIndex].phase - }, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${ + }, connectorId ${connectorId}, transaction ${connector?.transactionId}, value: ${ meterValue.sampledValue[sampledValuesPerPhaseIndex].value }/${connectorMaximumAmperage}` ); @@ -607,7 +614,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { } } // Energy.Active.Import.Register measurand (default) - const energySampledValueTemplate = ChargingStationUtils.getSampledValueTemplate( + const energySampledValueTemplate = OCPP16ServiceUtils.getSampledValueTemplate( chargingStation, connectorId ); @@ -641,9 +648,9 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { // Persist previous value on connector if ( connector && - !Utils.isNullOrUndefined(connector.energyActiveImportRegisterValue) && + Utils.isNullOrUndefined(connector.energyActiveImportRegisterValue) === false && connector.energyActiveImportRegisterValue >= 0 && - !Utils.isNullOrUndefined(connector.transactionEnergyActiveImportRegisterValue) && + Utils.isNullOrUndefined(connector.transactionEnergyActiveImportRegisterValue) === false && connector.transactionEnergyActiveImportRegisterValue >= 0 ) { connector.energyActiveImportRegisterValue += energyValueRounded; @@ -669,7 +676,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER }: connectorId ${connectorId}, transaction ${ - connector.transactionId + connector?.transactionId }, value: ${energyValueRounded}/${connectorMaximumEnergyRounded}, duration: ${Utils.roundTo( interval / (3600 * 1000), 4 @@ -686,11 +693,11 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { meterStart: number ): OCPP16MeterValue { const meterValue: OCPP16MeterValue = { - timestamp: new Date().toISOString(), + timestamp: new Date(), sampledValue: [], }; // Energy.Active.Import.Register measurand (default) - const sampledValueTemplate = ChargingStationUtils.getSampledValueTemplate( + const sampledValueTemplate = OCPP16ServiceUtils.getSampledValueTemplate( chargingStation, connectorId ); @@ -698,7 +705,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { meterValue.sampledValue.push( OCPP16ServiceUtils.buildSampledValue( sampledValueTemplate, - Utils.roundTo(meterStart / unitDivider, 4), + Utils.roundTo((meterStart ?? 0) / unitDivider, 4), MeterValueContext.TRANSACTION_BEGIN ) ); @@ -711,11 +718,11 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { meterStop: number ): OCPP16MeterValue { const meterValue: OCPP16MeterValue = { - timestamp: new Date().toISOString(), + timestamp: new Date(), sampledValue: [], }; // Energy.Active.Import.Register measurand (default) - const sampledValueTemplate = ChargingStationUtils.getSampledValueTemplate( + const sampledValueTemplate = OCPP16ServiceUtils.getSampledValueTemplate( chargingStation, connectorId ); @@ -723,7 +730,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { meterValue.sampledValue.push( OCPP16ServiceUtils.buildSampledValue( sampledValueTemplate, - Utils.roundTo(meterStop / unitDivider, 4), + Utils.roundTo((meterStop ?? 0) / unitDivider, 4), MeterValueContext.TRANSACTION_END ) ); @@ -740,6 +747,52 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { return meterValues; } + public static setChargingProfile( + chargingStation: ChargingStation, + connectorId: number, + cp: OCPP16ChargingProfile + ): void { + if ( + Utils.isNullOrUndefined(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles) + ) { + logger.error( + `${chargingStation.logPrefix()} Trying to set a charging profile on connectorId ${connectorId} with an uninitialized charging profiles array attribute, applying deferred initialization` + ); + chargingStation.getConnectorStatus(connectorId).chargingProfiles = []; + } + if ( + Array.isArray(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles) === false + ) { + logger.error( + `${chargingStation.logPrefix()} Trying to set a charging profile on connectorId ${connectorId} with an improper attribute type for the charging profiles array, applying proper type initialization` + ); + chargingStation.getConnectorStatus(connectorId).chargingProfiles = []; + } + let cpReplaced = false; + if (!Utils.isEmptyArray(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles)) { + chargingStation + .getConnectorStatus(connectorId) + ?.chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => { + if ( + chargingProfile.chargingProfileId === cp.chargingProfileId || + (chargingProfile.stackLevel === cp.stackLevel && + chargingProfile.chargingProfilePurpose === cp.chargingProfilePurpose) + ) { + chargingStation.getConnectorStatus(connectorId).chargingProfiles[index] = cp; + cpReplaced = true; + } + }); + } + !cpReplaced && chargingStation.getConnectorStatus(connectorId)?.chargingProfiles?.push(cp); + } + + public static parseJsonSchemaFile(relativePath: string): JSONSchemaType { + return super.parseJsonSchemaFile( + path.resolve(path.dirname(fileURLToPath(import.meta.url)), relativePath), + OCPPVersion.VERSION_16 + ); + } + private static buildSampledValue( sampledValueTemplate: SampledValueTemplate, value: number,