)}`,
);
while (this.connectorsStatus.get(connectorId)?.start === true) {
- if (new Date() > this.connectorsStatus.get(connectorId)!.stopDate!) {
- this.stopConnector(connectorId);
- break;
- }
- if (this.chargingStation.inAcceptedState() === false) {
- logger.error(
- `${this.logPrefix(
- connectorId,
- )} entered in transaction loop while the charging station is not in accepted state`,
- );
- this.stopConnector(connectorId);
- break;
- }
- if (this.chargingStation.isChargingStationAvailable() === false) {
- logger.info(
- `${this.logPrefix(
- connectorId,
- )} entered in transaction loop while the charging station is unavailable`,
- );
- this.stopConnector(connectorId);
- break;
- }
- if (this.chargingStation.isConnectorAvailable(connectorId) === false) {
- logger.info(
- `${this.logPrefix(
- connectorId,
- )} entered in transaction loop while the connector ${connectorId} is unavailable`,
- );
- this.stopConnector(connectorId);
- break;
- }
- if (
- this.chargingStation.getConnectorStatus(connectorId)?.status ===
- ConnectorStatusEnum.Unavailable
- ) {
- logger.info(
- `${this.logPrefix(
- connectorId,
- )} entered in transaction loop while the connector ${connectorId} status is unavailable`,
- );
+ if (!this.canStartConnector(connectorId)) {
this.stopConnector(connectorId);
break;
}
this.connectorsStatus.get(connectorId)!.start = true;
}
+ private canStartConnector(connectorId: number): boolean {
+ if (new Date() > this.connectorsStatus.get(connectorId)!.stopDate!) {
+ return false;
+ }
+ if (this.chargingStation.inAcceptedState() === false) {
+ logger.error(
+ `${this.logPrefix(
+ connectorId,
+ )} entered in transaction loop while the charging station is not in accepted state`,
+ );
+ return false;
+ }
+ if (this.chargingStation.isChargingStationAvailable() === false) {
+ logger.info(
+ `${this.logPrefix(
+ connectorId,
+ )} entered in transaction loop while the charging station is unavailable`,
+ );
+ return false;
+ }
+ if (this.chargingStation.isConnectorAvailable(connectorId) === false) {
+ logger.info(
+ `${this.logPrefix(
+ connectorId,
+ )} entered in transaction loop while the connector ${connectorId} is unavailable`,
+ );
+ return false;
+ }
+ if (
+ this.chargingStation.getConnectorStatus(connectorId)?.status ===
+ ConnectorStatusEnum.Unavailable
+ ) {
+ logger.info(
+ `${this.logPrefix(
+ connectorId,
+ )} entered in transaction loop while the connector ${connectorId} status is unavailable`,
+ );
+ return false;
+ }
+ return true;
+ }
+
private initializeConnectorsStatus(): void {
if (this.chargingStation.hasEvses) {
for (const [evseId, evseStatus] of this.chargingStation.evses) {
differenceInWeeks,
isAfter,
isBefore,
+ isDate,
isWithinInterval,
toDate,
} from 'date-fns';
isNotEmptyString,
isNullOrUndefined,
isUndefined,
- isValidDate,
+ isValidTime,
logger,
secureRandom,
} from '../utils';
const currentDate = new Date();
const connectorStatus = chargingStation.getConnectorStatus(connectorId);
for (const chargingProfile of chargingProfiles) {
- if (
- (isValidDate(chargingProfile.validFrom) &&
- isBefore(currentDate, chargingProfile.validFrom!)) ||
- (isValidDate(chargingProfile.validTo) && isAfter(currentDate, chargingProfile.validTo!))
- ) {
- logger.debug(
- `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Charging profile id ${
- chargingProfile.chargingProfileId
- } is not valid for the current date ${currentDate.toISOString()}`,
- );
- continue;
- }
const chargingSchedule = chargingProfile.chargingSchedule;
if (connectorStatus?.transactionStarted && isNullOrUndefined(chargingSchedule?.startSchedule)) {
logger.debug(
// OCPP specifies that if startSchedule is not defined, it should be relative to start of the connector transaction
chargingSchedule.startSchedule = connectorStatus?.transactionStart;
}
- if (!(chargingSchedule?.startSchedule instanceof Date)) {
+ if (!isDate(chargingSchedule?.startSchedule)) {
logger.warn(
`${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Charging profile id ${chargingProfile.chargingProfileId} startSchedule property is not a Date object. Trying to convert it to a Date object`,
);
chargingSchedule.startSchedule = convertToDate(chargingSchedule?.startSchedule)!;
}
- if (
- chargingProfile.chargingProfileKind === ChargingProfileKindType.RECURRING &&
- isNullOrUndefined(chargingProfile.recurrencyKind)
- ) {
- logger.error(
- `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Recurring charging profile id ${chargingProfile.chargingProfileId} has no recurrencyKind defined`,
- );
- continue;
- }
- if (chargingProfile.chargingProfileKind === ChargingProfileKindType.RECURRING) {
- prepareRecurringChargingProfile(chargingProfile, currentDate, logPrefix);
- } else if (
- chargingProfile.chargingProfileKind === ChargingProfileKindType.RELATIVE &&
- connectorStatus?.transactionStarted
- ) {
- chargingSchedule.startSchedule = connectorStatus?.transactionStart;
- }
- if (isNullOrUndefined(chargingSchedule?.startSchedule)) {
- logger.error(
- `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Charging profile id ${chargingProfile.chargingProfileId} has (still) no startSchedule defined`,
- );
- continue;
+ switch (chargingProfile.chargingProfileKind) {
+ case ChargingProfileKindType.RECURRING:
+ if (!canProceedRecurringChargingProfile(chargingProfile, logPrefix)) {
+ continue;
+ }
+ prepareRecurringChargingProfile(chargingProfile, currentDate, logPrefix);
+ break;
+ case ChargingProfileKindType.RELATIVE:
+ connectorStatus?.transactionStarted &&
+ (chargingSchedule.startSchedule = connectorStatus?.transactionStart);
+ break;
}
- if (isNullOrUndefined(chargingSchedule?.duration)) {
- logger.error(
- `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Charging profile id ${chargingProfile.chargingProfileId} has no duration defined, not yet supported`,
- );
+ if (!canProceedChargingProfile(chargingProfile, currentDate, logPrefix)) {
continue;
}
// Check if the charging profile is active
if (
- isValidDate(chargingSchedule?.startSchedule) &&
+ isValidTime(chargingSchedule?.startSchedule) &&
isWithinInterval(currentDate, {
start: chargingSchedule.startSchedule!,
end: addSeconds(chargingSchedule.startSchedule!, chargingSchedule.duration!),
}
};
+const canProceedChargingProfile = (
+ chargingProfile: ChargingProfile,
+ currentDate: Date,
+ logPrefix: string,
+): boolean => {
+ if (
+ (isValidTime(chargingProfile.validFrom) && isBefore(currentDate, chargingProfile.validFrom!)) ||
+ (isValidTime(chargingProfile.validTo) && isAfter(currentDate, chargingProfile.validTo!))
+ ) {
+ logger.debug(
+ `${logPrefix} ${moduleName}.canProceedChargingProfile: Charging profile id ${
+ chargingProfile.chargingProfileId
+ } is not valid for the current date ${currentDate.toISOString()}`,
+ );
+ return false;
+ }
+ const chargingSchedule = chargingProfile.chargingSchedule;
+ if (isNullOrUndefined(chargingSchedule?.startSchedule)) {
+ logger.error(
+ `${logPrefix} ${moduleName}.canProceedChargingProfile: Charging profile id ${chargingProfile.chargingProfileId} has (still) no startSchedule defined`,
+ );
+ return false;
+ }
+ if (isNullOrUndefined(chargingSchedule?.duration)) {
+ logger.error(
+ `${logPrefix} ${moduleName}.canProceedChargingProfile: Charging profile id ${chargingProfile.chargingProfileId} has no duration defined, not yet supported`,
+ );
+ return false;
+ }
+ return true;
+};
+
+const canProceedRecurringChargingProfile = (
+ chargingProfile: ChargingProfile,
+ logPrefix: string,
+): boolean => {
+ if (
+ chargingProfile.chargingProfileKind === ChargingProfileKindType.RECURRING &&
+ isNullOrUndefined(chargingProfile.recurrencyKind)
+ ) {
+ logger.error(
+ `${logPrefix} ${moduleName}.canProceedRecurringChargingProfile: Recurring charging profile id ${chargingProfile.chargingProfileId} has no recurrencyKind defined`,
+ );
+ return false;
+ }
+ return true;
+};
+
/**
* Adjust recurring charging profile startSchedule to the current recurrency time interval if needed
*
return formatDurationMilliSeconds(secondsToMilliseconds(duration));
};
-// More efficient date validation function than the one provided by date-fns
-export const isValidDate = (date: unknown): boolean => {
+// More efficient time validation function than the one provided by date-fns
+export const isValidTime = (date: unknown): boolean => {
if (typeof date === 'number') {
return !isNaN(date);
} else if (isDate(date)) {
if (isNullOrUndefined(value)) {
return value as null | undefined;
}
- if (value instanceof Date) {
- return value;
+ if (isDate(value)) {
+ return value as Date;
}
if (isString(value) || typeof value === 'number') {
return new Date(value!);
isNotEmptyString,
isNullOrUndefined,
isUndefined,
- isValidDate,
+ isValidTime,
logPrefix,
promiseWithTimeout,
roundTo,
isNullOrUndefined,
isObject,
isUndefined,
- isValidDate,
+ isValidTime,
roundTo,
secureRandom,
sleep,
expect(formatDurationSeconds(hoursToSeconds(4380))).toBe('182 days 12 hours');
});
- it('Verify isValidDate()', () => {
- expect(isValidDate(undefined)).toBe(false);
- expect(isValidDate(null)).toBe(false);
- expect(isValidDate('')).toBe(false);
- expect(isValidDate({})).toBe(false);
- expect(isValidDate([])).toBe(false);
- expect(isValidDate(new Map())).toBe(false);
- expect(isValidDate(new Set())).toBe(false);
- expect(isValidDate(new WeakMap())).toBe(false);
- expect(isValidDate(new WeakSet())).toBe(false);
- expect(isValidDate(-1)).toBe(true);
- expect(isValidDate(0)).toBe(true);
- expect(isValidDate(1)).toBe(true);
- expect(isValidDate(-0.5)).toBe(true);
- expect(isValidDate(0.5)).toBe(true);
- expect(isValidDate(new Date())).toBe(true);
+ it('Verify isValidTime()', () => {
+ expect(isValidTime(undefined)).toBe(false);
+ expect(isValidTime(null)).toBe(false);
+ expect(isValidTime('')).toBe(false);
+ expect(isValidTime({})).toBe(false);
+ expect(isValidTime([])).toBe(false);
+ expect(isValidTime(new Map())).toBe(false);
+ expect(isValidTime(new Set())).toBe(false);
+ expect(isValidTime(new WeakMap())).toBe(false);
+ expect(isValidTime(new WeakSet())).toBe(false);
+ expect(isValidTime(-1)).toBe(true);
+ expect(isValidTime(0)).toBe(true);
+ expect(isValidTime(1)).toBe(true);
+ expect(isValidTime(-0.5)).toBe(true);
+ expect(isValidTime(0.5)).toBe(true);
+ expect(isValidTime(new Date())).toBe(true);
});
it('Verify convertToDate()', () => {