+ delete template[deprecatedKey as keyof ChargingStationTemplate];
+ }
+};
+
+/**
+ * Charging profiles should already be sorted by connector id and stack level (highest stack level has priority)
+ *
+ * @param chargingProfiles -
+ * @param logPrefix -
+ * @returns
+ */
+const getLimitFromChargingProfiles = (
+ chargingProfiles: ChargingProfile[],
+ logPrefix: string,
+): {
+ limit: number;
+ matchingChargingProfile: ChargingProfile;
+} | null => {
+ 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
+ const chargingSchedule = chargingProfile.chargingSchedule;
+ if (!chargingSchedule?.startSchedule) {
+ logger.warn(
+ `${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(),
+ );
+ // Check if the start of the schedule is yesterday
+ if (moment(chargingSchedule.startSchedule).isAfter(currentMoment)) {
+ chargingSchedule.startSchedule.setDate(currentDate.getDate() - 1);
+ }
+ } 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)
+ ) {
+ let lastButOneSchedule: ChargingSchedulePeriod | undefined;
+ // Search the right schedule period
+ for (const schedulePeriod of chargingSchedule.chargingSchedulePeriod) {
+ // Handling of only one period
+ if (
+ chargingSchedule.chargingSchedulePeriod.length === 1 &&
+ schedulePeriod.startPeriod === 0
+ ) {
+ const result = {
+ limit: schedulePeriod.limit,
+ matchingChargingProfile: chargingProfile,
+ };
+ logger.debug(debugLogMsg, result);
+ return result;
+ }
+ // Find the right schedule period
+ if (
+ moment(chargingSchedule.startSchedule)
+ .add(schedulePeriod.startPeriod, 's')
+ .isAfter(currentMoment)
+ ) {
+ // Found the schedule: last but one is the correct one
+ const result = {
+ limit: lastButOneSchedule!.limit,
+ matchingChargingProfile: chargingProfile,
+ };
+ logger.debug(debugLogMsg, result);
+ return result;
+ }
+ // Keep it
+ lastButOneSchedule = schedulePeriod;
+ // Handle the last schedule period
+ if (
+ schedulePeriod.startPeriod ===
+ chargingSchedule.chargingSchedulePeriod[
+ chargingSchedule.chargingSchedulePeriod.length - 1
+ ].startPeriod
+ ) {
+ const result = {
+ limit: lastButOneSchedule.limit,
+ matchingChargingProfile: chargingProfile,
+ };
+ logger.debug(debugLogMsg, result);
+ return result;
+ }
+ }
+ }
+ }
+ return null;
+};
+
+const getRandomSerialNumberSuffix = (params?: {
+ randomBytesLength?: number;
+ upperCase?: boolean;
+}): string => {
+ const randomSerialNumberSuffix = randomBytes(params?.randomBytesLength ?? 16).toString('hex');
+ if (params?.upperCase) {
+ return randomSerialNumberSuffix.toUpperCase();