1 import { createHash
, randomBytes
} from
'node:crypto';
2 import type { EventEmitter
} from
'node:events';
3 import { basename
, dirname
, join
} 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
,
50 const moduleName
= 'ChargingStationUtils';
52 export const getChargingStationId
= (
54 stationTemplate
: ChargingStationTemplate
,
56 // In case of multiple instances: add instance index to charging station id
57 const instanceIndex
= process
.env
.CF_INSTANCE_INDEX
?? 0;
58 const idSuffix
= stationTemplate
?.nameSuffix
?? '';
59 const idStr
= `000000000${index.toString()}`;
60 return stationTemplate
?.fixedName
61 ? stationTemplate
.baseName
62 : `${stationTemplate.baseName}-${instanceIndex.toString()}${idStr.substring(
67 export const countReservableConnectors
= (connectors
: Map
<number, ConnectorStatus
>) => {
68 let reservableConnectors
= 0;
69 for (const [connectorId
, connectorStatus
] of connectors
) {
70 if (connectorId
=== 0) {
73 if (connectorStatus
.status === ConnectorStatusEnum
.Available
) {
74 ++reservableConnectors
;
77 return reservableConnectors
;
80 export const getHashId
= (index
: number, stationTemplate
: ChargingStationTemplate
): string => {
81 const chargingStationInfo
= {
82 chargePointModel
: stationTemplate
.chargePointModel
,
83 chargePointVendor
: stationTemplate
.chargePointVendor
,
84 ...(!isUndefined(stationTemplate
.chargeBoxSerialNumberPrefix
) && {
85 chargeBoxSerialNumber
: stationTemplate
.chargeBoxSerialNumberPrefix
,
87 ...(!isUndefined(stationTemplate
.chargePointSerialNumberPrefix
) && {
88 chargePointSerialNumber
: stationTemplate
.chargePointSerialNumberPrefix
,
90 ...(!isUndefined(stationTemplate
.meterSerialNumberPrefix
) && {
91 meterSerialNumber
: stationTemplate
.meterSerialNumberPrefix
,
93 ...(!isUndefined(stationTemplate
.meterType
) && {
94 meterType
: stationTemplate
.meterType
,
97 return createHash(Constants
.DEFAULT_HASH_ALGORITHM
)
98 .update(`${JSON.stringify(chargingStationInfo)}${getChargingStationId(index, stationTemplate)}`)
102 export const checkChargingStation
= (
103 chargingStation
: ChargingStation
,
106 if (chargingStation
.started
=== false && chargingStation
.starting
=== false) {
107 logger
.warn(`${logPrefix} charging station is stopped, cannot proceed`);
113 export const getPhaseRotationValue
= (
115 numberOfPhases
: number,
116 ): string | undefined => {
118 if (connectorId
=== 0 && numberOfPhases
=== 0) {
119 return `${connectorId}.${ConnectorPhaseRotation.RST}`;
120 } else if (connectorId
> 0 && numberOfPhases
=== 0) {
121 return `${connectorId}.${ConnectorPhaseRotation.NotApplicable}`;
123 } else if (connectorId
> 0 && numberOfPhases
=== 1) {
124 return `${connectorId}.${ConnectorPhaseRotation.NotApplicable}`;
125 } else if (connectorId
> 0 && numberOfPhases
=== 3) {
126 return `${connectorId}.${ConnectorPhaseRotation.RST}`;
130 export const getMaxNumberOfEvses
= (evses
: Record
<string, EvseTemplate
>): number => {
134 return Object.keys(evses
).length
;
137 const getMaxNumberOfConnectors
= (connectors
: Record
<string, ConnectorStatus
>): number => {
141 return Object.keys(connectors
).length
;
144 export const getBootConnectorStatus
= (
145 chargingStation
: ChargingStation
,
147 connectorStatus
: ConnectorStatus
,
148 ): ConnectorStatusEnum
=> {
149 let connectorBootStatus
: ConnectorStatusEnum
;
151 !connectorStatus
?.status &&
152 (chargingStation
.isChargingStationAvailable() === false ||
153 chargingStation
.isConnectorAvailable(connectorId
) === false)
155 connectorBootStatus
= ConnectorStatusEnum
.Unavailable
;
156 } else if (!connectorStatus
?.status && connectorStatus
?.bootStatus
) {
157 // Set boot status in template at startup
158 connectorBootStatus
= connectorStatus
?.bootStatus
;
159 } else if (connectorStatus
?.status) {
160 // Set previous status at startup
161 connectorBootStatus
= connectorStatus
?.status;
163 // Set default status
164 connectorBootStatus
= ConnectorStatusEnum
.Available
;
166 return connectorBootStatus
;
169 export const checkTemplate
= (
170 stationTemplate
: ChargingStationTemplate
,
172 templateFile
: string,
174 if (isNullOrUndefined(stationTemplate
)) {
175 const errorMsg
= `Failed to read charging station template file ${templateFile}`;
176 logger
.error(`${logPrefix} ${errorMsg}`);
177 throw new BaseError(errorMsg
);
179 if (isEmptyObject(stationTemplate
)) {
180 const errorMsg
= `Empty charging station information from template file ${templateFile}`;
181 logger
.error(`${logPrefix} ${errorMsg}`);
182 throw new BaseError(errorMsg
);
184 if (isEmptyObject(stationTemplate
.AutomaticTransactionGenerator
)) {
185 stationTemplate
.AutomaticTransactionGenerator
= Constants
.DEFAULT_ATG_CONFIGURATION
;
187 `${logPrefix} Empty automatic transaction generator configuration from template file ${templateFile}, set to default: %j`,
188 Constants
.DEFAULT_ATG_CONFIGURATION
,
191 if (isNullOrUndefined(stationTemplate
.idTagsFile
) || isEmptyString(stationTemplate
.idTagsFile
)) {
193 `${logPrefix} Missing id tags file in template file ${templateFile}. That can lead to issues with the Automatic Transaction Generator`,
198 export const checkConnectorsConfiguration
= (
199 stationTemplate
: ChargingStationTemplate
,
201 templateFile
: string,
203 configuredMaxConnectors
: number;
204 templateMaxConnectors
: number;
205 templateMaxAvailableConnectors
: number;
207 const configuredMaxConnectors
= getConfiguredNumberOfConnectors(stationTemplate
);
208 checkConfiguredMaxConnectors(configuredMaxConnectors
, logPrefix
, templateFile
);
209 const templateMaxConnectors
= getMaxNumberOfConnectors(stationTemplate
.Connectors
);
210 checkTemplateMaxConnectors(templateMaxConnectors
, logPrefix
, templateFile
);
211 const templateMaxAvailableConnectors
= stationTemplate
?.Connectors
[0]
212 ? templateMaxConnectors
- 1
213 : templateMaxConnectors
;
215 configuredMaxConnectors
> templateMaxAvailableConnectors
&&
216 !stationTemplate
?.randomConnectors
219 `${logPrefix} Number of connectors exceeds the number of connector configurations in template ${templateFile}, forcing random connector configurations affectation`,
221 stationTemplate
.randomConnectors
= true;
223 return { configuredMaxConnectors
, templateMaxConnectors
, templateMaxAvailableConnectors
};
226 export const checkStationInfoConnectorStatus
= (
228 connectorStatus
: ConnectorStatus
,
230 templateFile
: string,
232 if (!isNullOrUndefined(connectorStatus
?.status)) {
234 `${logPrefix} Charging station information from template ${templateFile} with connector id ${connectorId} status configuration defined, undefine it`,
236 delete connectorStatus
.status;
240 export const buildConnectorsMap
= (
241 connectors
: Record
<string, ConnectorStatus
>,
243 templateFile
: string,
244 ): Map
<number, ConnectorStatus
> => {
245 const connectorsMap
= new Map
<number, ConnectorStatus
>();
246 if (getMaxNumberOfConnectors(connectors
) > 0) {
247 for (const connector
in connectors
) {
248 const connectorStatus
= connectors
[connector
];
249 const connectorId
= convertToInt(connector
);
250 checkStationInfoConnectorStatus(connectorId
, connectorStatus
, logPrefix
, templateFile
);
251 connectorsMap
.set(connectorId
, cloneObject
<ConnectorStatus
>(connectorStatus
));
255 `${logPrefix} Charging station information from template ${templateFile} with no connectors, cannot build connectors map`,
258 return connectorsMap
;
261 export const initializeConnectorsMapStatus
= (
262 connectors
: Map
<number, ConnectorStatus
>,
265 for (const connectorId
of connectors
.keys()) {
266 if (connectorId
> 0 && connectors
.get(connectorId
)?.transactionStarted
=== true) {
268 `${logPrefix} Connector id ${connectorId} at initialization has a transaction started with id ${connectors.get(
273 if (connectorId
=== 0) {
274 connectors
.get(connectorId
).availability
= AvailabilityType
.Operative
;
275 if (isUndefined(connectors
.get(connectorId
)?.chargingProfiles
)) {
276 connectors
.get(connectorId
).chargingProfiles
= [];
280 isNullOrUndefined(connectors
.get(connectorId
)?.transactionStarted
)
282 initializeConnectorStatus(connectors
.get(connectorId
));
287 export const resetConnectorStatus
= (connectorStatus
: ConnectorStatus
): void => {
288 connectorStatus
.idTagLocalAuthorized
= false;
289 connectorStatus
.idTagAuthorized
= false;
290 connectorStatus
.transactionRemoteStarted
= false;
291 connectorStatus
.transactionStarted
= false;
292 delete connectorStatus
?.localAuthorizeIdTag
;
293 delete connectorStatus
?.authorizeIdTag
;
294 delete connectorStatus
?.transactionId
;
295 delete connectorStatus
?.transactionIdTag
;
296 connectorStatus
.transactionEnergyActiveImportRegisterValue
= 0;
297 delete connectorStatus
?.transactionBeginMeterValue
;
300 export const createBootNotificationRequest
= (
301 stationInfo
: ChargingStationInfo
,
302 bootReason
: BootReasonEnumType
= BootReasonEnumType
.PowerUp
,
303 ): BootNotificationRequest
=> {
304 const ocppVersion
= stationInfo
.ocppVersion
?? OCPPVersion
.VERSION_16
;
305 switch (ocppVersion
) {
306 case OCPPVersion
.VERSION_16
:
308 chargePointModel
: stationInfo
.chargePointModel
,
309 chargePointVendor
: stationInfo
.chargePointVendor
,
310 ...(!isUndefined(stationInfo
.chargeBoxSerialNumber
) && {
311 chargeBoxSerialNumber
: stationInfo
.chargeBoxSerialNumber
,
313 ...(!isUndefined(stationInfo
.chargePointSerialNumber
) && {
314 chargePointSerialNumber
: stationInfo
.chargePointSerialNumber
,
316 ...(!isUndefined(stationInfo
.firmwareVersion
) && {
317 firmwareVersion
: stationInfo
.firmwareVersion
,
319 ...(!isUndefined(stationInfo
.iccid
) && { iccid
: stationInfo
.iccid
}),
320 ...(!isUndefined(stationInfo
.imsi
) && { imsi
: stationInfo
.imsi
}),
321 ...(!isUndefined(stationInfo
.meterSerialNumber
) && {
322 meterSerialNumber
: stationInfo
.meterSerialNumber
,
324 ...(!isUndefined(stationInfo
.meterType
) && {
325 meterType
: stationInfo
.meterType
,
327 } as OCPP16BootNotificationRequest
;
328 case OCPPVersion
.VERSION_20
:
329 case OCPPVersion
.VERSION_201
:
333 model
: stationInfo
.chargePointModel
,
334 vendorName
: stationInfo
.chargePointVendor
,
335 ...(!isUndefined(stationInfo
.firmwareVersion
) && {
336 firmwareVersion
: stationInfo
.firmwareVersion
,
338 ...(!isUndefined(stationInfo
.chargeBoxSerialNumber
) && {
339 serialNumber
: stationInfo
.chargeBoxSerialNumber
,
341 ...((!isUndefined(stationInfo
.iccid
) || !isUndefined(stationInfo
.imsi
)) && {
343 ...(!isUndefined(stationInfo
.iccid
) && { iccid
: stationInfo
.iccid
}),
344 ...(!isUndefined(stationInfo
.imsi
) && { imsi
: stationInfo
.imsi
}),
348 } as OCPP20BootNotificationRequest
;
352 export const warnTemplateKeysDeprecation
= (
353 stationTemplate
: ChargingStationTemplate
,
355 templateFile
: string,
357 const templateKeys
: { key
: string; deprecatedKey
: string }[] = [
358 { key
: 'supervisionUrls', deprecatedKey
: 'supervisionUrl' },
359 { key
: 'idTagsFile', deprecatedKey
: 'authorizationFile' },
361 for (const templateKey
of templateKeys
) {
362 warnDeprecatedTemplateKey(
364 templateKey
.deprecatedKey
,
367 `Use '${templateKey.key}' instead`,
369 convertDeprecatedTemplateKey(stationTemplate
, templateKey
.deprecatedKey
, templateKey
.key
);
373 export const stationTemplateToStationInfo
= (
374 stationTemplate
: ChargingStationTemplate
,
375 ): ChargingStationInfo
=> {
376 stationTemplate
= 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 export const 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 ? getRandomSerialNumberSuffix({
403 upperCase
: params
.randomSerialNumberUpperCase
,
406 isNotEmptyString(stationTemplate
?.chargePointSerialNumberPrefix
) &&
407 (stationInfo
.chargePointSerialNumber
= `${stationTemplate.chargePointSerialNumberPrefix}${serialNumberSuffix}`);
408 isNotEmptyString(stationTemplate
?.chargeBoxSerialNumberPrefix
) &&
409 (stationInfo
.chargeBoxSerialNumber
= `${stationTemplate.chargeBoxSerialNumberPrefix}${serialNumberSuffix}`);
410 isNotEmptyString(stationTemplate
?.meterSerialNumberPrefix
) &&
411 (stationInfo
.meterSerialNumber
= `${stationTemplate.meterSerialNumberPrefix}${serialNumberSuffix}`);
414 export const 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 export const getAmperageLimitationUnitDivider
= (stationInfo
: ChargingStationInfo
): number => {
437 switch (stationInfo
.amperageLimitationUnit
) {
438 case AmpereUnits
.DECI_AMPERE
:
441 case AmpereUnits
.CENTI_AMPERE
:
444 case AmpereUnits
.MILLI_AMPERE
:
451 export const 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 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 ...cloneObject
<ChargingProfile
[]>(
465 chargingStation
.getConnectorStatus(0).chargingProfiles
,
466 ).sort((a
, b
) => b
.stackLevel
- a
.stackLevel
),
469 if (isNotEmptyArray(chargingProfiles
)) {
470 const result
= getLimitFromChargingProfiles(chargingProfiles
, chargingStation
.logPrefix());
471 if (!isNullOrUndefined(result
)) {
472 limit
= result
?.limit
;
473 matchingChargingProfile
= result
?.matchingChargingProfile
;
474 switch (chargingStation
.getCurrentOutType()) {
477 matchingChargingProfile
.chargingSchedule
.chargingRateUnit
=== ChargingRateUnitType
.WATT
479 : ACElectricUtils
.powerTotal(
480 chargingStation
.getNumberOfPhases(),
481 chargingStation
.getVoltageOut(),
487 matchingChargingProfile
.chargingSchedule
.chargingRateUnit
=== ChargingRateUnitType
.WATT
489 : DCElectricUtils
.power(chargingStation
.getVoltageOut(), limit
);
491 const connectorMaximumPower
=
492 chargingStation
.getMaximumPower() / chargingStation
.powerDivider
;
493 if (limit
> connectorMaximumPower
) {
495 `${chargingStation.logPrefix()} Charging profile id ${
496 matchingChargingProfile.chargingProfileId
497 } limit ${limit} is greater than connector id ${connectorId} maximum ${connectorMaximumPower}: %j`,
500 limit
= connectorMaximumPower
;
507 export const getDefaultVoltageOut
= (
508 currentType
: CurrentType
,
510 templateFile
: string,
512 const errorMsg
= `Unknown ${currentType} currentOutType in template file ${templateFile}, cannot define default voltage out`;
513 let defaultVoltageOut
: number;
514 switch (currentType
) {
516 defaultVoltageOut
= Voltage
.VOLTAGE_230
;
519 defaultVoltageOut
= Voltage
.VOLTAGE_400
;
522 logger
.error(`${logPrefix} ${errorMsg}`);
523 throw new BaseError(errorMsg
);
525 return defaultVoltageOut
;
528 export const getIdTagsFile
= (stationInfo
: ChargingStationInfo
): string | undefined => {
530 stationInfo
.idTagsFile
&&
531 join(dirname(fileURLToPath(import.meta
.url
)), 'assets', basename(stationInfo
.idTagsFile
))
535 export const waitChargingStationEvents
= async (
536 emitter
: EventEmitter
,
537 event
: ChargingStationWorkerMessageEvents
,
538 eventsToWait
: number,
539 ): Promise
<number> => {
540 return new Promise((resolve
) => {
542 if (eventsToWait
=== 0) {
545 emitter
.on(event
, () => {
547 if (events
=== eventsToWait
) {
554 const getConfiguredNumberOfConnectors
= (stationTemplate
: ChargingStationTemplate
): number => {
555 let configuredMaxConnectors
: number;
556 if (isNotEmptyArray(stationTemplate
.numberOfConnectors
) === true) {
557 const numberOfConnectors
= stationTemplate
.numberOfConnectors
as number[];
558 configuredMaxConnectors
=
559 numberOfConnectors
[Math.floor(secureRandom() * numberOfConnectors
.length
)];
560 } else if (isUndefined(stationTemplate
.numberOfConnectors
) === false) {
561 configuredMaxConnectors
= stationTemplate
.numberOfConnectors
as number;
562 } else if (stationTemplate
.Connectors
&& !stationTemplate
.Evses
) {
563 configuredMaxConnectors
= stationTemplate
?.Connectors
[0]
564 ? getMaxNumberOfConnectors(stationTemplate
.Connectors
) - 1
565 : getMaxNumberOfConnectors(stationTemplate
.Connectors
);
566 } else if (stationTemplate
.Evses
&& !stationTemplate
.Connectors
) {
567 configuredMaxConnectors
= 0;
568 for (const evse
in stationTemplate
.Evses
) {
572 configuredMaxConnectors
+= getMaxNumberOfConnectors(stationTemplate
.Evses
[evse
].Connectors
);
575 return configuredMaxConnectors
;
578 const checkConfiguredMaxConnectors
= (
579 configuredMaxConnectors
: number,
581 templateFile
: string,
583 if (configuredMaxConnectors
<= 0) {
585 `${logPrefix} Charging station information from template ${templateFile} with ${configuredMaxConnectors} connectors`,
590 const checkTemplateMaxConnectors
= (
591 templateMaxConnectors
: number,
593 templateFile
: string,
595 if (templateMaxConnectors
=== 0) {
597 `${logPrefix} Charging station information from template ${templateFile} with empty connectors configuration`,
599 } else if (templateMaxConnectors
< 0) {
601 `${logPrefix} Charging station information from template ${templateFile} with no connectors configuration defined`,
606 const initializeConnectorStatus
= (connectorStatus
: ConnectorStatus
): void => {
607 connectorStatus
.availability
= AvailabilityType
.Operative
;
608 connectorStatus
.idTagLocalAuthorized
= false;
609 connectorStatus
.idTagAuthorized
= false;
610 connectorStatus
.transactionRemoteStarted
= false;
611 connectorStatus
.transactionStarted
= false;
612 connectorStatus
.energyActiveImportRegisterValue
= 0;
613 connectorStatus
.transactionEnergyActiveImportRegisterValue
= 0;
614 if (isUndefined(connectorStatus
.chargingProfiles
)) {
615 connectorStatus
.chargingProfiles
= [];
619 const warnDeprecatedTemplateKey
= (
620 template
: ChargingStationTemplate
,
623 templateFile
: string,
626 if (!isUndefined(template
[key
])) {
627 const logMsg
= `Deprecated template key '${key}' usage in file '${templateFile}'${
628 isNotEmptyString(logMsgToAppend) ? `. ${logMsgToAppend}
` : ''
630 logger
.warn(`${logPrefix} ${logMsg}`);
631 console
.warn(chalk
.yellow(`${logMsg}`));
635 const convertDeprecatedTemplateKey
= (
636 template
: ChargingStationTemplate
,
637 deprecatedKey
: string,
640 if (!isUndefined(template
[deprecatedKey
])) {
641 template
[key
] = template
[deprecatedKey
] as unknown
;
642 delete template
[deprecatedKey
];
647 * Charging profiles should already be sorted by connector id and stack level (highest stack level has priority)
649 * @param chargingProfiles -
653 const getLimitFromChargingProfiles
= (
654 chargingProfiles
: ChargingProfile
[],
658 matchingChargingProfile
: ChargingProfile
;
660 const debugLogMsg
= `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Matching charging profile found for power limitation: %j`;
661 const currentMoment
= moment();
662 const currentDate
= new Date();
663 for (const chargingProfile
of chargingProfiles
) {
665 const chargingSchedule
= chargingProfile
.chargingSchedule
;
666 if (!chargingSchedule
?.startSchedule
) {
668 `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: startSchedule is not defined in charging profile id ${chargingProfile.chargingProfileId}`,
671 // Check type (recurring) and if it is already active
672 // Adjust the daily recurring schedule to today
674 chargingProfile
.chargingProfileKind
=== ChargingProfileKindType
.RECURRING
&&
675 chargingProfile
.recurrencyKind
=== RecurrencyKindType
.DAILY
&&
676 currentMoment
.isAfter(chargingSchedule
.startSchedule
)
678 if (!(chargingSchedule
?.startSchedule
instanceof Date)) {
680 `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: startSchedule is not a Date object in charging profile id ${chargingProfile.chargingProfileId}. Trying to convert it to a Date object`,
682 chargingSchedule
.startSchedule
= new Date(chargingSchedule
.startSchedule
);
684 chargingSchedule
.startSchedule
.setFullYear(
685 currentDate
.getFullYear(),
686 currentDate
.getMonth(),
687 currentDate
.getDate(),
689 // Check if the start of the schedule is yesterday
690 if (moment(chargingSchedule
.startSchedule
).isAfter(currentMoment
)) {
691 chargingSchedule
.startSchedule
.setDate(currentDate
.getDate() - 1);
693 } else if (moment(chargingSchedule
.startSchedule
).isAfter(currentMoment
)) {
696 // Check if the charging profile is active
698 moment(chargingSchedule
.startSchedule
)
699 .add(chargingSchedule
.duration
, 's')
700 .isAfter(currentMoment
)
702 let lastButOneSchedule
: ChargingSchedulePeriod
;
703 // Search the right schedule period
704 for (const schedulePeriod
of chargingSchedule
.chargingSchedulePeriod
) {
705 // Handling of only one period
707 chargingSchedule
.chargingSchedulePeriod
.length
=== 1 &&
708 schedulePeriod
.startPeriod
=== 0
711 limit
: schedulePeriod
.limit
,
712 matchingChargingProfile
: chargingProfile
,
714 logger
.debug(debugLogMsg
, result
);
717 // Find the right schedule period
719 moment(chargingSchedule
.startSchedule
)
720 .add(schedulePeriod
.startPeriod
, 's')
721 .isAfter(currentMoment
)
723 // Found the schedule: last but one is the correct one
725 limit
: lastButOneSchedule
.limit
,
726 matchingChargingProfile
: chargingProfile
,
728 logger
.debug(debugLogMsg
, result
);
732 lastButOneSchedule
= schedulePeriod
;
733 // Handle the last schedule period
735 schedulePeriod
.startPeriod
===
736 chargingSchedule
.chargingSchedulePeriod
[
737 chargingSchedule
.chargingSchedulePeriod
.length
- 1
741 limit
: lastButOneSchedule
.limit
,
742 matchingChargingProfile
: chargingProfile
,
744 logger
.debug(debugLogMsg
, result
);
753 const getRandomSerialNumberSuffix
= (params
?: {
754 randomBytesLength
?: number;
757 const randomSerialNumberSuffix
= randomBytes(params
?.randomBytesLength
?? 16).toString('hex');
758 if (params
?.upperCase
) {
759 return randomSerialNumberSuffix
.toUpperCase();
761 return randomSerialNumberSuffix
;