X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2Focpp%2F1.6%2FOCPP16ServiceUtils.ts;h=c73c32dc2ce940f9c017550183a44dd45a3a7a43;hb=6e3d9d04815abfe85d31735fc38bdf0253d85026;hp=de9c8548926477a3c73f8de0bd3c5c0bced4aa7c;hpb=7164966d863b4539243b473c5e2e9d22fb9b5fd1;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 de9c8548..c73c32dc 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts @@ -1,43 +1,35 @@ // 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, -} from '../../../types/ocpp/1.6/Configuration'; +import { type ChargingStation, ChargingStationUtils } from '../../../charging-station'; +import { OCPPError } from '../../../exception'; import { + CurrentType, + ErrorType, + type JsonType, + type MeasurandPerPhaseSampledValueTemplates, + type MeasurandValues, MeterValueContext, MeterValueLocation, MeterValueUnit, + OCPP16AuthorizationStatus, + type OCPP16AuthorizeRequest, + type OCPP16AuthorizeResponse, + type OCPP16ChargingProfile, + type OCPP16IncomingRequestCommand, type OCPP16MeterValue, OCPP16MeterValueMeasurand, OCPP16MeterValuePhase, - type OCPP16SampledValue, -} from '../../../types/ocpp/1.6/MeterValues'; -import { - 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'; + type OCPP16SampledValue, + OCPP16StandardParametersKey, + type OCPP16SupportedFeatureProfiles, + OCPPVersion, + type SampledValueTemplate, + Voltage, +} from '../../../types'; +import { ACElectricUtils, Constants, DCElectricUtils, Utils, logger } from '../../../utils'; import { OCPPServiceUtils } from '../OCPPServiceUtils'; export class OCPP16ServiceUtils extends OCPPServiceUtils { @@ -76,24 +68,32 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { OCPP16MeterValueMeasurand.STATE_OF_CHARGE ); if (socSampledValueTemplate) { + const socMaximumValue = 100; + const socMinimumValue = socSampledValueTemplate.minimumValue ?? 0; const socSampledValueTemplateValue = socSampledValueTemplate.value ? Utils.getRandomFloatFluctuatedRounded( parseInt(socSampledValueTemplate.value), socSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT ) - : Utils.getRandomInteger(100); + : Utils.getRandomInteger(socMaximumValue, socMinimumValue); meterValue.sampledValue.push( OCPP16ServiceUtils.buildSampledValue(socSampledValueTemplate, socSampledValueTemplateValue) ); const sampledValuesIndex = meterValue.sampledValue.length - 1; - if (Utils.convertToInt(meterValue.sampledValue[sampledValuesIndex].value) > 100 || debug) { + if ( + Utils.convertToInt(meterValue.sampledValue[sampledValuesIndex].value) > socMaximumValue || + Utils.convertToInt(meterValue.sampledValue[sampledValuesIndex].value) < socMinimumValue || + debug + ) { logger.error( `${chargingStation.logPrefix()} MeterValues measurand ${ meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER - }: connectorId ${connectorId}, transaction ${connector?.transactionId}, value: ${ + }: connector id ${connectorId}, transaction id ${ + connector?.transactionId + }, value: ${socMinimumValue}/${ meterValue.sampledValue[sampledValuesIndex].value - }/100` + }/${socMaximumValue}}` ); } } @@ -249,6 +249,10 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { const connectorMaximumPowerPerPhase = Math.round( connectorMaximumAvailablePower / chargingStation.getNumberOfPhases() ); + const connectorMinimumPower = Math.round(powerSampledValueTemplate.minimumValue) ?? 0; + const connectorMinimumPowerPerPhase = Math.round( + connectorMinimumPower / chargingStation.getNumberOfPhases() + ); switch (chargingStation.getCurrentOutType()) { case CurrentType.AC: if (chargingStation.getNumberOfPhases() === 3) { @@ -299,15 +303,24 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { powerMeasurandValues.L1 = phase1FluctuatedValue ?? defaultFluctuatedPowerPerPhase ?? - Utils.getRandomFloatRounded(connectorMaximumPowerPerPhase / unitDivider); + Utils.getRandomFloatRounded( + connectorMaximumPowerPerPhase / unitDivider, + connectorMinimumPowerPerPhase / unitDivider + ); powerMeasurandValues.L2 = phase2FluctuatedValue ?? defaultFluctuatedPowerPerPhase ?? - Utils.getRandomFloatRounded(connectorMaximumPowerPerPhase / unitDivider); + Utils.getRandomFloatRounded( + connectorMaximumPowerPerPhase / unitDivider, + connectorMinimumPowerPerPhase / unitDivider + ); powerMeasurandValues.L3 = phase3FluctuatedValue ?? defaultFluctuatedPowerPerPhase ?? - Utils.getRandomFloatRounded(connectorMaximumPowerPerPhase / unitDivider); + Utils.getRandomFloatRounded( + connectorMaximumPowerPerPhase / unitDivider, + connectorMinimumPowerPerPhase / unitDivider + ); } else { powerMeasurandValues.L1 = powerSampledValueTemplate.value ? Utils.getRandomFloatFluctuatedRounded( @@ -319,7 +332,10 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { powerSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT ) - : Utils.getRandomFloatRounded(connectorMaximumPower / unitDivider); + : Utils.getRandomFloatRounded( + connectorMaximumPower / unitDivider, + connectorMinimumPower / unitDivider + ); powerMeasurandValues.L2 = 0; powerMeasurandValues.L3 = 0; } @@ -339,7 +355,10 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { powerSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT ) - : Utils.getRandomFloatRounded(connectorMaximumPower / unitDivider); + : Utils.getRandomFloatRounded( + connectorMaximumPower / unitDivider, + connectorMinimumPower / unitDivider + ); break; default: logger.error(`${chargingStation.logPrefix()} ${errMsg}`); @@ -353,16 +372,21 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { ); const sampledValuesIndex = meterValue.sampledValue.length - 1; const connectorMaximumPowerRounded = Utils.roundTo(connectorMaximumPower / unitDivider, 2); + const connectorMinimumPowerRounded = Utils.roundTo(connectorMinimumPower / unitDivider, 2); if ( Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > connectorMaximumPowerRounded || + Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) < + connectorMinimumPowerRounded || debug ) { logger.error( `${chargingStation.logPrefix()} MeterValues measurand ${ meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER - }: connectorId ${connectorId}, transaction ${connector?.transactionId}, value: ${ + }: connector id ${connectorId}, transaction id ${ + connector?.transactionId + }, value: ${connectorMinimumPowerRounded}/${ meterValue.sampledValue[sampledValuesIndex].value }/${connectorMaximumPowerRounded}` ); @@ -387,9 +411,15 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { connectorMaximumPowerPerPhase / unitDivider, 2 ); + const connectorMinimumPowerPerPhaseRounded = Utils.roundTo( + connectorMinimumPowerPerPhase / unitDivider, + 2 + ); if ( Utils.convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) > connectorMaximumPowerPerPhaseRounded || + Utils.convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) < + connectorMinimumPowerPerPhaseRounded || debug ) { logger.error( @@ -398,7 +428,9 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER }: phase ${ meterValue.sampledValue[sampledValuesPerPhaseIndex].phase - }, connectorId ${connectorId}, transaction ${connector?.transactionId}, value: ${ + }, connector id ${connectorId}, transaction id ${ + connector?.transactionId + }, value: ${connectorMinimumPowerPerPhaseRounded}/${ meterValue.sampledValue[sampledValuesPerPhaseIndex].value }/${connectorMaximumPowerPerPhaseRounded}` ); @@ -451,6 +483,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { const currentMeasurandValues: MeasurandValues = {} as MeasurandValues; const connectorMaximumAvailablePower = chargingStation.getConnectorMaximumAvailablePower(connectorId); + const connectorMinimumAmperage = currentSampledValueTemplate.minimumValue ?? 0; let connectorMaximumAmperage: number; switch (chargingStation.getCurrentOutType()) { case CurrentType.AC: @@ -507,15 +540,15 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { currentMeasurandValues.L1 = phase1FluctuatedValue ?? defaultFluctuatedAmperagePerPhase ?? - Utils.getRandomFloatRounded(connectorMaximumAmperage); + Utils.getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage); currentMeasurandValues.L2 = phase2FluctuatedValue ?? defaultFluctuatedAmperagePerPhase ?? - Utils.getRandomFloatRounded(connectorMaximumAmperage); + Utils.getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage); currentMeasurandValues.L3 = phase3FluctuatedValue ?? defaultFluctuatedAmperagePerPhase ?? - Utils.getRandomFloatRounded(connectorMaximumAmperage); + Utils.getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage); } else { currentMeasurandValues.L1 = currentSampledValueTemplate.value ? Utils.getRandomFloatFluctuatedRounded( @@ -527,7 +560,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { currentSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT ) - : Utils.getRandomFloatRounded(connectorMaximumAmperage); + : Utils.getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage); currentMeasurandValues.L2 = 0; currentMeasurandValues.L3 = 0; } @@ -552,7 +585,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { currentSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT ) - : Utils.getRandomFloatRounded(connectorMaximumAmperage); + : Utils.getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage); break; default: logger.error(`${chargingStation.logPrefix()} ${errMsg}`); @@ -568,13 +601,17 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { if ( Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > connectorMaximumAmperage || + Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) < + connectorMinimumAmperage || debug ) { logger.error( `${chargingStation.logPrefix()} MeterValues measurand ${ meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER - }: connectorId ${connectorId}, transaction ${connector?.transactionId}, value: ${ + }: connector id ${connectorId}, transaction id ${ + connector?.transactionId + }, value: ${connectorMinimumAmperage}/${ meterValue.sampledValue[sampledValuesIndex].value }/${connectorMaximumAmperage}` ); @@ -598,6 +635,8 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { if ( Utils.convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) > connectorMaximumAmperage || + Utils.convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) < + connectorMinimumAmperage || debug ) { logger.error( @@ -606,7 +645,9 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER }: phase ${ meterValue.sampledValue[sampledValuesPerPhaseIndex].phase - }, connectorId ${connectorId}, transaction ${connector?.transactionId}, value: ${ + }, connector id ${connectorId}, transaction id ${ + connector?.transactionId + }, value: ${connectorMinimumAmperage}/${ meterValue.sampledValue[sampledValuesPerPhaseIndex].value }/${connectorMaximumAmperage}` ); @@ -675,7 +716,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { `${chargingStation.logPrefix()} MeterValues measurand ${ meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER - }: connectorId ${connectorId}, transaction ${ + }: connector id ${connectorId}, transaction id ${ connector?.transactionId }, value: ${energyValueRounded}/${connectorMaximumEnergyRounded}, duration: ${Utils.roundTo( interval / (3600 * 1000), @@ -756,7 +797,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { 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.logPrefix()} Trying to set a charging profile on connector id ${connectorId} with an uninitialized charging profiles array attribute, applying deferred initialization` ); chargingStation.getConnectorStatus(connectorId).chargingProfiles = []; } @@ -764,12 +805,12 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { 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.logPrefix()} Trying to set a charging profile on connector id ${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)) { + if (Utils.isNotEmptyArray(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles)) { chargingStation .getConnectorStatus(connectorId) ?.chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => { @@ -786,13 +827,43 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { !cpReplaced && chargingStation.getConnectorStatus(connectorId)?.chargingProfiles?.push(cp); } - public static parseJsonSchemaFile(relativePath: string): JSONSchemaType { + public static parseJsonSchemaFile( + relativePath: string, + moduleName?: string, + methodName?: string + ): JSONSchemaType { return super.parseJsonSchemaFile( - path.resolve(path.dirname(fileURLToPath(import.meta.url)), relativePath), - OCPPVersion.VERSION_16 + relativePath, + OCPPVersion.VERSION_16, + moduleName, + methodName ); } + public static async isIdTagAuthorized( + chargingStation: ChargingStation, + connectorId: number, + idTag: string, + parentIdTag?: string + ): Promise { + let authorized = false; + const connectorStatus = chargingStation.getConnectorStatus(connectorId); + if (OCPP16ServiceUtils.isIdTagLocalAuthorized(chargingStation, idTag)) { + connectorStatus.localAuthorizeIdTag = idTag; + connectorStatus.idTagLocalAuthorized = true; + authorized = true; + } else if (chargingStation.getMustAuthorizeAtRemoteStart() === true) { + connectorStatus.authorizeIdTag = idTag; + authorized = await OCPP16ServiceUtils.isIdTagRemoteAuthorized(chargingStation, idTag); + } else { + logger.warn( + `${chargingStation.logPrefix()} The charging station configuration expects authorize at + remote start transaction but local authorization or authorize isn't enabled` + ); + } + return authorized; + } + private static buildSampledValue( sampledValueTemplate: SampledValueTemplate, value: number, @@ -868,4 +939,30 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { return MeterValueUnit.VOLT; } } + + private static isIdTagLocalAuthorized(chargingStation: ChargingStation, idTag: string): boolean { + return ( + chargingStation.getLocalAuthListEnabled() === true && + chargingStation.hasIdTags() === true && + Utils.isNotEmptyString( + chargingStation.idTagsCache + .getIdTags(ChargingStationUtils.getIdTagsFile(chargingStation.stationInfo)) + ?.find((tag) => tag === idTag) + ) + ); + } + + private static async isIdTagRemoteAuthorized( + chargingStation: ChargingStation, + idTag: string + ): Promise { + const authorizeResponse: OCPP16AuthorizeResponse = + await chargingStation.ocppRequestService.requestHandler< + OCPP16AuthorizeRequest, + OCPP16AuthorizeResponse + >(chargingStation, OCPP16RequestCommand.AUTHORIZE, { + idTag: idTag, + }); + return authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED; + } }