import { fileURLToPath } from 'node:url';
import chalk from 'chalk';
-import moment from 'moment';
+import {
+ addDays,
+ addSeconds,
+ addWeeks,
+ differenceInDays,
+ differenceInWeeks,
+ endOfDay,
+ endOfWeek,
+ isAfter,
+ isBefore,
+ startOfDay,
+ startOfWeek,
+} from 'date-fns';
import type { ChargingStation } from './ChargingStation';
import { BaseError } from '../exception';
Constants,
DCElectricUtils,
cloneObject,
+ convertToDate,
convertToInt,
isEmptyObject,
isEmptyString,
logger.error(`${logPrefix} ${errorMsg}`);
throw new BaseError(errorMsg);
}
- if (isEmptyObject(stationTemplate.AutomaticTransactionGenerator)) {
+ if (isEmptyObject(stationTemplate.AutomaticTransactionGenerator!)) {
stationTemplate.AutomaticTransactionGenerator = Constants.DEFAULT_ATG_CONFIGURATION;
logger.warn(
`${logPrefix} Empty automatic transaction generator configuration from template file ${templateFile}, set to default: %j`,
} => {
const configuredMaxConnectors = getConfiguredNumberOfConnectors(stationTemplate);
checkConfiguredMaxConnectors(configuredMaxConnectors, logPrefix, templateFile);
- const templateMaxConnectors = getMaxNumberOfConnectors(stationTemplate.Connectors);
+ const templateMaxConnectors = getMaxNumberOfConnectors(stationTemplate.Connectors!);
checkTemplateMaxConnectors(templateMaxConnectors, logPrefix, templateFile);
- const templateMaxAvailableConnectors = stationTemplate?.Connectors[0]
+ const templateMaxAvailableConnectors = stationTemplate.Connectors![0]
? templateMaxConnectors - 1
: templateMaxConnectors;
if (
);
}
if (connectorId === 0) {
- connectors.get(connectorId).availability = AvailabilityType.Operative;
+ connectors.get(connectorId)!.availability = AvailabilityType.Operative;
if (isUndefined(connectors.get(connectorId)?.chargingProfiles)) {
- connectors.get(connectorId).chargingProfiles = [];
+ connectors.get(connectorId)!.chargingProfiles = [];
}
} else if (
connectorId > 0 &&
isNullOrUndefined(connectors.get(connectorId)?.transactionStarted)
) {
- initializeConnectorStatus(connectors.get(connectorId));
+ initializeConnectorStatus(connectors.get(connectorId)!);
}
}
};
logPrefix: string,
templateFile: string,
) => {
- const templateKeys: { key: string; deprecatedKey: string }[] = [
- { key: 'supervisionUrls', deprecatedKey: 'supervisionUrl' },
- { key: 'idTagsFile', deprecatedKey: 'authorizationFile' },
- { key: 'ocppStrictCompliance', deprecatedKey: 'payloadSchemaValidation' },
+ const templateKeys: { deprecatedKey: string; key?: string }[] = [
+ { deprecatedKey: 'supervisionUrl', key: 'supervisionUrls' },
+ { deprecatedKey: 'authorizationFile', key: 'idTagsFile' },
+ { deprecatedKey: 'payloadSchemaValidation', key: 'ocppStrictCompliance' },
];
for (const templateKey of templateKeys) {
warnDeprecatedTemplateKey(
templateKey.deprecatedKey,
logPrefix,
templateFile,
- `Use '${templateKey.key}' instead`,
+ !isUndefined(templateKey.key) ? `Use '${templateKey.key}' instead` : undefined,
);
convertDeprecatedTemplateKey(stationTemplate, templateKey.deprecatedKey, templateKey.key);
}
stationTemplate = cloneObject<ChargingStationTemplate>(stationTemplate);
delete stationTemplate.power;
delete stationTemplate.powerUnit;
- delete stationTemplate?.Connectors;
- delete stationTemplate?.Evses;
+ delete stationTemplate.Connectors;
+ delete stationTemplate.Evses;
delete stationTemplate.Configuration;
delete stationTemplate.AutomaticTransactionGenerator;
delete stationTemplate.chargeBoxSerialNumberPrefix;
chargingStation: ChargingStation,
connectorId: number,
): number | undefined => {
- let limit: number, matchingChargingProfile: ChargingProfile;
+ let limit: number | undefined, matchingChargingProfile: ChargingProfile | undefined;
// Get charging profiles for connector and sort by stack level
const chargingProfiles =
cloneObject<ChargingProfile[]>(
- chargingStation.getConnectorStatus(connectorId)?.chargingProfiles,
+ chargingStation.getConnectorStatus(connectorId)!.chargingProfiles!,
)?.sort((a, b) => b.stackLevel - a.stackLevel) ?? [];
// Get profiles on connector 0
if (chargingStation.getConnectorStatus(0)?.chargingProfiles) {
chargingProfiles.push(
...cloneObject<ChargingProfile[]>(
- chargingStation.getConnectorStatus(0).chargingProfiles,
+ chargingStation.getConnectorStatus(0)!.chargingProfiles!,
).sort((a, b) => b.stackLevel - a.stackLevel),
);
}
switch (chargingStation.getCurrentOutType()) {
case CurrentType.AC:
limit =
- matchingChargingProfile.chargingSchedule.chargingRateUnit === ChargingRateUnitType.WATT
+ matchingChargingProfile?.chargingSchedule?.chargingRateUnit ===
+ ChargingRateUnitType.WATT
? limit
: ACElectricUtils.powerTotal(
chargingStation.getNumberOfPhases(),
chargingStation.getVoltageOut(),
- limit,
+ limit!,
);
break;
case CurrentType.DC:
limit =
- matchingChargingProfile.chargingSchedule.chargingRateUnit === ChargingRateUnitType.WATT
+ matchingChargingProfile?.chargingSchedule?.chargingRateUnit ===
+ ChargingRateUnitType.WATT
? limit
- : DCElectricUtils.power(chargingStation.getVoltageOut(), limit);
+ : DCElectricUtils.power(chargingStation.getVoltageOut(), limit!);
}
const connectorMaximumPower =
chargingStation.getMaximumPower() / chargingStation.powerDivider;
- if (limit > connectorMaximumPower) {
+ if (limit! > connectorMaximumPower) {
logger.error(
- `${chargingStation.logPrefix()} Charging profile id ${
- matchingChargingProfile.chargingProfileId
- } limit ${limit} is greater than connector id ${connectorId} maximum ${connectorMaximumPower}: %j`,
+ `${chargingStation.logPrefix()} Charging profile id ${matchingChargingProfile?.chargingProfileId} limit ${limit} is greater than connector id ${connectorId} maximum ${connectorMaximumPower}: %j`,
result,
);
limit = connectorMaximumPower;
event: ChargingStationWorkerMessageEvents,
eventsToWait: number,
): Promise<number> => {
- return new Promise((resolve) => {
+ return new Promise<number>((resolve) => {
let events = 0;
if (eventsToWait === 0) {
resolve(events);
};
const getConfiguredNumberOfConnectors = (stationTemplate: ChargingStationTemplate): number => {
- let configuredMaxConnectors: number;
+ let configuredMaxConnectors = 0;
if (isNotEmptyArray(stationTemplate.numberOfConnectors) === true) {
const numberOfConnectors = stationTemplate.numberOfConnectors as number[];
configuredMaxConnectors =
} else if (isUndefined(stationTemplate.numberOfConnectors) === false) {
configuredMaxConnectors = stationTemplate.numberOfConnectors as number;
} else if (stationTemplate.Connectors && !stationTemplate.Evses) {
- configuredMaxConnectors = stationTemplate?.Connectors[0]
+ configuredMaxConnectors = stationTemplate.Connectors[0]
? getMaxNumberOfConnectors(stationTemplate.Connectors) - 1
: getMaxNumberOfConnectors(stationTemplate.Connectors);
} else if (stationTemplate.Evses && !stationTemplate.Connectors) {
- configuredMaxConnectors = 0;
for (const evse in stationTemplate.Evses) {
if (evse === '0') {
continue;
templateFile: string,
logMsgToAppend = '',
): void => {
- if (!isUndefined(template[key])) {
+ if (!isUndefined(template[key as keyof ChargingStationTemplate])) {
const logMsg = `Deprecated template key '${key}' usage in file '${templateFile}'${
isNotEmptyString(logMsgToAppend) ? `. ${logMsgToAppend}` : ''
}`;
const convertDeprecatedTemplateKey = (
template: ChargingStationTemplate,
deprecatedKey: string,
- key: string,
+ key?: string,
): void => {
- if (!isUndefined(template[deprecatedKey])) {
- template[key] = template[deprecatedKey] as unknown;
- delete template[deprecatedKey];
+ if (!isUndefined(template[deprecatedKey as keyof ChargingStationTemplate])) {
+ if (!isUndefined(key)) {
+ (template as unknown as Record<string, unknown>)[key!] =
+ template[deprecatedKey as keyof ChargingStationTemplate];
+ }
+ delete template[deprecatedKey as keyof ChargingStationTemplate];
}
};
const getLimitFromChargingProfiles = (
chargingProfiles: ChargingProfile[],
logPrefix: string,
-): {
- limit: number;
- matchingChargingProfile: ChargingProfile;
-} | null => {
+):
+ | {
+ limit: number;
+ matchingChargingProfile: ChargingProfile;
+ }
+ | undefined => {
const debugLogMsg = `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Matching charging profile found for power limitation: %j`;
- const currentMoment = moment();
const currentDate = new Date();
for (const chargingProfile of chargingProfiles) {
// Set helpers
`${logPrefix} ${moduleName}.getLimitFromChargingProfiles: startSchedule is not defined in charging profile id ${chargingProfile.chargingProfileId}`,
);
}
- // Check type (recurring) and if it is already active
- // Adjust the daily recurring schedule to today
- if (
- chargingProfile.chargingProfileKind === ChargingProfileKindType.RECURRING &&
- chargingProfile.recurrencyKind === RecurrencyKindType.DAILY &&
- currentMoment.isAfter(chargingSchedule.startSchedule)
- ) {
- if (!(chargingSchedule?.startSchedule instanceof Date)) {
- logger.warn(
- `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: startSchedule is not a Date object in charging profile id ${chargingProfile.chargingProfileId}. Trying to convert it to a Date object`,
- );
- chargingSchedule.startSchedule = new Date(chargingSchedule.startSchedule);
- }
- chargingSchedule.startSchedule.setFullYear(
- currentDate.getFullYear(),
- currentDate.getMonth(),
- currentDate.getDate(),
+ if (!(chargingSchedule?.startSchedule instanceof Date)) {
+ logger.warn(
+ `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: startSchedule is not a Date object in charging profile id ${chargingProfile.chargingProfileId}. Trying to convert it to a Date object`,
);
- // Check if the start of the schedule is yesterday
- if (moment(chargingSchedule.startSchedule).isAfter(currentMoment)) {
- chargingSchedule.startSchedule.setDate(currentDate.getDate() - 1);
+ chargingSchedule.startSchedule = convertToDate(chargingSchedule.startSchedule)!;
+ }
+ // Adjust recurring start schedule
+ if (chargingProfile.chargingProfileKind === ChargingProfileKindType.RECURRING) {
+ switch (chargingProfile.recurrencyKind) {
+ case RecurrencyKindType.DAILY:
+ if (isBefore(chargingSchedule.startSchedule, startOfDay(currentDate))) {
+ addDays(
+ chargingSchedule.startSchedule,
+ differenceInDays(chargingSchedule.startSchedule, endOfDay(currentDate)),
+ );
+ }
+ break;
+ case RecurrencyKindType.WEEKLY:
+ if (isBefore(chargingSchedule.startSchedule, startOfWeek(currentDate))) {
+ addWeeks(
+ chargingSchedule.startSchedule,
+ differenceInWeeks(chargingSchedule.startSchedule, endOfWeek(currentDate)),
+ );
+ }
+ break;
}
- } else if (moment(chargingSchedule.startSchedule).isAfter(currentMoment)) {
- return null;
}
// Check if the charging profile is active
if (
- moment(chargingSchedule.startSchedule)
- .add(chargingSchedule.duration, 's')
- .isAfter(currentMoment)
+ isAfter(addSeconds(chargingSchedule.startSchedule, chargingSchedule.duration!), currentDate)
) {
- let lastButOneSchedule: ChargingSchedulePeriod;
+ let lastButOneSchedule: ChargingSchedulePeriod | undefined;
// Search the right schedule period
for (const schedulePeriod of chargingSchedule.chargingSchedulePeriod) {
// Handling of only one period
}
// Find the right schedule period
if (
- moment(chargingSchedule.startSchedule)
- .add(schedulePeriod.startPeriod, 's')
- .isAfter(currentMoment)
+ isAfter(
+ addSeconds(chargingSchedule.startSchedule, schedulePeriod.startPeriod),
+ currentDate,
+ )
) {
// Found the schedule: last but one is the correct one
const result = {
- limit: lastButOneSchedule.limit,
+ limit: lastButOneSchedule!.limit,
matchingChargingProfile: chargingProfile,
};
logger.debug(debugLogMsg, result);
}
}
}
- return null;
};
const getRandomSerialNumberSuffix = (params?: {