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
'./internal';
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
,
40 import { WorkerProcessType
} from
'../worker';
42 const moduleName
= 'ChargingStationUtils';
44 export class ChargingStationUtils
{
45 private constructor() {
46 // This is intentional
49 public static getChargingStationId(
51 stationTemplate
: ChargingStationTemplate
53 // In case of multiple instances: add instance index to charging station id
54 const instanceIndex
= process
.env
.CF_INSTANCE_INDEX
?? 0;
55 const idSuffix
= stationTemplate
?.nameSuffix
?? '';
56 const idStr
= `000000000${index.toString()}`;
57 return stationTemplate
?.fixedName
58 ? stationTemplate
.baseName
59 : `${stationTemplate.baseName}-${instanceIndex.toString()}${idStr.substring(
64 public static getHashId(index
: number, stationTemplate
: ChargingStationTemplate
): string {
65 const chargingStationInfo
= {
66 chargePointModel
: stationTemplate
.chargePointModel
,
67 chargePointVendor
: stationTemplate
.chargePointVendor
,
68 ...(!Utils
.isUndefined(stationTemplate
.chargeBoxSerialNumberPrefix
) && {
69 chargeBoxSerialNumber
: stationTemplate
.chargeBoxSerialNumberPrefix
,
71 ...(!Utils
.isUndefined(stationTemplate
.chargePointSerialNumberPrefix
) && {
72 chargePointSerialNumber
: stationTemplate
.chargePointSerialNumberPrefix
,
74 ...(!Utils
.isUndefined(stationTemplate
.meterSerialNumberPrefix
) && {
75 meterSerialNumber
: stationTemplate
.meterSerialNumberPrefix
,
77 ...(!Utils
.isUndefined(stationTemplate
.meterType
) && {
78 meterType
: stationTemplate
.meterType
,
82 .createHash(Constants
.DEFAULT_HASH_ALGORITHM
)
84 `${JSON.stringify(chargingStationInfo)}${ChargingStationUtils.getChargingStationId(
92 public static checkChargingStation(chargingStation
: ChargingStation
, logPrefix
: string): boolean {
93 if (chargingStation
.started
=== false && chargingStation
.starting
=== false) {
94 logger
.warn(`${logPrefix} charging station is stopped, cannot proceed`);
100 public static getPhaseRotationValue(
102 numberOfPhases
: number
103 ): string | undefined {
105 if (connectorId
=== 0 && numberOfPhases
=== 0) {
106 return `${connectorId}.${ConnectorPhaseRotation.RST}`;
107 } else if (connectorId
> 0 && numberOfPhases
=== 0) {
108 return `${connectorId}.${ConnectorPhaseRotation.NotApplicable}`;
110 } else if (connectorId
> 0 && numberOfPhases
=== 1) {
111 return `${connectorId}.${ConnectorPhaseRotation.NotApplicable}`;
112 } else if (connectorId
> 0 && numberOfPhases
=== 3) {
113 return `${connectorId}.${ConnectorPhaseRotation.RST}`;
117 public static getMaxNumberOfEvses(evses
: Record
<string, EvseTemplate
>): number {
121 return Object.keys(evses
).length
;
124 public static getMaxNumberOfConnectors(connectors
: Record
<string, ConnectorStatus
>): number {
128 return Object.keys(connectors
).length
;
131 public static getBootConnectorStatus(
132 chargingStation
: ChargingStation
,
134 connectorStatus
: ConnectorStatus
135 ): ConnectorStatusEnum
{
136 let connectorBootStatus
: ConnectorStatusEnum
;
138 !connectorStatus
?.status &&
139 (chargingStation
.isChargingStationAvailable() === false ||
140 chargingStation
.isConnectorAvailable(connectorId
) === false)
142 connectorBootStatus
= ConnectorStatusEnum
.Unavailable
;
143 } else if (!connectorStatus
?.status && connectorStatus
?.bootStatus
) {
144 // Set boot status in template at startup
145 connectorBootStatus
= connectorStatus
?.bootStatus
;
146 } else if (connectorStatus
?.status) {
147 // Set previous status at startup
148 connectorBootStatus
= connectorStatus
?.status;
150 // Set default status
151 connectorBootStatus
= ConnectorStatusEnum
.Available
;
153 return connectorBootStatus
;
156 public static checkTemplateFile(
157 stationTemplate
: ChargingStationTemplate
,
161 if (Utils
.isNullOrUndefined(stationTemplate
)) {
162 const errorMsg
= `Failed to read charging station template file ${templateFile}`;
163 logger
.error(`${logPrefix} ${errorMsg}`);
164 throw new BaseError(errorMsg
);
166 if (Utils
.isEmptyObject(stationTemplate
)) {
167 const errorMsg
= `Empty charging station information from template file ${templateFile}`;
168 logger
.error(`${logPrefix} ${errorMsg}`);
169 throw new BaseError(errorMsg
);
173 public static checkConnectorsConfiguration(
174 stationTemplate
: ChargingStationTemplate
,
178 configuredMaxConnectors
: number;
179 templateMaxConnectors
: number;
180 templateMaxAvailableConnectors
: number;
182 const configuredMaxConnectors
=
183 ChargingStationUtils
.getConfiguredNumberOfConnectors(stationTemplate
);
184 ChargingStationUtils
.checkConfiguredMaxConnectors(
185 configuredMaxConnectors
,
189 const templateMaxConnectors
= ChargingStationUtils
.getMaxNumberOfConnectors(
190 stationTemplate
.Connectors
192 ChargingStationUtils
.checkTemplateMaxConnectors(templateMaxConnectors
, logPrefix
, templateFile
);
193 const templateMaxAvailableConnectors
= stationTemplate
?.Connectors
[0]
194 ? templateMaxConnectors
- 1
195 : templateMaxConnectors
;
197 configuredMaxConnectors
> templateMaxAvailableConnectors
&&
198 !stationTemplate
?.randomConnectors
201 `${logPrefix} Number of connectors exceeds the number of connector configurations in template ${templateFile}, forcing random connector configurations affectation`
203 stationTemplate
.randomConnectors
= true;
205 return { configuredMaxConnectors
, templateMaxConnectors
, templateMaxAvailableConnectors
};
208 public static checkStationInfoConnectorStatus(
210 connectorStatus
: ConnectorStatus
,
214 if (!Utils
.isNullOrUndefined(connectorStatus
?.status)) {
216 `${logPrefix} Charging station information from template ${templateFile} with connector id ${connectorId} status configuration defined, undefine it`
218 delete connectorStatus
.status;
222 public static buildConnectorsMap(
223 connectors
: Record
<string, ConnectorStatus
>,
226 ): Map
<number, ConnectorStatus
> {
227 const connectorsMap
= new Map
<number, ConnectorStatus
>();
228 if (ChargingStationUtils
.getMaxNumberOfConnectors(connectors
) > 0) {
229 for (const connector
in connectors
) {
230 const connectorStatus
= connectors
[connector
];
231 const connectorId
= Utils
.convertToInt(connector
);
232 ChargingStationUtils
.checkStationInfoConnectorStatus(
238 connectorsMap
.set(connectorId
, Utils
.cloneObject
<ConnectorStatus
>(connectorStatus
));
242 `${logPrefix} Charging station information from template ${templateFile} with no connectors, cannot build connectors map`
245 return connectorsMap
;
248 public static initializeConnectorsMapStatus(
249 connectors
: Map
<number, ConnectorStatus
>,
252 for (const connectorId
of connectors
.keys()) {
253 if (connectorId
> 0 && connectors
.get(connectorId
)?.transactionStarted
=== true) {
255 `${logPrefix} Connector id ${connectorId} at initialization has a transaction started with id ${
256 connectors.get(connectorId)?.transactionId
260 if (connectorId
=== 0) {
261 connectors
.get(connectorId
).availability
= AvailabilityType
.Operative
;
262 if (Utils
.isUndefined(connectors
.get(connectorId
)?.chargingProfiles
)) {
263 connectors
.get(connectorId
).chargingProfiles
= [];
267 Utils
.isNullOrUndefined(connectors
.get(connectorId
)?.transactionStarted
)
269 ChargingStationUtils
.initializeConnectorStatus(connectors
.get(connectorId
));
274 public static resetConnectorStatus(connectorStatus
: ConnectorStatus
): void {
275 connectorStatus
.idTagLocalAuthorized
= false;
276 connectorStatus
.idTagAuthorized
= false;
277 connectorStatus
.transactionRemoteStarted
= false;
278 connectorStatus
.transactionStarted
= false;
279 delete connectorStatus
?.localAuthorizeIdTag
;
280 delete connectorStatus
?.authorizeIdTag
;
281 delete connectorStatus
?.transactionId
;
282 delete connectorStatus
?.transactionIdTag
;
283 connectorStatus
.transactionEnergyActiveImportRegisterValue
= 0;
284 delete connectorStatus
?.transactionBeginMeterValue
;
287 public static createBootNotificationRequest(
288 stationInfo
: ChargingStationInfo
,
289 bootReason
: BootReasonEnumType
= BootReasonEnumType
.PowerUp
290 ): BootNotificationRequest
{
291 const ocppVersion
= stationInfo
.ocppVersion
?? OCPPVersion
.VERSION_16
;
292 switch (ocppVersion
) {
293 case OCPPVersion
.VERSION_16
:
295 chargePointModel
: stationInfo
.chargePointModel
,
296 chargePointVendor
: stationInfo
.chargePointVendor
,
297 ...(!Utils
.isUndefined(stationInfo
.chargeBoxSerialNumber
) && {
298 chargeBoxSerialNumber
: stationInfo
.chargeBoxSerialNumber
,
300 ...(!Utils
.isUndefined(stationInfo
.chargePointSerialNumber
) && {
301 chargePointSerialNumber
: stationInfo
.chargePointSerialNumber
,
303 ...(!Utils
.isUndefined(stationInfo
.firmwareVersion
) && {
304 firmwareVersion
: stationInfo
.firmwareVersion
,
306 ...(!Utils
.isUndefined(stationInfo
.iccid
) && { iccid
: stationInfo
.iccid
}),
307 ...(!Utils
.isUndefined(stationInfo
.imsi
) && { imsi
: stationInfo
.imsi
}),
308 ...(!Utils
.isUndefined(stationInfo
.meterSerialNumber
) && {
309 meterSerialNumber
: stationInfo
.meterSerialNumber
,
311 ...(!Utils
.isUndefined(stationInfo
.meterType
) && {
312 meterType
: stationInfo
.meterType
,
314 } as OCPP16BootNotificationRequest
;
315 case OCPPVersion
.VERSION_20
:
316 case OCPPVersion
.VERSION_201
:
320 model
: stationInfo
.chargePointModel
,
321 vendorName
: stationInfo
.chargePointVendor
,
322 ...(!Utils
.isUndefined(stationInfo
.firmwareVersion
) && {
323 firmwareVersion
: stationInfo
.firmwareVersion
,
325 ...(!Utils
.isUndefined(stationInfo
.chargeBoxSerialNumber
) && {
326 serialNumber
: stationInfo
.chargeBoxSerialNumber
,
328 ...((!Utils
.isUndefined(stationInfo
.iccid
) || !Utils
.isUndefined(stationInfo
.imsi
)) && {
330 ...(!Utils
.isUndefined(stationInfo
.iccid
) && { iccid
: stationInfo
.iccid
}),
331 ...(!Utils
.isUndefined(stationInfo
.imsi
) && { imsi
: stationInfo
.imsi
}),
335 } as OCPP20BootNotificationRequest
;
339 public static workerPoolInUse(): boolean {
340 return [WorkerProcessType
.dynamicPool
, WorkerProcessType
.staticPool
].includes(
341 Configuration
.getWorker().processType
345 public static workerDynamicPoolInUse(): boolean {
346 return Configuration
.getWorker().processType
=== WorkerProcessType
.dynamicPool
;
349 public static warnTemplateKeysDeprecation(
350 stationTemplate
: ChargingStationTemplate
,
354 const templateKeys
: { key
: string; deprecatedKey
: string }[] = [
355 { key
: 'supervisionUrls', deprecatedKey
: 'supervisionUrl' },
356 { key
: 'idTagsFile', deprecatedKey
: 'authorizationFile' },
358 for (const templateKey
of templateKeys
) {
359 ChargingStationUtils
.warnDeprecatedTemplateKey(
361 templateKey
.deprecatedKey
,
364 `Use '${templateKey.key}' instead`
366 ChargingStationUtils
.convertDeprecatedTemplateKey(
368 templateKey
.deprecatedKey
,
374 public static stationTemplateToStationInfo(
375 stationTemplate
: ChargingStationTemplate
376 ): ChargingStationInfo
{
377 stationTemplate
= Utils
.cloneObject(stationTemplate
);
378 delete stationTemplate
.power
;
379 delete stationTemplate
.powerUnit
;
380 delete stationTemplate
?.Connectors
;
381 delete stationTemplate
?.Evses
;
382 delete stationTemplate
.Configuration
;
383 delete stationTemplate
.AutomaticTransactionGenerator
;
384 delete stationTemplate
.chargeBoxSerialNumberPrefix
;
385 delete stationTemplate
.chargePointSerialNumberPrefix
;
386 delete stationTemplate
.meterSerialNumberPrefix
;
387 return stationTemplate
as unknown
as ChargingStationInfo
;
390 public static createSerialNumber(
391 stationTemplate
: ChargingStationTemplate
,
392 stationInfo
: ChargingStationInfo
,
394 randomSerialNumberUpperCase
?: boolean;
395 randomSerialNumber
?: boolean;
397 randomSerialNumberUpperCase
: true,
398 randomSerialNumber
: true,
401 params
= { ...{ randomSerialNumberUpperCase
: true, randomSerialNumber
: true }, ...params
};
402 const serialNumberSuffix
= params
?.randomSerialNumber
403 ? ChargingStationUtils
.getRandomSerialNumberSuffix({
404 upperCase
: params
.randomSerialNumberUpperCase
,
407 Utils
.isNotEmptyString(stationTemplate
?.chargePointSerialNumberPrefix
) &&
408 (stationInfo
.chargePointSerialNumber
= `${stationTemplate.chargePointSerialNumberPrefix}${serialNumberSuffix}`);
409 Utils
.isNotEmptyString(stationTemplate
?.chargeBoxSerialNumberPrefix
) &&
410 (stationInfo
.chargeBoxSerialNumber
= `${stationTemplate.chargeBoxSerialNumberPrefix}${serialNumberSuffix}`);
411 Utils
.isNotEmptyString(stationTemplate
?.meterSerialNumberPrefix
) &&
412 (stationInfo
.meterSerialNumber
= `${stationTemplate.meterSerialNumberPrefix}${serialNumberSuffix}`);
415 public static propagateSerialNumber(
416 stationTemplate
: ChargingStationTemplate
,
417 stationInfoSrc
: ChargingStationInfo
,
418 stationInfoDst
: ChargingStationInfo
420 if (!stationInfoSrc
|| !stationTemplate
) {
422 'Missing charging station template or existing configuration to propagate serial number'
425 stationTemplate
?.chargePointSerialNumberPrefix
&& stationInfoSrc
?.chargePointSerialNumber
426 ? (stationInfoDst
.chargePointSerialNumber
= stationInfoSrc
.chargePointSerialNumber
)
427 : stationInfoDst
?.chargePointSerialNumber
&& delete stationInfoDst
.chargePointSerialNumber
;
428 stationTemplate
?.chargeBoxSerialNumberPrefix
&& stationInfoSrc
?.chargeBoxSerialNumber
429 ? (stationInfoDst
.chargeBoxSerialNumber
= stationInfoSrc
.chargeBoxSerialNumber
)
430 : stationInfoDst
?.chargeBoxSerialNumber
&& delete stationInfoDst
.chargeBoxSerialNumber
;
431 stationTemplate
?.meterSerialNumberPrefix
&& stationInfoSrc
?.meterSerialNumber
432 ? (stationInfoDst
.meterSerialNumber
= stationInfoSrc
.meterSerialNumber
)
433 : stationInfoDst
?.meterSerialNumber
&& delete stationInfoDst
.meterSerialNumber
;
436 public static getAmperageLimitationUnitDivider(stationInfo
: ChargingStationInfo
): number {
438 switch (stationInfo
.amperageLimitationUnit
) {
439 case AmpereUnits
.DECI_AMPERE
:
442 case AmpereUnits
.CENTI_AMPERE
:
445 case AmpereUnits
.MILLI_AMPERE
:
452 public static getChargingStationConnectorChargingProfilesPowerLimit(
453 chargingStation
: ChargingStation
,
455 ): number | undefined {
456 let limit
: number, matchingChargingProfile
: ChargingProfile
;
457 // Get charging profiles for connector and sort by stack level
458 const chargingProfiles
=
459 Utils
.cloneObject(chargingStation
.getConnectorStatus(connectorId
)?.chargingProfiles
)?.sort(
460 (a
, b
) => b
.stackLevel
- a
.stackLevel
462 // Get profiles on connector 0
463 if (chargingStation
.getConnectorStatus(0)?.chargingProfiles
) {
464 chargingProfiles
.push(
465 ...Utils
.cloneObject(chargingStation
.getConnectorStatus(0).chargingProfiles
).sort(
466 (a
, b
) => b
.stackLevel
- a
.stackLevel
470 if (Utils
.isNotEmptyArray(chargingProfiles
)) {
471 const result
= ChargingStationUtils
.getLimitFromChargingProfiles(
473 chargingStation
.logPrefix()
475 if (!Utils
.isNullOrUndefined(result
)) {
476 limit
= result
?.limit
;
477 matchingChargingProfile
= result
?.matchingChargingProfile
;
478 switch (chargingStation
.getCurrentOutType()) {
481 matchingChargingProfile
.chargingSchedule
.chargingRateUnit
===
482 ChargingRateUnitType
.WATT
484 : ACElectricUtils
.powerTotal(
485 chargingStation
.getNumberOfPhases(),
486 chargingStation
.getVoltageOut(),
492 matchingChargingProfile
.chargingSchedule
.chargingRateUnit
===
493 ChargingRateUnitType
.WATT
495 : DCElectricUtils
.power(chargingStation
.getVoltageOut(), limit
);
497 const connectorMaximumPower
=
498 chargingStation
.getMaximumPower() / chargingStation
.powerDivider
;
499 if (limit
> connectorMaximumPower
) {
501 `${chargingStation.logPrefix()} Charging profile id ${
502 matchingChargingProfile.chargingProfileId
503 } limit ${limit} is greater than connector id ${connectorId} maximum ${connectorMaximumPower}: %j`,
506 limit
= connectorMaximumPower
;
513 public static getDefaultVoltageOut(
514 currentType
: CurrentType
,
518 const errorMsg
= `Unknown ${currentType} currentOutType in template file ${templateFile}, cannot define default voltage out`;
519 let defaultVoltageOut
: number;
520 switch (currentType
) {
522 defaultVoltageOut
= Voltage
.VOLTAGE_230
;
525 defaultVoltageOut
= Voltage
.VOLTAGE_400
;
528 logger
.error(`${logPrefix} ${errorMsg}`);
529 throw new BaseError(errorMsg
);
531 return defaultVoltageOut
;
534 public static getIdTagsFile(stationInfo
: ChargingStationInfo
): string | undefined {
536 stationInfo
.idTagsFile
&&
538 path
.resolve(path
.dirname(fileURLToPath(import.meta
.url
)), '../'),
540 path
.basename(stationInfo
.idTagsFile
)
545 private static getConfiguredNumberOfConnectors(stationTemplate
: ChargingStationTemplate
): number {
546 let configuredMaxConnectors
: number;
547 if (Utils
.isNotEmptyArray(stationTemplate
.numberOfConnectors
) === true) {
548 const numberOfConnectors
= stationTemplate
.numberOfConnectors
as number[];
549 configuredMaxConnectors
=
550 numberOfConnectors
[Math.floor(Utils
.secureRandom() * numberOfConnectors
.length
)];
551 } else if (Utils
.isUndefined(stationTemplate
.numberOfConnectors
) === false) {
552 configuredMaxConnectors
= stationTemplate
.numberOfConnectors
as number;
553 } else if (stationTemplate
.Connectors
&& !stationTemplate
.Evses
) {
554 configuredMaxConnectors
= stationTemplate
?.Connectors
[0]
555 ? ChargingStationUtils
.getMaxNumberOfConnectors(stationTemplate
.Connectors
) - 1
556 : ChargingStationUtils
.getMaxNumberOfConnectors(stationTemplate
.Connectors
);
557 } else if (stationTemplate
.Evses
&& !stationTemplate
.Connectors
) {
558 configuredMaxConnectors
= 0;
559 for (const evse
in stationTemplate
.Evses
) {
563 configuredMaxConnectors
+= ChargingStationUtils
.getMaxNumberOfConnectors(
564 stationTemplate
.Evses
[evse
].Connectors
568 return configuredMaxConnectors
;
571 private static checkConfiguredMaxConnectors(
572 configuredMaxConnectors
: number,
576 if (configuredMaxConnectors
<= 0) {
578 `${logPrefix} Charging station information from template ${templateFile} with ${configuredMaxConnectors} connectors`
583 private static checkTemplateMaxConnectors(
584 templateMaxConnectors
: number,
588 if (templateMaxConnectors
=== 0) {
590 `${logPrefix} Charging station information from template ${templateFile} with empty connectors configuration`
592 } else if (templateMaxConnectors
< 0) {
594 `${logPrefix} Charging station information from template ${templateFile} with no connectors configuration defined`
599 private static initializeConnectorStatus(connectorStatus
: ConnectorStatus
): void {
600 connectorStatus
.availability
= AvailabilityType
.Operative
;
601 connectorStatus
.idTagLocalAuthorized
= false;
602 connectorStatus
.idTagAuthorized
= false;
603 connectorStatus
.transactionRemoteStarted
= false;
604 connectorStatus
.transactionStarted
= false;
605 connectorStatus
.energyActiveImportRegisterValue
= 0;
606 connectorStatus
.transactionEnergyActiveImportRegisterValue
= 0;
607 if (Utils
.isUndefined(connectorStatus
.chargingProfiles
)) {
608 connectorStatus
.chargingProfiles
= [];
612 private static warnDeprecatedTemplateKey(
613 template
: ChargingStationTemplate
,
616 templateFile
: string,
619 if (!Utils
.isUndefined(template
[key
])) {
620 const logMsg
= `Deprecated template key '${key}' usage in file '${templateFile}'${
621 Utils.isNotEmptyString(logMsgToAppend) ? `. ${logMsgToAppend}
` : ''
623 logger
.warn(`${logPrefix} ${logMsg}`);
624 console
.warn(chalk
.yellow(`${logMsg}`));
628 private static convertDeprecatedTemplateKey(
629 template
: ChargingStationTemplate
,
630 deprecatedKey
: string,
633 if (!Utils
.isUndefined(template
[deprecatedKey
])) {
634 template
[key
] = template
[deprecatedKey
] as unknown
;
635 delete template
[deprecatedKey
];
640 * Charging profiles should already be sorted by connector id and stack level (highest stack level has priority)
642 * @param chargingProfiles -
646 private static getLimitFromChargingProfiles(
647 chargingProfiles
: ChargingProfile
[],
651 matchingChargingProfile
: ChargingProfile
;
653 const debugLogMsg
= `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Matching charging profile found for power limitation: %j`;
654 const currentMoment
= moment();
655 const currentDate
= new Date();
656 for (const chargingProfile
of chargingProfiles
) {
658 const chargingSchedule
= chargingProfile
.chargingSchedule
;
659 if (!chargingSchedule
?.startSchedule
) {
661 `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: startSchedule is not defined in charging profile id ${chargingProfile.chargingProfileId}`
664 // Check type (recurring) and if it is already active
665 // Adjust the daily recurring schedule to today
667 chargingProfile
.chargingProfileKind
=== ChargingProfileKindType
.RECURRING
&&
668 chargingProfile
.recurrencyKind
=== RecurrencyKindType
.DAILY
&&
669 currentMoment
.isAfter(chargingSchedule
.startSchedule
)
671 if (!(chargingSchedule
?.startSchedule
instanceof Date)) {
673 `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: startSchedule is not a Date object in charging profile id ${chargingProfile.chargingProfileId}. Trying to convert it to a Date object`
675 chargingSchedule
.startSchedule
= new Date(chargingSchedule
.startSchedule
);
677 chargingSchedule
.startSchedule
.setFullYear(
678 currentDate
.getFullYear(),
679 currentDate
.getMonth(),
680 currentDate
.getDate()
682 // Check if the start of the schedule is yesterday
683 if (moment(chargingSchedule
.startSchedule
).isAfter(currentMoment
)) {
684 chargingSchedule
.startSchedule
.setDate(currentDate
.getDate() - 1);
686 } else if (moment(chargingSchedule
.startSchedule
).isAfter(currentMoment
)) {
689 // Check if the charging profile is active
691 moment(chargingSchedule
.startSchedule
)
692 .add(chargingSchedule
.duration
, 's')
693 .isAfter(currentMoment
)
695 let lastButOneSchedule
: ChargingSchedulePeriod
;
696 // Search the right schedule period
697 for (const schedulePeriod
of chargingSchedule
.chargingSchedulePeriod
) {
698 // Handling of only one period
700 chargingSchedule
.chargingSchedulePeriod
.length
=== 1 &&
701 schedulePeriod
.startPeriod
=== 0
704 limit
: schedulePeriod
.limit
,
705 matchingChargingProfile
: chargingProfile
,
707 logger
.debug(debugLogMsg
, result
);
710 // Find the right schedule period
712 moment(chargingSchedule
.startSchedule
)
713 .add(schedulePeriod
.startPeriod
, 's')
714 .isAfter(currentMoment
)
716 // Found the schedule: last but one is the correct one
718 limit
: lastButOneSchedule
.limit
,
719 matchingChargingProfile
: chargingProfile
,
721 logger
.debug(debugLogMsg
, result
);
725 lastButOneSchedule
= schedulePeriod
;
726 // Handle the last schedule period
728 schedulePeriod
.startPeriod
===
729 chargingSchedule
.chargingSchedulePeriod
[
730 chargingSchedule
.chargingSchedulePeriod
.length
- 1
734 limit
: lastButOneSchedule
.limit
,
735 matchingChargingProfile
: chargingProfile
,
737 logger
.debug(debugLogMsg
, result
);
746 private static getRandomSerialNumberSuffix(params
?: {
747 randomBytesLength
?: number;
750 const randomSerialNumberSuffix
= crypto
751 .randomBytes(params
?.randomBytesLength
?? 16)
753 if (params
?.upperCase
) {
754 return randomSerialNumberSuffix
.toUpperCase();
756 return randomSerialNumberSuffix
;