X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2Focpp%2F1.6%2FOCPP16ServiceUtils.ts;h=02cbac8d48ae1710c638e592ea4f9b5569ea4f56;hb=74ed61d9509bdf19991b404c0db95af2f7302c70;hp=b70be6fadc8170728fcd06abcda9d88e6e73afc7;hpb=3b0ed03465692b766a2112aabbd3241d5bdc702d;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 b70be6fa..02cbac8d 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts @@ -1,9 +1,21 @@ // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. import type { JSONSchemaType } from 'ajv'; +import { + addSeconds, + areIntervalsOverlapping, + differenceInSeconds, + isAfter, + isBefore, + isWithinInterval, +} from 'date-fns'; import { OCPP16Constants } from './OCPP16Constants'; -import { type ChargingStation, hasFeatureProfile } from '../../../charging-station'; +import { + type ChargingStation, + hasFeatureProfile, + hasReservationExpired, +} from '../../../charging-station'; import { OCPPError } from '../../../exception'; import { type ClearChargingProfileRequest, @@ -21,6 +33,7 @@ import { type OCPP16ChangeAvailabilityResponse, OCPP16ChargePointStatus, type OCPP16ChargingProfile, + type OCPP16ChargingSchedule, type OCPP16IncomingRequestCommand, type OCPP16MeterValue, OCPP16MeterValueMeasurand, @@ -110,7 +123,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER }: connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${socMinimumValue}/${ meterValue.sampledValue[sampledValuesIndex].value - }/${socMaximumValue}}`, + }/${socMaximumValue}`, ); } } @@ -123,7 +136,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { if (voltageSampledValueTemplate) { const voltageSampledValueTemplateValue = voltageSampledValueTemplate.value ? parseInt(voltageSampledValueTemplate.value) - : chargingStation.getVoltageOut(); + : chargingStation.stationInfo.voltageOut!; const fluctuationPercent = voltageSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT; const voltageMeasurandValue = getRandomFloatFluctuatedRounded( @@ -132,7 +145,8 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { ); if ( chargingStation.getNumberOfPhases() !== 3 || - (chargingStation.getNumberOfPhases() === 3 && chargingStation.getMainVoltageMeterValues()) + (chargingStation.getNumberOfPhases() === 3 && + chargingStation.stationInfo?.mainVoltageMeterValues) ) { meterValue.sampledValue.push( OCPP16ServiceUtils.buildSampledValue(voltageSampledValueTemplate, voltageMeasurandValue), @@ -156,7 +170,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { const voltagePhaseLineToNeutralSampledValueTemplateValue = voltagePhaseLineToNeutralSampledValueTemplate.value ? parseInt(voltagePhaseLineToNeutralSampledValueTemplate.value) - : chargingStation.getVoltageOut(); + : chargingStation.stationInfo.voltageOut!; const fluctuationPhaseToNeutralPercent = voltagePhaseLineToNeutralSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT; @@ -173,7 +187,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { phaseLineToNeutralValue as OCPP16MeterValuePhase, ), ); - if (chargingStation.getPhaseLineToLineVoltageMeterValues()) { + if (chargingStation.stationInfo?.phaseLineToLineVoltageMeterValues) { const phaseLineToLineValue = `L${phase}-L${ (phase + 1) % chargingStation.getNumberOfPhases() !== 0 ? (phase + 1) % chargingStation.getNumberOfPhases() @@ -252,7 +266,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { const errMsg = `MeterValues measurand ${ powerSampledValueTemplate.measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER - }: Unknown ${chargingStation.getCurrentOutType()} currentOutType in template file ${ + }: Unknown ${chargingStation.stationInfo?.currentOutType} currentOutType in template file ${ chargingStation.templateFile }, cannot calculate ${ powerSampledValueTemplate.measurand ?? @@ -270,7 +284,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { const connectorMinimumPowerPerPhase = Math.round( connectorMinimumPower / chargingStation.getNumberOfPhases(), ); - switch (chargingStation.getCurrentOutType()) { + switch (chargingStation.stationInfo?.currentOutType) { case CurrentType.AC: if (chargingStation.getNumberOfPhases() === 3) { const defaultFluctuatedPowerPerPhase = @@ -279,7 +293,10 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue( powerSampledValueTemplate.value, connectorMaximumPower / unitDivider, - { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }, + { + limitationEnabled: + chargingStation.stationInfo?.customValueLimitationMeterValues, + }, ) / chargingStation.getNumberOfPhases(), powerSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT, @@ -290,7 +307,10 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue( powerPerPhaseSampledValueTemplates.L1.value, connectorMaximumPowerPerPhase / unitDivider, - { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }, + { + limitationEnabled: + chargingStation.stationInfo?.customValueLimitationMeterValues, + }, ), powerPerPhaseSampledValueTemplates.L1.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT, @@ -301,7 +321,10 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue( powerPerPhaseSampledValueTemplates.L2.value, connectorMaximumPowerPerPhase / unitDivider, - { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }, + { + limitationEnabled: + chargingStation.stationInfo?.customValueLimitationMeterValues, + }, ), powerPerPhaseSampledValueTemplates.L2.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT, @@ -312,7 +335,10 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue( powerPerPhaseSampledValueTemplates.L3.value, connectorMaximumPowerPerPhase / unitDivider, - { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }, + { + limitationEnabled: + chargingStation.stationInfo?.customValueLimitationMeterValues, + }, ), powerPerPhaseSampledValueTemplates.L3.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT, @@ -344,7 +370,10 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue( powerSampledValueTemplate.value, connectorMaximumPower / unitDivider, - { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }, + { + limitationEnabled: + chargingStation.stationInfo?.customValueLimitationMeterValues, + }, ), powerSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT, @@ -367,7 +396,10 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue( powerSampledValueTemplate.value, connectorMaximumPower / unitDivider, - { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }, + { + limitationEnabled: + chargingStation.stationInfo?.customValueLimitationMeterValues, + }, ), powerSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT, @@ -488,7 +520,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { const errMsg = `MeterValues measurand ${ currentSampledValueTemplate.measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER - }: Unknown ${chargingStation.getCurrentOutType()} currentOutType in template file ${ + }: Unknown ${chargingStation.stationInfo?.currentOutType} currentOutType in template file ${ chargingStation.templateFile }, cannot calculate ${ currentSampledValueTemplate.measurand ?? @@ -499,12 +531,12 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { chargingStation.getConnectorMaximumAvailablePower(connectorId); const connectorMinimumAmperage = currentSampledValueTemplate.minimumValue ?? 0; let connectorMaximumAmperage: number; - switch (chargingStation.getCurrentOutType()) { + switch (chargingStation.stationInfo?.currentOutType) { case CurrentType.AC: connectorMaximumAmperage = ACElectricUtils.amperagePerPhaseFromPower( chargingStation.getNumberOfPhases(), connectorMaximumAvailablePower, - chargingStation.getVoltageOut(), + chargingStation.stationInfo.voltageOut!, ); if (chargingStation.getNumberOfPhases() === 3) { const defaultFluctuatedAmperagePerPhase = @@ -513,7 +545,10 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue( currentSampledValueTemplate.value, connectorMaximumAmperage, - { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }, + { + limitationEnabled: + chargingStation.stationInfo?.customValueLimitationMeterValues, + }, ), currentSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT, @@ -524,7 +559,10 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue( currentPerPhaseSampledValueTemplates.L1.value, connectorMaximumAmperage, - { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }, + { + limitationEnabled: + chargingStation.stationInfo?.customValueLimitationMeterValues, + }, ), currentPerPhaseSampledValueTemplates.L1.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT, @@ -535,7 +573,10 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue( currentPerPhaseSampledValueTemplates.L2.value, connectorMaximumAmperage, - { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }, + { + limitationEnabled: + chargingStation.stationInfo?.customValueLimitationMeterValues, + }, ), currentPerPhaseSampledValueTemplates.L2.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT, @@ -546,7 +587,10 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue( currentPerPhaseSampledValueTemplates.L3.value, connectorMaximumAmperage, - { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }, + { + limitationEnabled: + chargingStation.stationInfo?.customValueLimitationMeterValues, + }, ), currentPerPhaseSampledValueTemplates.L3.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT, @@ -569,7 +613,10 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue( currentSampledValueTemplate.value, connectorMaximumAmperage, - { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }, + { + limitationEnabled: + chargingStation.stationInfo?.customValueLimitationMeterValues, + }, ), currentSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT, @@ -587,14 +634,17 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { case CurrentType.DC: connectorMaximumAmperage = DCElectricUtils.amperage( connectorMaximumAvailablePower, - chargingStation.getVoltageOut(), + chargingStation.stationInfo.voltageOut!, ); currentMeasurandValues.allPhases = currentSampledValueTemplate.value ? getRandomFloatFluctuatedRounded( OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue( currentSampledValueTemplate.value, connectorMaximumAmperage, - { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }, + { + limitationEnabled: + chargingStation.stationInfo?.customValueLimitationMeterValues, + }, ), currentSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT, @@ -690,7 +740,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { energySampledValueTemplate.value, connectorMaximumEnergyRounded, { - limitationEnabled: chargingStation.getCustomValueLimitationMeterValues(), + limitationEnabled: chargingStation.stationInfo?.customValueLimitationMeterValues, unitMultiplier: unitDivider, }, ), @@ -859,7 +909,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { Array.isArray(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles) === false ) { logger.error( - `${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.logPrefix()} Trying to set a charging profile on connector id ${connectorId} with an improper attribute type for the charging profiles array, applying proper type deferred initialization`, ); chargingStation.getConnectorStatus(connectorId)!.chargingProfiles = []; } @@ -886,28 +936,23 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { commandPayload: ClearChargingProfileRequest, chargingProfiles: OCPP16ChargingProfile[] | undefined, ): boolean => { + const { id, chargingProfilePurpose, stackLevel } = commandPayload; let clearedCP = false; if (isNotEmptyArray(chargingProfiles)) { chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => { let clearCurrentCP = false; - if (chargingProfile.chargingProfileId === commandPayload.id) { + if (chargingProfile.chargingProfileId === id) { clearCurrentCP = true; } - if ( - !commandPayload.chargingProfilePurpose && - chargingProfile.stackLevel === commandPayload.stackLevel - ) { + if (!chargingProfilePurpose && chargingProfile.stackLevel === stackLevel) { clearCurrentCP = true; } - if ( - !chargingProfile.stackLevel && - chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose - ) { + if (!stackLevel && chargingProfile.chargingProfilePurpose === chargingProfilePurpose) { clearCurrentCP = true; } if ( - chargingProfile.stackLevel === commandPayload.stackLevel && - chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose + chargingProfile.stackLevel === stackLevel && + chargingProfile.chargingProfilePurpose === chargingProfilePurpose ) { clearCurrentCP = true; } @@ -924,6 +969,226 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { return clearedCP; }; + public static composeChargingSchedules = ( + chargingScheduleHigher: OCPP16ChargingSchedule | undefined, + chargingScheduleLower: OCPP16ChargingSchedule | undefined, + compositeInterval: Interval, + ): OCPP16ChargingSchedule | undefined => { + if (!chargingScheduleHigher && !chargingScheduleLower) { + return undefined; + } + if (chargingScheduleHigher && !chargingScheduleLower) { + return OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleHigher, compositeInterval); + } + if (!chargingScheduleHigher && chargingScheduleLower) { + return OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleLower, compositeInterval); + } + const compositeChargingScheduleHigher: OCPP16ChargingSchedule | undefined = + OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleHigher!, compositeInterval); + const compositeChargingScheduleLower: OCPP16ChargingSchedule | undefined = + OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleLower!, compositeInterval); + const compositeChargingScheduleHigherInterval: Interval = { + start: compositeChargingScheduleHigher!.startSchedule!, + end: addSeconds( + compositeChargingScheduleHigher!.startSchedule!, + compositeChargingScheduleHigher!.duration!, + ), + }; + const compositeChargingScheduleLowerInterval: Interval = { + start: compositeChargingScheduleLower!.startSchedule!, + end: addSeconds( + compositeChargingScheduleLower!.startSchedule!, + compositeChargingScheduleLower!.duration!, + ), + }; + const higherFirst = isBefore( + compositeChargingScheduleHigherInterval.start, + compositeChargingScheduleLowerInterval.start, + ); + if ( + !areIntervalsOverlapping( + compositeChargingScheduleHigherInterval, + compositeChargingScheduleLowerInterval, + ) + ) { + return { + ...compositeChargingScheduleLower, + ...compositeChargingScheduleHigher!, + startSchedule: higherFirst + ? (compositeChargingScheduleHigherInterval.start as Date) + : (compositeChargingScheduleLowerInterval.start as Date), + duration: higherFirst + ? differenceInSeconds( + compositeChargingScheduleLowerInterval.end, + compositeChargingScheduleHigherInterval.start, + ) + : differenceInSeconds( + compositeChargingScheduleHigherInterval.end, + compositeChargingScheduleLowerInterval.start, + ), + chargingSchedulePeriod: [ + ...compositeChargingScheduleHigher!.chargingSchedulePeriod.map((schedulePeriod) => { + return { + ...schedulePeriod, + startPeriod: higherFirst + ? 0 + : schedulePeriod.startPeriod + + differenceInSeconds( + compositeChargingScheduleHigherInterval.start, + compositeChargingScheduleLowerInterval.start, + ), + }; + }), + ...compositeChargingScheduleLower!.chargingSchedulePeriod.map((schedulePeriod) => { + return { + ...schedulePeriod, + startPeriod: higherFirst + ? schedulePeriod.startPeriod + + differenceInSeconds( + compositeChargingScheduleLowerInterval.start, + compositeChargingScheduleHigherInterval.start, + ) + : 0, + }; + }), + ].sort((a, b) => a.startPeriod - b.startPeriod), + }; + } + return { + ...compositeChargingScheduleLower, + ...compositeChargingScheduleHigher!, + startSchedule: higherFirst + ? (compositeChargingScheduleHigherInterval.start as Date) + : (compositeChargingScheduleLowerInterval.start as Date), + duration: higherFirst + ? differenceInSeconds( + compositeChargingScheduleLowerInterval.end, + compositeChargingScheduleHigherInterval.start, + ) + : differenceInSeconds( + compositeChargingScheduleHigherInterval.end, + compositeChargingScheduleLowerInterval.start, + ), + chargingSchedulePeriod: [ + ...compositeChargingScheduleHigher!.chargingSchedulePeriod.map((schedulePeriod) => { + return { + ...schedulePeriod, + startPeriod: higherFirst + ? 0 + : schedulePeriod.startPeriod + + differenceInSeconds( + compositeChargingScheduleHigherInterval.start, + compositeChargingScheduleLowerInterval.start, + ), + }; + }), + ...compositeChargingScheduleLower!.chargingSchedulePeriod + .filter((schedulePeriod, index) => { + if ( + higherFirst && + isWithinInterval( + addSeconds( + compositeChargingScheduleLowerInterval.start, + schedulePeriod.startPeriod, + ), + { + start: compositeChargingScheduleLowerInterval.start, + end: compositeChargingScheduleHigherInterval.end, + }, + ) + ) { + return false; + } + if ( + higherFirst && + index < compositeChargingScheduleLower!.chargingSchedulePeriod.length - 1 && + !isWithinInterval( + addSeconds( + compositeChargingScheduleLowerInterval.start, + schedulePeriod.startPeriod, + ), + { + start: compositeChargingScheduleLowerInterval.start, + end: compositeChargingScheduleHigherInterval.end, + }, + ) && + isWithinInterval( + addSeconds( + compositeChargingScheduleLowerInterval.start, + compositeChargingScheduleLower!.chargingSchedulePeriod[index + 1].startPeriod, + ), + { + start: compositeChargingScheduleLowerInterval.start, + end: compositeChargingScheduleHigherInterval.end, + }, + ) + ) { + return false; + } + if ( + !higherFirst && + isWithinInterval( + addSeconds( + compositeChargingScheduleLowerInterval.start, + schedulePeriod.startPeriod, + ), + { + start: compositeChargingScheduleHigherInterval.start, + end: compositeChargingScheduleLowerInterval.end, + }, + ) + ) { + return false; + } + return true; + }) + .map((schedulePeriod, index) => { + if (index === 0 && schedulePeriod.startPeriod !== 0) { + schedulePeriod.startPeriod = 0; + } + return { + ...schedulePeriod, + startPeriod: higherFirst + ? schedulePeriod.startPeriod + + differenceInSeconds( + compositeChargingScheduleLowerInterval.start, + compositeChargingScheduleHigherInterval.start, + ) + : 0, + }; + }), + ].sort((a, b) => a.startPeriod - b.startPeriod), + }; + }; + + public static hasReservation = ( + chargingStation: ChargingStation, + connectorId: number, + idTag: string, + ): boolean => { + const connectorReservation = chargingStation.getReservationBy('connectorId', connectorId); + const chargingStationReservation = chargingStation.getReservationBy('connectorId', 0); + if ( + (chargingStation.getConnectorStatus(connectorId)?.status === + OCPP16ChargePointStatus.Reserved && + connectorReservation && + !hasReservationExpired(connectorReservation) && + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + connectorReservation?.idTag === idTag) || + (chargingStation.getConnectorStatus(0)?.status === OCPP16ChargePointStatus.Reserved && + chargingStationReservation && + !hasReservationExpired(chargingStationReservation) && + chargingStationReservation?.idTag === idTag) + ) { + logger.debug( + `${chargingStation.logPrefix()} Connector id ${connectorId} has a valid reservation for idTag ${idTag}: %j`, + connectorReservation ?? chargingStationReservation, + ); + return true; + } + return false; + }; + public static parseJsonSchemaFile( relativePath: string, moduleName?: string, @@ -937,18 +1202,91 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { ); } + private static composeChargingSchedule = ( + chargingSchedule: OCPP16ChargingSchedule, + compositeInterval: Interval, + ): OCPP16ChargingSchedule | undefined => { + const chargingScheduleInterval: Interval = { + start: chargingSchedule.startSchedule!, + end: addSeconds(chargingSchedule.startSchedule!, chargingSchedule.duration!), + }; + if (areIntervalsOverlapping(chargingScheduleInterval, compositeInterval)) { + chargingSchedule.chargingSchedulePeriod.sort((a, b) => a.startPeriod - b.startPeriod); + if (isBefore(chargingScheduleInterval.start, compositeInterval.start)) { + return { + ...chargingSchedule, + startSchedule: compositeInterval.start as Date, + duration: differenceInSeconds( + chargingScheduleInterval.end, + compositeInterval.start as Date, + ), + chargingSchedulePeriod: chargingSchedule.chargingSchedulePeriod + .filter((schedulePeriod, index) => { + if ( + isWithinInterval( + addSeconds(chargingScheduleInterval.start, schedulePeriod.startPeriod)!, + compositeInterval, + ) + ) { + return true; + } + if ( + index < chargingSchedule.chargingSchedulePeriod.length - 1 && + !isWithinInterval( + addSeconds(chargingScheduleInterval.start, schedulePeriod.startPeriod), + compositeInterval, + ) && + isWithinInterval( + addSeconds( + chargingScheduleInterval.start, + chargingSchedule.chargingSchedulePeriod[index + 1].startPeriod, + ), + compositeInterval, + ) + ) { + return true; + } + return false; + }) + .map((schedulePeriod, index) => { + if (index === 0 && schedulePeriod.startPeriod !== 0) { + schedulePeriod.startPeriod = 0; + } + return schedulePeriod; + }), + }; + } + if (isAfter(chargingScheduleInterval.end, compositeInterval.end)) { + return { + ...chargingSchedule, + duration: differenceInSeconds( + compositeInterval.end as Date, + chargingScheduleInterval.start, + ), + chargingSchedulePeriod: chargingSchedule.chargingSchedulePeriod.filter((schedulePeriod) => + isWithinInterval( + addSeconds(chargingScheduleInterval.start, schedulePeriod.startPeriod)!, + compositeInterval, + ), + ), + }; + } + return chargingSchedule; + } + }; + private static buildSampledValue( sampledValueTemplate: SampledValueTemplate, value: number, context?: MeterValueContext, phase?: OCPP16MeterValuePhase, ): OCPP16SampledValue { - const sampledValueValue = value ?? sampledValueTemplate?.value ?? null; - const sampledValueContext = context ?? sampledValueTemplate?.context ?? null; + const sampledValueValue = value ?? sampledValueTemplate?.value; + const sampledValueContext = context ?? sampledValueTemplate?.context; const sampledValueLocation = sampledValueTemplate?.location ?? OCPP16ServiceUtils.getMeasurandDefaultLocation(sampledValueTemplate.measurand!); - const sampledValuePhase = phase ?? sampledValueTemplate?.phase ?? null; + const sampledValuePhase = phase ?? sampledValueTemplate?.phase; return { ...(!isNullOrUndefined(sampledValueTemplate.unit) && { unit: sampledValueTemplate.unit,