// 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 OCPP16ChangeAvailabilityResponse,
OCPP16ChargePointStatus,
type OCPP16ChargingProfile,
+ type OCPP16ChargingSchedule,
type OCPP16IncomingRequestCommand,
type OCPP16MeterValue,
OCPP16MeterValueMeasurand,
type OCPP16SupportedFeatureProfiles,
OCPPVersion,
type SampledValueTemplate,
- Voltage,
} from '../../../types';
import {
ACElectricUtils,
getRandomFloatRounded,
getRandomInteger,
isNotEmptyArray,
+ isNotEmptyString,
isNullOrUndefined,
isUndefined,
logger,
if (socSampledValueTemplate) {
const socMaximumValue = 100;
const socMinimumValue = socSampledValueTemplate.minimumValue ?? 0;
- const socSampledValueTemplateValue = socSampledValueTemplate.value
+ const socSampledValueTemplateValue = isNotEmptyString(socSampledValueTemplate.value)
? getRandomFloatFluctuatedRounded(
parseInt(socSampledValueTemplate.value),
socSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT,
OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
}: connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${socMinimumValue}/${
meterValue.sampledValue[sampledValuesIndex].value
- }/${socMaximumValue}}`,
+ }/${socMaximumValue}`,
);
}
}
OCPP16MeterValueMeasurand.VOLTAGE,
);
if (voltageSampledValueTemplate) {
- const voltageSampledValueTemplateValue = voltageSampledValueTemplate.value
+ const voltageSampledValueTemplateValue = isNotEmptyString(voltageSampledValueTemplate.value)
? parseInt(voltageSampledValueTemplate.value)
- : chargingStation.getVoltageOut();
+ : chargingStation.stationInfo.voltageOut!;
const fluctuationPercent =
voltageSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT;
const voltageMeasurandValue = getRandomFloatFluctuatedRounded(
);
if (
chargingStation.getNumberOfPhases() !== 3 ||
- (chargingStation.getNumberOfPhases() === 3 && chargingStation.getMainVoltageMeterValues())
+ (chargingStation.getNumberOfPhases() === 3 &&
+ chargingStation.stationInfo?.mainVoltageMeterValues)
) {
meterValue.sampledValue.push(
OCPP16ServiceUtils.buildSampledValue(voltageSampledValueTemplate, voltageMeasurandValue),
);
let voltagePhaseLineToNeutralMeasurandValue: number | undefined;
if (voltagePhaseLineToNeutralSampledValueTemplate) {
- const voltagePhaseLineToNeutralSampledValueTemplateValue =
- voltagePhaseLineToNeutralSampledValueTemplate.value
- ? parseInt(voltagePhaseLineToNeutralSampledValueTemplate.value)
- : chargingStation.getVoltageOut();
+ const voltagePhaseLineToNeutralSampledValueTemplateValue = isNotEmptyString(
+ voltagePhaseLineToNeutralSampledValueTemplate.value,
+ )
+ ? parseInt(voltagePhaseLineToNeutralSampledValueTemplate.value)
+ : chargingStation.stationInfo.voltageOut!;
const fluctuationPhaseToNeutralPercent =
voltagePhaseLineToNeutralSampledValueTemplate.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT;
phaseLineToNeutralValue as OCPP16MeterValuePhase,
),
);
- if (chargingStation.getPhaseLineToLineVoltageMeterValues()) {
+ if (chargingStation.stationInfo?.phaseLineToLineVoltageMeterValues) {
const phaseLineToLineValue = `L${phase}-L${
(phase + 1) % chargingStation.getNumberOfPhases() !== 0
? (phase + 1) % chargingStation.getNumberOfPhases()
);
let voltagePhaseLineToLineMeasurandValue: number | undefined;
if (voltagePhaseLineToLineSampledValueTemplate) {
- const voltagePhaseLineToLineSampledValueTemplateValue =
- voltagePhaseLineToLineSampledValueTemplate.value
- ? parseInt(voltagePhaseLineToLineSampledValueTemplate.value)
- : Voltage.VOLTAGE_400;
+ const voltagePhaseLineToLineSampledValueTemplateValue = isNotEmptyString(
+ voltagePhaseLineToLineSampledValueTemplate.value,
+ )
+ ? parseInt(voltagePhaseLineToLineSampledValueTemplate.value)
+ : roundTo(
+ Math.sqrt(chargingStation.getNumberOfPhases()) *
+ chargingStation.stationInfo.voltageOut!,
+ 2,
+ );
const fluctuationPhaseLineToLinePercent =
voltagePhaseLineToLineSampledValueTemplate.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT;
);
}
const defaultVoltagePhaseLineToLineMeasurandValue = getRandomFloatFluctuatedRounded(
- Voltage.VOLTAGE_400,
+ Math.sqrt(chargingStation.getNumberOfPhases()) *
+ chargingStation.stationInfo.voltageOut!,
fluctuationPercent,
);
meterValue.sampledValue.push(
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 ??
const connectorMinimumPowerPerPhase = Math.round(
connectorMinimumPower / chargingStation.getNumberOfPhases(),
);
- switch (chargingStation.getCurrentOutType()) {
+ switch (chargingStation.stationInfo?.currentOutType) {
case CurrentType.AC:
if (chargingStation.getNumberOfPhases() === 3) {
const defaultFluctuatedPowerPerPhase =
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
powerSampledValueTemplate.value,
connectorMaximumPower / unitDivider,
- { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ },
) / chargingStation.getNumberOfPhases(),
powerSampledValueTemplate.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT,
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
powerPerPhaseSampledValueTemplates.L1.value,
connectorMaximumPowerPerPhase / unitDivider,
- { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ },
),
powerPerPhaseSampledValueTemplates.L1.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT,
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
powerPerPhaseSampledValueTemplates.L2.value,
connectorMaximumPowerPerPhase / unitDivider,
- { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ },
),
powerPerPhaseSampledValueTemplates.L2.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT,
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
powerPerPhaseSampledValueTemplates.L3.value,
connectorMaximumPowerPerPhase / unitDivider,
- { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ },
),
powerPerPhaseSampledValueTemplates.L3.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT,
connectorMinimumPowerPerPhase / unitDivider,
);
} else {
- powerMeasurandValues.L1 = powerSampledValueTemplate.value
+ powerMeasurandValues.L1 = isNotEmptyString(powerSampledValueTemplate.value)
? getRandomFloatFluctuatedRounded(
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
powerSampledValueTemplate.value,
connectorMaximumPower / unitDivider,
- { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ },
),
powerSampledValueTemplate.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT,
);
break;
case CurrentType.DC:
- powerMeasurandValues.allPhases = powerSampledValueTemplate.value
+ powerMeasurandValues.allPhases = isNotEmptyString(powerSampledValueTemplate.value)
? getRandomFloatFluctuatedRounded(
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
powerSampledValueTemplate.value,
connectorMaximumPower / unitDivider,
- { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ },
),
powerSampledValueTemplate.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT,
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 ??
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 =
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
currentSampledValueTemplate.value,
connectorMaximumAmperage,
- { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ },
),
currentSampledValueTemplate.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT,
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
currentPerPhaseSampledValueTemplates.L1.value,
connectorMaximumAmperage,
- { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ },
),
currentPerPhaseSampledValueTemplates.L1.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT,
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
currentPerPhaseSampledValueTemplates.L2.value,
connectorMaximumAmperage,
- { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ },
),
currentPerPhaseSampledValueTemplates.L2.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT,
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
currentPerPhaseSampledValueTemplates.L3.value,
connectorMaximumAmperage,
- { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ },
),
currentPerPhaseSampledValueTemplates.L3.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT,
(defaultFluctuatedAmperagePerPhase as number) ??
getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage);
} else {
- currentMeasurandValues.L1 = currentSampledValueTemplate.value
+ currentMeasurandValues.L1 = isNotEmptyString(currentSampledValueTemplate.value)
? getRandomFloatFluctuatedRounded(
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
currentSampledValueTemplate.value,
connectorMaximumAmperage,
- { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ },
),
currentSampledValueTemplate.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT,
case CurrentType.DC:
connectorMaximumAmperage = DCElectricUtils.amperage(
connectorMaximumAvailablePower,
- chargingStation.getVoltageOut(),
+ chargingStation.stationInfo.voltageOut!,
);
- currentMeasurandValues.allPhases = currentSampledValueTemplate.value
+ currentMeasurandValues.allPhases = isNotEmptyString(currentSampledValueTemplate.value)
? getRandomFloatFluctuatedRounded(
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
currentSampledValueTemplate.value,
connectorMaximumAmperage,
- { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() },
+ {
+ limitationEnabled:
+ chargingStation.stationInfo?.customValueLimitationMeterValues,
+ },
),
currentSampledValueTemplate.fluctuationPercent ??
Constants.DEFAULT_FLUCTUATION_PERCENT,
(connectorMaximumAvailablePower * interval) / (3600 * 1000),
2,
);
- const energyValueRounded = energySampledValueTemplate.value
+ const energyValueRounded = isNotEmptyString(energySampledValueTemplate.value)
? // Cumulate the fluctuated value around the static one
getRandomFloatFluctuatedRounded(
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
energySampledValueTemplate.value,
connectorMaximumEnergyRounded,
{
- limitationEnabled: chargingStation.getCustomValueLimitationMeterValues(),
+ limitationEnabled: chargingStation.stationInfo?.customValueLimitationMeterValues,
unitMultiplier: unitDivider,
},
),
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 = [];
}
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;
}
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,
!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;
);
}
+ 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,