1 import crypto from
'node:crypto';
2 import path from
'node:path';
3 import { fileURLToPath
} from
'node:url';
5 import chalk from
'chalk';
6 import moment from
'moment';
8 import type { ChargingStation
} from
'./ChargingStation';
9 import { BaseError
} from
'../exception';
13 type BootNotificationRequest
,
16 ChargingProfileKindType
,
18 type ChargingSchedulePeriod
,
19 type ChargingStationInfo
,
20 type ChargingStationTemplate
,
21 ConnectorPhaseRotation
,
26 type OCPP16BootNotificationRequest
,
27 type OCPP20BootNotificationRequest
,
32 import { ACElectricUtils
, Constants
, DCElectricUtils
, Utils
, logger
} from
'../utils';
34 const moduleName
= 'ChargingStationUtils';
36 export class ChargingStationUtils
{
37 private constructor() {
38 // This is intentional
41 public static getChargingStationId(
43 stationTemplate
: ChargingStationTemplate
45 // In case of multiple instances: add instance index to charging station id
46 const instanceIndex
= process
.env
.CF_INSTANCE_INDEX
?? 0;
47 const idSuffix
= stationTemplate
?.nameSuffix
?? '';
48 const idStr
= `000000000${index.toString()}`;
49 return stationTemplate
?.fixedName
50 ? stationTemplate
.baseName
51 : `${stationTemplate.baseName}-${instanceIndex.toString()}${idStr.substring(
56 public static getHashId(index
: number, stationTemplate
: ChargingStationTemplate
): string {
57 const chargingStationInfo
= {
58 chargePointModel
: stationTemplate
.chargePointModel
,
59 chargePointVendor
: stationTemplate
.chargePointVendor
,
60 ...(!Utils
.isUndefined(stationTemplate
.chargeBoxSerialNumberPrefix
) && {
61 chargeBoxSerialNumber
: stationTemplate
.chargeBoxSerialNumberPrefix
,
63 ...(!Utils
.isUndefined(stationTemplate
.chargePointSerialNumberPrefix
) && {
64 chargePointSerialNumber
: stationTemplate
.chargePointSerialNumberPrefix
,
66 ...(!Utils
.isUndefined(stationTemplate
.meterSerialNumberPrefix
) && {
67 meterSerialNumber
: stationTemplate
.meterSerialNumberPrefix
,
69 ...(!Utils
.isUndefined(stationTemplate
.meterType
) && {
70 meterType
: stationTemplate
.meterType
,
74 .createHash(Constants
.DEFAULT_HASH_ALGORITHM
)
76 `${JSON.stringify(chargingStationInfo)}${ChargingStationUtils.getChargingStationId(
84 public static checkChargingStation(chargingStation
: ChargingStation
, logPrefix
: string): boolean {
85 if (chargingStation
.started
=== false && chargingStation
.starting
=== false) {
86 logger
.warn(`${logPrefix} charging station is stopped, cannot proceed`);
92 public static getPhaseRotationValue(
94 numberOfPhases
: number
95 ): string | undefined {
97 if (connectorId
=== 0 && numberOfPhases
=== 0) {
98 return `${connectorId}.${ConnectorPhaseRotation.RST}`;
99 } else if (connectorId
> 0 && numberOfPhases
=== 0) {
100 return `${connectorId}.${ConnectorPhaseRotation.NotApplicable}`;
102 } else if (connectorId
> 0 && numberOfPhases
=== 1) {
103 return `${connectorId}.${ConnectorPhaseRotation.NotApplicable}`;
104 } else if (connectorId
> 0 && numberOfPhases
=== 3) {
105 return `${connectorId}.${ConnectorPhaseRotation.RST}`;
109 public static getMaxNumberOfEvses(evses
: Record
<string, EvseTemplate
>): number {
113 return Object.keys(evses
).length
;
116 public static getMaxNumberOfConnectors(connectors
: Record
<string, ConnectorStatus
>): number {
120 return Object.keys(connectors
).length
;
123 public static getBootConnectorStatus(
124 chargingStation
: ChargingStation
,
126 connectorStatus
: ConnectorStatus
127 ): ConnectorStatusEnum
{
128 let connectorBootStatus
: ConnectorStatusEnum
;
130 !connectorStatus
?.status &&
131 (chargingStation
.isChargingStationAvailable() === false ||
132 chargingStation
.isConnectorAvailable(connectorId
) === false)
134 connectorBootStatus
= ConnectorStatusEnum
.Unavailable
;
135 } else if (!connectorStatus
?.status && connectorStatus
?.bootStatus
) {
136 // Set boot status in template at startup
137 connectorBootStatus
= connectorStatus
?.bootStatus
;
138 } else if (connectorStatus
?.status) {
139 // Set previous status at startup
140 connectorBootStatus
= connectorStatus
?.status;
142 // Set default status
143 connectorBootStatus
= ConnectorStatusEnum
.Available
;
145 return connectorBootStatus
;
148 public static checkTemplateFile(
149 stationTemplate
: ChargingStationTemplate
,
153 if (Utils
.isNullOrUndefined(stationTemplate
)) {
154 const errorMsg
= `Failed to read charging station template file ${templateFile}`;
155 logger
.error(`${logPrefix} ${errorMsg}`);
156 throw new BaseError(errorMsg
);
158 if (Utils
.isEmptyObject(stationTemplate
)) {
159 const errorMsg
= `Empty charging station information from template file ${templateFile}`;
160 logger
.error(`${logPrefix} ${errorMsg}`);
161 throw new BaseError(errorMsg
);
163 if (Utils
.isEmptyObject(stationTemplate
.AutomaticTransactionGenerator
)) {
164 stationTemplate
.AutomaticTransactionGenerator
= {
168 minDelayBetweenTwoTransactions
: 15,
169 maxDelayBetweenTwoTransactions
: 30,
170 probabilityOfStart
: 1,
172 stopOnConnectionFailure
: true,
175 `${logPrefix} Empty automatic transaction generator configuration from template file ${templateFile}, set to default values`
180 public static checkConnectorsConfiguration(
181 stationTemplate
: ChargingStationTemplate
,
185 configuredMaxConnectors
: number;
186 templateMaxConnectors
: number;
187 templateMaxAvailableConnectors
: number;
189 const configuredMaxConnectors
=
190 ChargingStationUtils
.getConfiguredNumberOfConnectors(stationTemplate
);
191 ChargingStationUtils
.checkConfiguredMaxConnectors(
192 configuredMaxConnectors
,
196 const templateMaxConnectors
= ChargingStationUtils
.getMaxNumberOfConnectors(
197 stationTemplate
.Connectors
199 ChargingStationUtils
.checkTemplateMaxConnectors(templateMaxConnectors
, logPrefix
, templateFile
);
200 const templateMaxAvailableConnectors
= stationTemplate
?.Connectors
[0]
201 ? templateMaxConnectors
- 1
202 : templateMaxConnectors
;
204 configuredMaxConnectors
> templateMaxAvailableConnectors
&&
205 !stationTemplate
?.randomConnectors
208 `${logPrefix} Number of connectors exceeds the number of connector configurations in template ${templateFile}, forcing random connector configurations affectation`
210 stationTemplate
.randomConnectors
= true;
212 return { configuredMaxConnectors
, templateMaxConnectors
, templateMaxAvailableConnectors
};
215 public static checkStationInfoConnectorStatus(
217 connectorStatus
: ConnectorStatus
,
221 if (!Utils
.isNullOrUndefined(connectorStatus
?.status)) {
223 `${logPrefix} Charging station information from template ${templateFile} with connector id ${connectorId} status configuration defined, undefine it`
225 delete connectorStatus
.status;
229 public static buildConnectorsMap(
230 connectors
: Record
<string, ConnectorStatus
>,
233 ): Map
<number, ConnectorStatus
> {
234 const connectorsMap
= new Map
<number, ConnectorStatus
>();
235 if (ChargingStationUtils
.getMaxNumberOfConnectors(connectors
) > 0) {
236 for (const connector
in connectors
) {
237 const connectorStatus
= connectors
[connector
];
238 const connectorId
= Utils
.convertToInt(connector
);
239 ChargingStationUtils
.checkStationInfoConnectorStatus(
245 connectorsMap
.set(connectorId
, Utils
.cloneObject
<ConnectorStatus
>(connectorStatus
));
249 `${logPrefix} Charging station information from template ${templateFile} with no connectors, cannot build connectors map`
252 return connectorsMap
;
255 public static initializeConnectorsMapStatus(
256 connectors
: Map
<number, ConnectorStatus
>,
259 for (const connectorId
of connectors
.keys()) {
260 if (connectorId
> 0 && connectors
.get(connectorId
)?.transactionStarted
=== true) {
262 `${logPrefix} Connector id ${connectorId} at initialization has a transaction started with id ${
263 connectors.get(connectorId)?.transactionId
267 if (connectorId
=== 0) {
268 connectors
.get(connectorId
).availability
= AvailabilityType
.Operative
;
269 if (Utils
.isUndefined(connectors
.get(connectorId
)?.chargingProfiles
)) {
270 connectors
.get(connectorId
).chargingProfiles
= [];
274 Utils
.isNullOrUndefined(connectors
.get(connectorId
)?.transactionStarted
)
276 ChargingStationUtils
.initializeConnectorStatus(connectors
.get(connectorId
));
281 public static resetConnectorStatus(connectorStatus
: ConnectorStatus
): void {
282 connectorStatus
.idTagLocalAuthorized
= false;
283 connectorStatus
.idTagAuthorized
= false;
284 connectorStatus
.transactionRemoteStarted
= false;
285 connectorStatus
.transactionStarted
= false;
286 delete connectorStatus
?.localAuthorizeIdTag
;
287 delete connectorStatus
?.authorizeIdTag
;
288 delete connectorStatus
?.transactionId
;
289 delete connectorStatus
?.transactionIdTag
;
290 connectorStatus
.transactionEnergyActiveImportRegisterValue
= 0;
291 delete connectorStatus
?.transactionBeginMeterValue
;
294 public static createBootNotificationRequest(
295 stationInfo
: ChargingStationInfo
,
296 bootReason
: BootReasonEnumType
= BootReasonEnumType
.PowerUp
297 ): BootNotificationRequest
{
298 const ocppVersion
= stationInfo
.ocppVersion
?? OCPPVersion
.VERSION_16
;
299 switch (ocppVersion
) {
300 case OCPPVersion
.VERSION_16
:
302 chargePointModel
: stationInfo
.chargePointModel
,
303 chargePointVendor
: stationInfo
.chargePointVendor
,
304 ...(!Utils
.isUndefined(stationInfo
.chargeBoxSerialNumber
) && {
305 chargeBoxSerialNumber
: stationInfo
.chargeBoxSerialNumber
,
307 ...(!Utils
.isUndefined(stationInfo
.chargePointSerialNumber
) && {
308 chargePointSerialNumber
: stationInfo
.chargePointSerialNumber
,
310 ...(!Utils
.isUndefined(stationInfo
.firmwareVersion
) && {
311 firmwareVersion
: stationInfo
.firmwareVersion
,
313 ...(!Utils
.isUndefined(stationInfo
.iccid
) && { iccid
: stationInfo
.iccid
}),
314 ...(!Utils
.isUndefined(stationInfo
.imsi
) && { imsi
: stationInfo
.imsi
}),
315 ...(!Utils
.isUndefined(stationInfo
.meterSerialNumber
) && {
316 meterSerialNumber
: stationInfo
.meterSerialNumber
,
318 ...(!Utils
.isUndefined(stationInfo
.meterType
) && {
319 meterType
: stationInfo
.meterType
,
321 } as OCPP16BootNotificationRequest
;
322 case OCPPVersion
.VERSION_20
:
323 case OCPPVersion
.VERSION_201
:
327 model
: stationInfo
.chargePointModel
,
328 vendorName
: stationInfo
.chargePointVendor
,
329 ...(!Utils
.isUndefined(stationInfo
.firmwareVersion
) && {
330 firmwareVersion
: stationInfo
.firmwareVersion
,
332 ...(!Utils
.isUndefined(stationInfo
.chargeBoxSerialNumber
) && {
333 serialNumber
: stationInfo
.chargeBoxSerialNumber
,
335 ...((!Utils
.isUndefined(stationInfo
.iccid
) || !Utils
.isUndefined(stationInfo
.imsi
)) && {
337 ...(!Utils
.isUndefined(stationInfo
.iccid
) && { iccid
: stationInfo
.iccid
}),
338 ...(!Utils
.isUndefined(stationInfo
.imsi
) && { imsi
: stationInfo
.imsi
}),
342 } as OCPP20BootNotificationRequest
;
346 public static warnTemplateKeysDeprecation(
347 stationTemplate
: ChargingStationTemplate
,
351 const templateKeys
: { key
: string; deprecatedKey
: string }[] = [
352 { key
: 'supervisionUrls', deprecatedKey
: 'supervisionUrl' },
353 { key
: 'idTagsFile', deprecatedKey
: 'authorizationFile' },
355 for (const templateKey
of templateKeys
) {
356 ChargingStationUtils
.warnDeprecatedTemplateKey(
358 templateKey
.deprecatedKey
,
361 `Use '${templateKey.key}' instead`
363 ChargingStationUtils
.convertDeprecatedTemplateKey(
365 templateKey
.deprecatedKey
,
371 public static stationTemplateToStationInfo(
372 stationTemplate
: ChargingStationTemplate
373 ): ChargingStationInfo
{
374 stationTemplate
= Utils
.cloneObject
<ChargingStationTemplate
>(stationTemplate
);
375 delete stationTemplate
.power
;
376 delete stationTemplate
.powerUnit
;
377 delete stationTemplate
?.Connectors
;
378 delete stationTemplate
?.Evses
;
379 delete stationTemplate
.Configuration
;
380 delete stationTemplate
.AutomaticTransactionGenerator
;
381 delete stationTemplate
.chargeBoxSerialNumberPrefix
;
382 delete stationTemplate
.chargePointSerialNumberPrefix
;
383 delete stationTemplate
.meterSerialNumberPrefix
;
384 return stationTemplate
as unknown
as ChargingStationInfo
;
387 public static createSerialNumber(
388 stationTemplate
: ChargingStationTemplate
,
389 stationInfo
: ChargingStationInfo
,
391 randomSerialNumberUpperCase
?: boolean;
392 randomSerialNumber
?: boolean;
394 randomSerialNumberUpperCase
: true,
395 randomSerialNumber
: true,
398 params
= { ...{ randomSerialNumberUpperCase
: true, randomSerialNumber
: true }, ...params
};
399 const serialNumberSuffix
= params
?.randomSerialNumber
400 ? ChargingStationUtils
.getRandomSerialNumberSuffix({
401 upperCase
: params
.randomSerialNumberUpperCase
,
404 Utils
.isNotEmptyString(stationTemplate
?.chargePointSerialNumberPrefix
) &&
405 (stationInfo
.chargePointSerialNumber
= `${stationTemplate.chargePointSerialNumberPrefix}${serialNumberSuffix}`);
406 Utils
.isNotEmptyString(stationTemplate
?.chargeBoxSerialNumberPrefix
) &&
407 (stationInfo
.chargeBoxSerialNumber
= `${stationTemplate.chargeBoxSerialNumberPrefix}${serialNumberSuffix}`);
408 Utils
.isNotEmptyString(stationTemplate
?.meterSerialNumberPrefix
) &&
409 (stationInfo
.meterSerialNumber
= `${stationTemplate.meterSerialNumberPrefix}${serialNumberSuffix}`);
412 public static propagateSerialNumber(
413 stationTemplate
: ChargingStationTemplate
,
414 stationInfoSrc
: ChargingStationInfo
,
415 stationInfoDst
: ChargingStationInfo
417 if (!stationInfoSrc
|| !stationTemplate
) {
419 'Missing charging station template or existing configuration to propagate serial number'
422 stationTemplate
?.chargePointSerialNumberPrefix
&& stationInfoSrc
?.chargePointSerialNumber
423 ? (stationInfoDst
.chargePointSerialNumber
= stationInfoSrc
.chargePointSerialNumber
)
424 : stationInfoDst
?.chargePointSerialNumber
&& delete stationInfoDst
.chargePointSerialNumber
;
425 stationTemplate
?.chargeBoxSerialNumberPrefix
&& stationInfoSrc
?.chargeBoxSerialNumber
426 ? (stationInfoDst
.chargeBoxSerialNumber
= stationInfoSrc
.chargeBoxSerialNumber
)
427 : stationInfoDst
?.chargeBoxSerialNumber
&& delete stationInfoDst
.chargeBoxSerialNumber
;
428 stationTemplate
?.meterSerialNumberPrefix
&& stationInfoSrc
?.meterSerialNumber
429 ? (stationInfoDst
.meterSerialNumber
= stationInfoSrc
.meterSerialNumber
)
430 : stationInfoDst
?.meterSerialNumber
&& delete stationInfoDst
.meterSerialNumber
;
433 public static getAmperageLimitationUnitDivider(stationInfo
: ChargingStationInfo
): number {
435 switch (stationInfo
.amperageLimitationUnit
) {
436 case AmpereUnits
.DECI_AMPERE
:
439 case AmpereUnits
.CENTI_AMPERE
:
442 case AmpereUnits
.MILLI_AMPERE
:
449 public static getChargingStationConnectorChargingProfilesPowerLimit(
450 chargingStation
: ChargingStation
,
452 ): number | undefined {
453 let limit
: number, matchingChargingProfile
: ChargingProfile
;
454 // Get charging profiles for connector and sort by stack level
455 const chargingProfiles
=
456 Utils
.cloneObject
<ChargingProfile
[]>(
457 chargingStation
.getConnectorStatus(connectorId
)?.chargingProfiles
458 )?.sort((a
, b
) => b
.stackLevel
- a
.stackLevel
) ?? [];
459 // Get profiles on connector 0
460 if (chargingStation
.getConnectorStatus(0)?.chargingProfiles
) {
461 chargingProfiles
.push(
462 ...Utils
.cloneObject
<ChargingProfile
[]>(
463 chargingStation
.getConnectorStatus(0).chargingProfiles
464 ).sort((a
, b
) => b
.stackLevel
- a
.stackLevel
)
467 if (Utils
.isNotEmptyArray(chargingProfiles
)) {
468 const result
= ChargingStationUtils
.getLimitFromChargingProfiles(
470 chargingStation
.logPrefix()
472 if (!Utils
.isNullOrUndefined(result
)) {
473 limit
= result
?.limit
;
474 matchingChargingProfile
= result
?.matchingChargingProfile
;
475 switch (chargingStation
.getCurrentOutType()) {
478 matchingChargingProfile
.chargingSchedule
.chargingRateUnit
===
479 ChargingRateUnitType
.WATT
481 : ACElectricUtils
.powerTotal(
482 chargingStation
.getNumberOfPhases(),
483 chargingStation
.getVoltageOut(),
489 matchingChargingProfile
.chargingSchedule
.chargingRateUnit
===
490 ChargingRateUnitType
.WATT
492 : DCElectricUtils
.power(chargingStation
.getVoltageOut(), limit
);
494 const connectorMaximumPower
=
495 chargingStation
.getMaximumPower() / chargingStation
.powerDivider
;
496 if (limit
> connectorMaximumPower
) {
498 `${chargingStation.logPrefix()} Charging profile id ${
499 matchingChargingProfile.chargingProfileId
500 } limit ${limit} is greater than connector id ${connectorId} maximum ${connectorMaximumPower}: %j`,
503 limit
= connectorMaximumPower
;
510 public static getDefaultVoltageOut(
511 currentType
: CurrentType
,
515 const errorMsg
= `Unknown ${currentType} currentOutType in template file ${templateFile}, cannot define default voltage out`;
516 let defaultVoltageOut
: number;
517 switch (currentType
) {
519 defaultVoltageOut
= Voltage
.VOLTAGE_230
;
522 defaultVoltageOut
= Voltage
.VOLTAGE_400
;
525 logger
.error(`${logPrefix} ${errorMsg}`);
526 throw new BaseError(errorMsg
);
528 return defaultVoltageOut
;
531 public static getIdTagsFile(stationInfo
: ChargingStationInfo
): string | undefined {
533 stationInfo
.idTagsFile
&&
535 path
.dirname(fileURLToPath(import.meta
.url
)),
537 path
.basename(stationInfo
.idTagsFile
)
542 private static getConfiguredNumberOfConnectors(stationTemplate
: ChargingStationTemplate
): number {
543 let configuredMaxConnectors
: number;
544 if (Utils
.isNotEmptyArray(stationTemplate
.numberOfConnectors
) === true) {
545 const numberOfConnectors
= stationTemplate
.numberOfConnectors
as number[];
546 configuredMaxConnectors
=
547 numberOfConnectors
[Math.floor(Utils
.secureRandom() * numberOfConnectors
.length
)];
548 } else if (Utils
.isUndefined(stationTemplate
.numberOfConnectors
) === false) {
549 configuredMaxConnectors
= stationTemplate
.numberOfConnectors
as number;
550 } else if (stationTemplate
.Connectors
&& !stationTemplate
.Evses
) {
551 configuredMaxConnectors
= stationTemplate
?.Connectors
[0]
552 ? ChargingStationUtils
.getMaxNumberOfConnectors(stationTemplate
.Connectors
) - 1
553 : ChargingStationUtils
.getMaxNumberOfConnectors(stationTemplate
.Connectors
);
554 } else if (stationTemplate
.Evses
&& !stationTemplate
.Connectors
) {
555 configuredMaxConnectors
= 0;
556 for (const evse
in stationTemplate
.Evses
) {
560 configuredMaxConnectors
+= ChargingStationUtils
.getMaxNumberOfConnectors(
561 stationTemplate
.Evses
[evse
].Connectors
565 return configuredMaxConnectors
;
568 private static checkConfiguredMaxConnectors(
569 configuredMaxConnectors
: number,
573 if (configuredMaxConnectors
<= 0) {
575 `${logPrefix} Charging station information from template ${templateFile} with ${configuredMaxConnectors} connectors`
580 private static checkTemplateMaxConnectors(
581 templateMaxConnectors
: number,
585 if (templateMaxConnectors
=== 0) {
587 `${logPrefix} Charging station information from template ${templateFile} with empty connectors configuration`
589 } else if (templateMaxConnectors
< 0) {
591 `${logPrefix} Charging station information from template ${templateFile} with no connectors configuration defined`
596 private static initializeConnectorStatus(connectorStatus
: ConnectorStatus
): void {
597 connectorStatus
.availability
= AvailabilityType
.Operative
;
598 connectorStatus
.idTagLocalAuthorized
= false;
599 connectorStatus
.idTagAuthorized
= false;
600 connectorStatus
.transactionRemoteStarted
= false;
601 connectorStatus
.transactionStarted
= false;
602 connectorStatus
.energyActiveImportRegisterValue
= 0;
603 connectorStatus
.transactionEnergyActiveImportRegisterValue
= 0;
604 if (Utils
.isUndefined(connectorStatus
.chargingProfiles
)) {
605 connectorStatus
.chargingProfiles
= [];
609 private static warnDeprecatedTemplateKey(
610 template
: ChargingStationTemplate
,
613 templateFile
: string,
616 if (!Utils
.isUndefined(template
[key
])) {
617 const logMsg
= `Deprecated template key '${key}' usage in file '${templateFile}'${
618 Utils.isNotEmptyString(logMsgToAppend) ? `. ${logMsgToAppend}
` : ''
620 logger
.warn(`${logPrefix} ${logMsg}`);
621 console
.warn(chalk
.yellow(`${logMsg}`));
625 private static convertDeprecatedTemplateKey(
626 template
: ChargingStationTemplate
,
627 deprecatedKey
: string,
630 if (!Utils
.isUndefined(template
[deprecatedKey
])) {
631 template
[key
] = template
[deprecatedKey
] as unknown
;
632 delete template
[deprecatedKey
];
637 * Charging profiles should already be sorted by connector id and stack level (highest stack level has priority)
639 * @param chargingProfiles -
643 private static getLimitFromChargingProfiles(
644 chargingProfiles
: ChargingProfile
[],
648 matchingChargingProfile
: ChargingProfile
;
650 const debugLogMsg
= `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Matching charging profile found for power limitation: %j`;
651 const currentMoment
= moment();
652 const currentDate
= new Date();
653 for (const chargingProfile
of chargingProfiles
) {
655 const chargingSchedule
= chargingProfile
.chargingSchedule
;
656 if (!chargingSchedule
?.startSchedule
) {
658 `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: startSchedule is not defined in charging profile id ${chargingProfile.chargingProfileId}`
661 // Check type (recurring) and if it is already active
662 // Adjust the daily recurring schedule to today
664 chargingProfile
.chargingProfileKind
=== ChargingProfileKindType
.RECURRING
&&
665 chargingProfile
.recurrencyKind
=== RecurrencyKindType
.DAILY
&&
666 currentMoment
.isAfter(chargingSchedule
.startSchedule
)
668 if (!(chargingSchedule
?.startSchedule
instanceof Date)) {
670 `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: startSchedule is not a Date object in charging profile id ${chargingProfile.chargingProfileId}. Trying to convert it to a Date object`
672 chargingSchedule
.startSchedule
= new Date(chargingSchedule
.startSchedule
);
674 chargingSchedule
.startSchedule
.setFullYear(
675 currentDate
.getFullYear(),
676 currentDate
.getMonth(),
677 currentDate
.getDate()
679 // Check if the start of the schedule is yesterday
680 if (moment(chargingSchedule
.startSchedule
).isAfter(currentMoment
)) {
681 chargingSchedule
.startSchedule
.setDate(currentDate
.getDate() - 1);
683 } else if (moment(chargingSchedule
.startSchedule
).isAfter(currentMoment
)) {
686 // Check if the charging profile is active
688 moment(chargingSchedule
.startSchedule
)
689 .add(chargingSchedule
.duration
, 's')
690 .isAfter(currentMoment
)
692 let lastButOneSchedule
: ChargingSchedulePeriod
;
693 // Search the right schedule period
694 for (const schedulePeriod
of chargingSchedule
.chargingSchedulePeriod
) {
695 // Handling of only one period
697 chargingSchedule
.chargingSchedulePeriod
.length
=== 1 &&
698 schedulePeriod
.startPeriod
=== 0
701 limit
: schedulePeriod
.limit
,
702 matchingChargingProfile
: chargingProfile
,
704 logger
.debug(debugLogMsg
, result
);
707 // Find the right schedule period
709 moment(chargingSchedule
.startSchedule
)
710 .add(schedulePeriod
.startPeriod
, 's')
711 .isAfter(currentMoment
)
713 // Found the schedule: last but one is the correct one
715 limit
: lastButOneSchedule
.limit
,
716 matchingChargingProfile
: chargingProfile
,
718 logger
.debug(debugLogMsg
, result
);
722 lastButOneSchedule
= schedulePeriod
;
723 // Handle the last schedule period
725 schedulePeriod
.startPeriod
===
726 chargingSchedule
.chargingSchedulePeriod
[
727 chargingSchedule
.chargingSchedulePeriod
.length
- 1
731 limit
: lastButOneSchedule
.limit
,
732 matchingChargingProfile
: chargingProfile
,
734 logger
.debug(debugLogMsg
, result
);
743 private static getRandomSerialNumberSuffix(params
?: {
744 randomBytesLength
?: number;
747 const randomSerialNumberSuffix
= crypto
748 .randomBytes(params
?.randomBytesLength
?? 16)
750 if (params
?.upperCase
) {
751 return randomSerialNumberSuffix
.toUpperCase();
753 return randomSerialNumberSuffix
;