1 import crypto from
'node:crypto';
2 import type EventEmitter from
'node:events';
3 import path from
'node:path';
4 import { fileURLToPath
} from
'node:url';
6 import chalk from
'chalk';
7 import moment from
'moment';
9 import type { ChargingStation
} from
'./ChargingStation';
10 import { BaseError
} from
'../exception';
14 type BootNotificationRequest
,
17 ChargingProfileKindType
,
19 type ChargingSchedulePeriod
,
20 type ChargingStationInfo
,
21 type ChargingStationTemplate
,
22 ChargingStationWorkerMessageEvents
,
23 ConnectorPhaseRotation
,
28 type OCPP16BootNotificationRequest
,
29 type OCPP20BootNotificationRequest
,
34 import { ACElectricUtils
, Constants
, DCElectricUtils
, Utils
, logger
} from
'../utils';
36 const moduleName
= 'ChargingStationUtils';
38 export class ChargingStationUtils
{
39 private constructor() {
40 // This is intentional
43 public static getChargingStationId(
45 stationTemplate
: ChargingStationTemplate
47 // In case of multiple instances: add instance index to charging station id
48 const instanceIndex
= process
.env
.CF_INSTANCE_INDEX
?? 0;
49 const idSuffix
= stationTemplate
?.nameSuffix
?? '';
50 const idStr
= `000000000${index.toString()}`;
51 return stationTemplate
?.fixedName
52 ? stationTemplate
.baseName
53 : `${stationTemplate.baseName}-${instanceIndex.toString()}${idStr.substring(
58 public static getHashId(index
: number, stationTemplate
: ChargingStationTemplate
): string {
59 const chargingStationInfo
= {
60 chargePointModel
: stationTemplate
.chargePointModel
,
61 chargePointVendor
: stationTemplate
.chargePointVendor
,
62 ...(!Utils
.isUndefined(stationTemplate
.chargeBoxSerialNumberPrefix
) && {
63 chargeBoxSerialNumber
: stationTemplate
.chargeBoxSerialNumberPrefix
,
65 ...(!Utils
.isUndefined(stationTemplate
.chargePointSerialNumberPrefix
) && {
66 chargePointSerialNumber
: stationTemplate
.chargePointSerialNumberPrefix
,
68 ...(!Utils
.isUndefined(stationTemplate
.meterSerialNumberPrefix
) && {
69 meterSerialNumber
: stationTemplate
.meterSerialNumberPrefix
,
71 ...(!Utils
.isUndefined(stationTemplate
.meterType
) && {
72 meterType
: stationTemplate
.meterType
,
76 .createHash(Constants
.DEFAULT_HASH_ALGORITHM
)
78 `${JSON.stringify(chargingStationInfo)}${ChargingStationUtils.getChargingStationId(
86 public static checkChargingStation(chargingStation
: ChargingStation
, logPrefix
: string): boolean {
87 if (chargingStation
.started
=== false && chargingStation
.starting
=== false) {
88 logger
.warn(`${logPrefix} charging station is stopped, cannot proceed`);
94 public static getPhaseRotationValue(
96 numberOfPhases
: number
97 ): string | undefined {
99 if (connectorId
=== 0 && numberOfPhases
=== 0) {
100 return `${connectorId}.${ConnectorPhaseRotation.RST}`;
101 } else if (connectorId
> 0 && numberOfPhases
=== 0) {
102 return `${connectorId}.${ConnectorPhaseRotation.NotApplicable}`;
104 } else if (connectorId
> 0 && numberOfPhases
=== 1) {
105 return `${connectorId}.${ConnectorPhaseRotation.NotApplicable}`;
106 } else if (connectorId
> 0 && numberOfPhases
=== 3) {
107 return `${connectorId}.${ConnectorPhaseRotation.RST}`;
111 public static getMaxNumberOfEvses(evses
: Record
<string, EvseTemplate
>): number {
115 return Object.keys(evses
).length
;
118 public static getMaxNumberOfConnectors(connectors
: Record
<string, ConnectorStatus
>): number {
122 return Object.keys(connectors
).length
;
125 public static getBootConnectorStatus(
126 chargingStation
: ChargingStation
,
128 connectorStatus
: ConnectorStatus
129 ): ConnectorStatusEnum
{
130 let connectorBootStatus
: ConnectorStatusEnum
;
132 !connectorStatus
?.status &&
133 (chargingStation
.isChargingStationAvailable() === false ||
134 chargingStation
.isConnectorAvailable(connectorId
) === false)
136 connectorBootStatus
= ConnectorStatusEnum
.Unavailable
;
137 } else if (!connectorStatus
?.status && connectorStatus
?.bootStatus
) {
138 // Set boot status in template at startup
139 connectorBootStatus
= connectorStatus
?.bootStatus
;
140 } else if (connectorStatus
?.status) {
141 // Set previous status at startup
142 connectorBootStatus
= connectorStatus
?.status;
144 // Set default status
145 connectorBootStatus
= ConnectorStatusEnum
.Available
;
147 return connectorBootStatus
;
150 public static checkTemplate(
151 stationTemplate
: ChargingStationTemplate
,
155 if (Utils
.isNullOrUndefined(stationTemplate
)) {
156 const errorMsg
= `Failed to read charging station template file ${templateFile}`;
157 logger
.error(`${logPrefix} ${errorMsg}`);
158 throw new BaseError(errorMsg
);
160 if (Utils
.isEmptyObject(stationTemplate
)) {
161 const errorMsg
= `Empty charging station information from template file ${templateFile}`;
162 logger
.error(`${logPrefix} ${errorMsg}`);
163 throw new BaseError(errorMsg
);
165 if (Utils
.isEmptyObject(stationTemplate
.AutomaticTransactionGenerator
)) {
166 stationTemplate
.AutomaticTransactionGenerator
= Constants
.DEFAULT_ATG_CONFIGURATION
;
168 `${logPrefix} Empty automatic transaction generator configuration from template file ${templateFile}, set to default: %j`,
169 Constants
.DEFAULT_ATG_CONFIGURATION
173 Utils
.isNullOrUndefined(stationTemplate
.idTagsFile
) ||
174 Utils
.isEmptyString(stationTemplate
.idTagsFile
)
177 `${logPrefix} Missing id tags file in template file ${templateFile}. That can lead to issues with the Automatic Transaction Generator`
182 public static checkConnectorsConfiguration(
183 stationTemplate
: ChargingStationTemplate
,
187 configuredMaxConnectors
: number;
188 templateMaxConnectors
: number;
189 templateMaxAvailableConnectors
: number;
191 const configuredMaxConnectors
=
192 ChargingStationUtils
.getConfiguredNumberOfConnectors(stationTemplate
);
193 ChargingStationUtils
.checkConfiguredMaxConnectors(
194 configuredMaxConnectors
,
198 const templateMaxConnectors
= ChargingStationUtils
.getMaxNumberOfConnectors(
199 stationTemplate
.Connectors
201 ChargingStationUtils
.checkTemplateMaxConnectors(templateMaxConnectors
, logPrefix
, templateFile
);
202 const templateMaxAvailableConnectors
= stationTemplate
?.Connectors
[0]
203 ? templateMaxConnectors
- 1
204 : templateMaxConnectors
;
206 configuredMaxConnectors
> templateMaxAvailableConnectors
&&
207 !stationTemplate
?.randomConnectors
210 `${logPrefix} Number of connectors exceeds the number of connector configurations in template ${templateFile}, forcing random connector configurations affectation`
212 stationTemplate
.randomConnectors
= true;
214 return { configuredMaxConnectors
, templateMaxConnectors
, templateMaxAvailableConnectors
};
217 public static checkStationInfoConnectorStatus(
219 connectorStatus
: ConnectorStatus
,
223 if (!Utils
.isNullOrUndefined(connectorStatus
?.status)) {
225 `${logPrefix} Charging station information from template ${templateFile} with connector id ${connectorId} status configuration defined, undefine it`
227 delete connectorStatus
.status;
231 public static buildConnectorsMap(
232 connectors
: Record
<string, ConnectorStatus
>,
235 ): Map
<number, ConnectorStatus
> {
236 const connectorsMap
= new Map
<number, ConnectorStatus
>();
237 if (ChargingStationUtils
.getMaxNumberOfConnectors(connectors
) > 0) {
238 for (const connector
in connectors
) {
239 const connectorStatus
= connectors
[connector
];
240 const connectorId
= Utils
.convertToInt(connector
);
241 ChargingStationUtils
.checkStationInfoConnectorStatus(
247 connectorsMap
.set(connectorId
, Utils
.cloneObject
<ConnectorStatus
>(connectorStatus
));
251 `${logPrefix} Charging station information from template ${templateFile} with no connectors, cannot build connectors map`
254 return connectorsMap
;
257 public static initializeConnectorsMapStatus(
258 connectors
: Map
<number, ConnectorStatus
>,
261 for (const connectorId
of connectors
.keys()) {
262 if (connectorId
> 0 && connectors
.get(connectorId
)?.transactionStarted
=== true) {
264 `${logPrefix} Connector id ${connectorId} at initialization has a transaction started with id ${
265 connectors.get(connectorId)?.transactionId
269 if (connectorId
=== 0) {
270 connectors
.get(connectorId
).availability
= AvailabilityType
.Operative
;
271 if (Utils
.isUndefined(connectors
.get(connectorId
)?.chargingProfiles
)) {
272 connectors
.get(connectorId
).chargingProfiles
= [];
276 Utils
.isNullOrUndefined(connectors
.get(connectorId
)?.transactionStarted
)
278 ChargingStationUtils
.initializeConnectorStatus(connectors
.get(connectorId
));
283 public static resetConnectorStatus(connectorStatus
: ConnectorStatus
): void {
284 connectorStatus
.idTagLocalAuthorized
= false;
285 connectorStatus
.idTagAuthorized
= false;
286 connectorStatus
.transactionRemoteStarted
= false;
287 connectorStatus
.transactionStarted
= false;
288 delete connectorStatus
?.localAuthorizeIdTag
;
289 delete connectorStatus
?.authorizeIdTag
;
290 delete connectorStatus
?.transactionId
;
291 delete connectorStatus
?.transactionIdTag
;
292 connectorStatus
.transactionEnergyActiveImportRegisterValue
= 0;
293 delete connectorStatus
?.transactionBeginMeterValue
;
296 public static createBootNotificationRequest(
297 stationInfo
: ChargingStationInfo
,
298 bootReason
: BootReasonEnumType
= BootReasonEnumType
.PowerUp
299 ): BootNotificationRequest
{
300 const ocppVersion
= stationInfo
.ocppVersion
?? OCPPVersion
.VERSION_16
;
301 switch (ocppVersion
) {
302 case OCPPVersion
.VERSION_16
:
304 chargePointModel
: stationInfo
.chargePointModel
,
305 chargePointVendor
: stationInfo
.chargePointVendor
,
306 ...(!Utils
.isUndefined(stationInfo
.chargeBoxSerialNumber
) && {
307 chargeBoxSerialNumber
: stationInfo
.chargeBoxSerialNumber
,
309 ...(!Utils
.isUndefined(stationInfo
.chargePointSerialNumber
) && {
310 chargePointSerialNumber
: stationInfo
.chargePointSerialNumber
,
312 ...(!Utils
.isUndefined(stationInfo
.firmwareVersion
) && {
313 firmwareVersion
: stationInfo
.firmwareVersion
,
315 ...(!Utils
.isUndefined(stationInfo
.iccid
) && { iccid
: stationInfo
.iccid
}),
316 ...(!Utils
.isUndefined(stationInfo
.imsi
) && { imsi
: stationInfo
.imsi
}),
317 ...(!Utils
.isUndefined(stationInfo
.meterSerialNumber
) && {
318 meterSerialNumber
: stationInfo
.meterSerialNumber
,
320 ...(!Utils
.isUndefined(stationInfo
.meterType
) && {
321 meterType
: stationInfo
.meterType
,
323 } as OCPP16BootNotificationRequest
;
324 case OCPPVersion
.VERSION_20
:
325 case OCPPVersion
.VERSION_201
:
329 model
: stationInfo
.chargePointModel
,
330 vendorName
: stationInfo
.chargePointVendor
,
331 ...(!Utils
.isUndefined(stationInfo
.firmwareVersion
) && {
332 firmwareVersion
: stationInfo
.firmwareVersion
,
334 ...(!Utils
.isUndefined(stationInfo
.chargeBoxSerialNumber
) && {
335 serialNumber
: stationInfo
.chargeBoxSerialNumber
,
337 ...((!Utils
.isUndefined(stationInfo
.iccid
) || !Utils
.isUndefined(stationInfo
.imsi
)) && {
339 ...(!Utils
.isUndefined(stationInfo
.iccid
) && { iccid
: stationInfo
.iccid
}),
340 ...(!Utils
.isUndefined(stationInfo
.imsi
) && { imsi
: stationInfo
.imsi
}),
344 } as OCPP20BootNotificationRequest
;
348 public static warnTemplateKeysDeprecation(
349 stationTemplate
: ChargingStationTemplate
,
353 const templateKeys
: { key
: string; deprecatedKey
: string }[] = [
354 { key
: 'supervisionUrls', deprecatedKey
: 'supervisionUrl' },
355 { key
: 'idTagsFile', deprecatedKey
: 'authorizationFile' },
357 for (const templateKey
of templateKeys
) {
358 ChargingStationUtils
.warnDeprecatedTemplateKey(
360 templateKey
.deprecatedKey
,
363 `Use '${templateKey.key}' instead`
365 ChargingStationUtils
.convertDeprecatedTemplateKey(
367 templateKey
.deprecatedKey
,
373 public static stationTemplateToStationInfo(
374 stationTemplate
: ChargingStationTemplate
375 ): ChargingStationInfo
{
376 stationTemplate
= Utils
.cloneObject
<ChargingStationTemplate
>(stationTemplate
);
377 delete stationTemplate
.power
;
378 delete stationTemplate
.powerUnit
;
379 delete stationTemplate
?.Connectors
;
380 delete stationTemplate
?.Evses
;
381 delete stationTemplate
.Configuration
;
382 delete stationTemplate
.AutomaticTransactionGenerator
;
383 delete stationTemplate
.chargeBoxSerialNumberPrefix
;
384 delete stationTemplate
.chargePointSerialNumberPrefix
;
385 delete stationTemplate
.meterSerialNumberPrefix
;
386 return stationTemplate
as unknown
as ChargingStationInfo
;
389 public static createSerialNumber(
390 stationTemplate
: ChargingStationTemplate
,
391 stationInfo
: ChargingStationInfo
,
393 randomSerialNumberUpperCase
?: boolean;
394 randomSerialNumber
?: boolean;
396 randomSerialNumberUpperCase
: true,
397 randomSerialNumber
: true,
400 params
= { ...{ randomSerialNumberUpperCase
: true, randomSerialNumber
: true }, ...params
};
401 const serialNumberSuffix
= params
?.randomSerialNumber
402 ? ChargingStationUtils
.getRandomSerialNumberSuffix({
403 upperCase
: params
.randomSerialNumberUpperCase
,
406 Utils
.isNotEmptyString(stationTemplate
?.chargePointSerialNumberPrefix
) &&
407 (stationInfo
.chargePointSerialNumber
= `${stationTemplate.chargePointSerialNumberPrefix}${serialNumberSuffix}`);
408 Utils
.isNotEmptyString(stationTemplate
?.chargeBoxSerialNumberPrefix
) &&
409 (stationInfo
.chargeBoxSerialNumber
= `${stationTemplate.chargeBoxSerialNumberPrefix}${serialNumberSuffix}`);
410 Utils
.isNotEmptyString(stationTemplate
?.meterSerialNumberPrefix
) &&
411 (stationInfo
.meterSerialNumber
= `${stationTemplate.meterSerialNumberPrefix}${serialNumberSuffix}`);
414 public static propagateSerialNumber(
415 stationTemplate
: ChargingStationTemplate
,
416 stationInfoSrc
: ChargingStationInfo
,
417 stationInfoDst
: ChargingStationInfo
419 if (!stationInfoSrc
|| !stationTemplate
) {
421 'Missing charging station template or existing configuration to propagate serial number'
424 stationTemplate
?.chargePointSerialNumberPrefix
&& stationInfoSrc
?.chargePointSerialNumber
425 ? (stationInfoDst
.chargePointSerialNumber
= stationInfoSrc
.chargePointSerialNumber
)
426 : stationInfoDst
?.chargePointSerialNumber
&& delete stationInfoDst
.chargePointSerialNumber
;
427 stationTemplate
?.chargeBoxSerialNumberPrefix
&& stationInfoSrc
?.chargeBoxSerialNumber
428 ? (stationInfoDst
.chargeBoxSerialNumber
= stationInfoSrc
.chargeBoxSerialNumber
)
429 : stationInfoDst
?.chargeBoxSerialNumber
&& delete stationInfoDst
.chargeBoxSerialNumber
;
430 stationTemplate
?.meterSerialNumberPrefix
&& stationInfoSrc
?.meterSerialNumber
431 ? (stationInfoDst
.meterSerialNumber
= stationInfoSrc
.meterSerialNumber
)
432 : stationInfoDst
?.meterSerialNumber
&& delete stationInfoDst
.meterSerialNumber
;
435 public static getAmperageLimitationUnitDivider(stationInfo
: ChargingStationInfo
): number {
437 switch (stationInfo
.amperageLimitationUnit
) {
438 case AmpereUnits
.DECI_AMPERE
:
441 case AmpereUnits
.CENTI_AMPERE
:
444 case AmpereUnits
.MILLI_AMPERE
:
451 public static getChargingStationConnectorChargingProfilesPowerLimit(
452 chargingStation
: ChargingStation
,
454 ): number | undefined {
455 let limit
: number, matchingChargingProfile
: ChargingProfile
;
456 // Get charging profiles for connector and sort by stack level
457 const chargingProfiles
=
458 Utils
.cloneObject
<ChargingProfile
[]>(
459 chargingStation
.getConnectorStatus(connectorId
)?.chargingProfiles
460 )?.sort((a
, b
) => b
.stackLevel
- a
.stackLevel
) ?? [];
461 // Get profiles on connector 0
462 if (chargingStation
.getConnectorStatus(0)?.chargingProfiles
) {
463 chargingProfiles
.push(
464 ...Utils
.cloneObject
<ChargingProfile
[]>(
465 chargingStation
.getConnectorStatus(0).chargingProfiles
466 ).sort((a
, b
) => b
.stackLevel
- a
.stackLevel
)
469 if (Utils
.isNotEmptyArray(chargingProfiles
)) {
470 const result
= ChargingStationUtils
.getLimitFromChargingProfiles(
472 chargingStation
.logPrefix()
474 if (!Utils
.isNullOrUndefined(result
)) {
475 limit
= result
?.limit
;
476 matchingChargingProfile
= result
?.matchingChargingProfile
;
477 switch (chargingStation
.getCurrentOutType()) {
480 matchingChargingProfile
.chargingSchedule
.chargingRateUnit
===
481 ChargingRateUnitType
.WATT
483 : ACElectricUtils
.powerTotal(
484 chargingStation
.getNumberOfPhases(),
485 chargingStation
.getVoltageOut(),
491 matchingChargingProfile
.chargingSchedule
.chargingRateUnit
===
492 ChargingRateUnitType
.WATT
494 : DCElectricUtils
.power(chargingStation
.getVoltageOut(), limit
);
496 const connectorMaximumPower
=
497 chargingStation
.getMaximumPower() / chargingStation
.powerDivider
;
498 if (limit
> connectorMaximumPower
) {
500 `${chargingStation.logPrefix()} Charging profile id ${
501 matchingChargingProfile.chargingProfileId
502 } limit ${limit} is greater than connector id ${connectorId} maximum ${connectorMaximumPower}: %j`,
505 limit
= connectorMaximumPower
;
512 public static getDefaultVoltageOut(
513 currentType
: CurrentType
,
517 const errorMsg
= `Unknown ${currentType} currentOutType in template file ${templateFile}, cannot define default voltage out`;
518 let defaultVoltageOut
: number;
519 switch (currentType
) {
521 defaultVoltageOut
= Voltage
.VOLTAGE_230
;
524 defaultVoltageOut
= Voltage
.VOLTAGE_400
;
527 logger
.error(`${logPrefix} ${errorMsg}`);
528 throw new BaseError(errorMsg
);
530 return defaultVoltageOut
;
533 public static getIdTagsFile(stationInfo
: ChargingStationInfo
): string | undefined {
535 stationInfo
.idTagsFile
&&
537 path
.dirname(fileURLToPath(import.meta
.url
)),
539 path
.basename(stationInfo
.idTagsFile
)
544 public static waitForChargingStationEvents
= async (
545 emitter
: EventEmitter
,
546 event
: ChargingStationWorkerMessageEvents
,
548 ): Promise
<number> => {
549 return new Promise((resolve
) => {
551 if (eventsToWait
=== 0) {
554 emitter
.on(event
, () => {
556 if (events
=== eventsToWait
) {
563 private static getConfiguredNumberOfConnectors(stationTemplate
: ChargingStationTemplate
): number {
564 let configuredMaxConnectors
: number;
565 if (Utils
.isNotEmptyArray(stationTemplate
.numberOfConnectors
) === true) {
566 const numberOfConnectors
= stationTemplate
.numberOfConnectors
as number[];
567 configuredMaxConnectors
=
568 numberOfConnectors
[Math.floor(Utils
.secureRandom() * numberOfConnectors
.length
)];
569 } else if (Utils
.isUndefined(stationTemplate
.numberOfConnectors
) === false) {
570 configuredMaxConnectors
= stationTemplate
.numberOfConnectors
as number;
571 } else if (stationTemplate
.Connectors
&& !stationTemplate
.Evses
) {
572 configuredMaxConnectors
= stationTemplate
?.Connectors
[0]
573 ? ChargingStationUtils
.getMaxNumberOfConnectors(stationTemplate
.Connectors
) - 1
574 : ChargingStationUtils
.getMaxNumberOfConnectors(stationTemplate
.Connectors
);
575 } else if (stationTemplate
.Evses
&& !stationTemplate
.Connectors
) {
576 configuredMaxConnectors
= 0;
577 for (const evse
in stationTemplate
.Evses
) {
581 configuredMaxConnectors
+= ChargingStationUtils
.getMaxNumberOfConnectors(
582 stationTemplate
.Evses
[evse
].Connectors
586 return configuredMaxConnectors
;
589 private static checkConfiguredMaxConnectors(
590 configuredMaxConnectors
: number,
594 if (configuredMaxConnectors
<= 0) {
596 `${logPrefix} Charging station information from template ${templateFile} with ${configuredMaxConnectors} connectors`
601 private static checkTemplateMaxConnectors(
602 templateMaxConnectors
: number,
606 if (templateMaxConnectors
=== 0) {
608 `${logPrefix} Charging station information from template ${templateFile} with empty connectors configuration`
610 } else if (templateMaxConnectors
< 0) {
612 `${logPrefix} Charging station information from template ${templateFile} with no connectors configuration defined`
617 private static initializeConnectorStatus(connectorStatus
: ConnectorStatus
): void {
618 connectorStatus
.availability
= AvailabilityType
.Operative
;
619 connectorStatus
.idTagLocalAuthorized
= false;
620 connectorStatus
.idTagAuthorized
= false;
621 connectorStatus
.transactionRemoteStarted
= false;
622 connectorStatus
.transactionStarted
= false;
623 connectorStatus
.energyActiveImportRegisterValue
= 0;
624 connectorStatus
.transactionEnergyActiveImportRegisterValue
= 0;
625 if (Utils
.isUndefined(connectorStatus
.chargingProfiles
)) {
626 connectorStatus
.chargingProfiles
= [];
630 private static warnDeprecatedTemplateKey(
631 template
: ChargingStationTemplate
,
634 templateFile
: string,
637 if (!Utils
.isUndefined(template
[key
])) {
638 const logMsg
= `Deprecated template key '${key}' usage in file '${templateFile}'${
639 Utils.isNotEmptyString(logMsgToAppend) ? `. ${logMsgToAppend}
` : ''
641 logger
.warn(`${logPrefix} ${logMsg}`);
642 console
.warn(chalk
.yellow(`${logMsg}`));
646 private static convertDeprecatedTemplateKey(
647 template
: ChargingStationTemplate
,
648 deprecatedKey
: string,
651 if (!Utils
.isUndefined(template
[deprecatedKey
])) {
652 template
[key
] = template
[deprecatedKey
] as unknown
;
653 delete template
[deprecatedKey
];
658 * Charging profiles should already be sorted by connector id and stack level (highest stack level has priority)
660 * @param chargingProfiles -
664 private static getLimitFromChargingProfiles(
665 chargingProfiles
: ChargingProfile
[],
669 matchingChargingProfile
: ChargingProfile
;
671 const debugLogMsg
= `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Matching charging profile found for power limitation: %j`;
672 const currentMoment
= moment();
673 const currentDate
= new Date();
674 for (const chargingProfile
of chargingProfiles
) {
676 const chargingSchedule
= chargingProfile
.chargingSchedule
;
677 if (!chargingSchedule
?.startSchedule
) {
679 `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: startSchedule is not defined in charging profile id ${chargingProfile.chargingProfileId}`
682 // Check type (recurring) and if it is already active
683 // Adjust the daily recurring schedule to today
685 chargingProfile
.chargingProfileKind
=== ChargingProfileKindType
.RECURRING
&&
686 chargingProfile
.recurrencyKind
=== RecurrencyKindType
.DAILY
&&
687 currentMoment
.isAfter(chargingSchedule
.startSchedule
)
689 if (!(chargingSchedule
?.startSchedule
instanceof Date)) {
691 `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: startSchedule is not a Date object in charging profile id ${chargingProfile.chargingProfileId}. Trying to convert it to a Date object`
693 chargingSchedule
.startSchedule
= new Date(chargingSchedule
.startSchedule
);
695 chargingSchedule
.startSchedule
.setFullYear(
696 currentDate
.getFullYear(),
697 currentDate
.getMonth(),
698 currentDate
.getDate()
700 // Check if the start of the schedule is yesterday
701 if (moment(chargingSchedule
.startSchedule
).isAfter(currentMoment
)) {
702 chargingSchedule
.startSchedule
.setDate(currentDate
.getDate() - 1);
704 } else if (moment(chargingSchedule
.startSchedule
).isAfter(currentMoment
)) {
707 // Check if the charging profile is active
709 moment(chargingSchedule
.startSchedule
)
710 .add(chargingSchedule
.duration
, 's')
711 .isAfter(currentMoment
)
713 let lastButOneSchedule
: ChargingSchedulePeriod
;
714 // Search the right schedule period
715 for (const schedulePeriod
of chargingSchedule
.chargingSchedulePeriod
) {
716 // Handling of only one period
718 chargingSchedule
.chargingSchedulePeriod
.length
=== 1 &&
719 schedulePeriod
.startPeriod
=== 0
722 limit
: schedulePeriod
.limit
,
723 matchingChargingProfile
: chargingProfile
,
725 logger
.debug(debugLogMsg
, result
);
728 // Find the right schedule period
730 moment(chargingSchedule
.startSchedule
)
731 .add(schedulePeriod
.startPeriod
, 's')
732 .isAfter(currentMoment
)
734 // Found the schedule: last but one is the correct one
736 limit
: lastButOneSchedule
.limit
,
737 matchingChargingProfile
: chargingProfile
,
739 logger
.debug(debugLogMsg
, result
);
743 lastButOneSchedule
= schedulePeriod
;
744 // Handle the last schedule period
746 schedulePeriod
.startPeriod
===
747 chargingSchedule
.chargingSchedulePeriod
[
748 chargingSchedule
.chargingSchedulePeriod
.length
- 1
752 limit
: lastButOneSchedule
.limit
,
753 matchingChargingProfile
: chargingProfile
,
755 logger
.debug(debugLogMsg
, result
);
764 private static getRandomSerialNumberSuffix(params
?: {
765 randomBytesLength
?: number;
768 const randomSerialNumberSuffix
= crypto
769 .randomBytes(params
?.randomBytesLength
?? 16)
771 if (params
?.upperCase
) {
772 return randomSerialNumberSuffix
.toUpperCase();
774 return randomSerialNumberSuffix
;