getConfigurationKey,
setConfigurationKeyValue,
} from './ConfigurationKeyUtils';
-import { IdTagsCache } from './IdTagsCache';
-import {
- OCPP16IncomingRequestService,
- OCPP16RequestService,
- OCPP16ResponseService,
- OCPP16ServiceUtils,
- OCPP20IncomingRequestService,
- OCPP20RequestService,
- OCPP20ResponseService,
- type OCPPIncomingRequestService,
- type OCPPRequestService,
- OCPPServiceUtils,
-} from './ocpp';
-import { SharedLRUCache } from './SharedLRUCache';
import {
buildConnectorsMap,
+ checkChargingStation,
checkConnectorsConfiguration,
checkStationInfoConnectorStatus,
checkTemplate,
- countReservableConnectors,
createBootNotificationRequest,
createSerialNumber,
getAmperageLimitationUnitDivider,
getHashId,
getIdTagsFile,
getMaxNumberOfEvses,
+ getNumberOfReservableConnectors,
getPhaseRotationValue,
hasFeatureProfile,
+ hasReservationExpired,
initializeConnectorsMapStatus,
propagateSerialNumber,
+ removeExpiredReservations,
stationTemplateToStationInfo,
warnTemplateKeysDeprecation,
-} from './Utils';
+} from './Helpers';
+import { IdTagsCache } from './IdTagsCache';
+import {
+ OCPP16IncomingRequestService,
+ OCPP16RequestService,
+ OCPP16ResponseService,
+ OCPP16ServiceUtils,
+ OCPP20IncomingRequestService,
+ OCPP20RequestService,
+ OCPP20ResponseService,
+ type OCPPIncomingRequestService,
+ type OCPPRequestService,
+ OCPPServiceUtils,
+} from './ocpp';
+import { SharedLRUCache } from './SharedLRUCache';
import { BaseError, OCPPError } from '../exception';
import { PerformanceStatistics } from '../performance';
import {
RegistrationStatusEnumType,
RequestCommand,
type Reservation,
- type ReservationFilterKey,
+ type ReservationKey,
ReservationTerminationReason,
type Response,
StandardParametersKey,
return this.stationInfo.enableStatistics ?? false;
}
- public getMustAuthorizeAtRemoteStart(): boolean {
- return this.stationInfo.mustAuthorizeAtRemoteStart ?? true;
+ public getRemoteAuthorization(): boolean {
+ return this.stationInfo.remoteAuthorization ?? true;
}
public getNumberOfPhases(stationInfo?: ChargingStationInfo): number {
}
public getNumberOfRunningTransactions(): number {
- let trxCount = 0;
+ let numberOfRunningTransactions = 0;
if (this.hasEvses) {
for (const [evseId, evseStatus] of this.evses) {
if (evseId === 0) {
}
for (const connectorStatus of evseStatus.connectors.values()) {
if (connectorStatus.transactionStarted === true) {
- ++trxCount;
+ ++numberOfRunningTransactions;
}
}
}
} else {
for (const connectorId of this.connectors.keys()) {
if (connectorId > 0 && this.getConnectorStatus(connectorId)?.transactionStarted === true) {
- ++trxCount;
+ ++numberOfRunningTransactions;
}
}
}
- return trxCount;
+ return numberOfRunningTransactions;
}
public getOutOfOrderEndMeterValues(): boolean {
}
public openWSConnection(
- options: WsOptions = this.stationInfo?.wsOptions ?? {},
- params: { closeOpened?: boolean; terminateOpened?: boolean } = {
- closeOpened: false,
- terminateOpened: false,
- },
+ options?: WsOptions,
+ params?: { closeOpened?: boolean; terminateOpened?: boolean },
): void {
- options = { handshakeTimeout: secondsToMilliseconds(this.getConnectionTimeout()), ...options };
+ options = {
+ handshakeTimeout: secondsToMilliseconds(this.getConnectionTimeout()),
+ ...this.stationInfo?.wsOptions,
+ ...options,
+ };
params = { ...{ closeOpened: false, terminateOpened: false }, ...params };
- if (this.started === false && this.starting === false) {
- logger.warn(
- `${this.logPrefix()} Cannot open OCPP connection to URL ${this.wsConnectionUrl.toString()}
- on stopped charging station`,
- );
+ if (!checkChargingStation(this, this.logPrefix())) {
return;
}
if (
);
}
- public getReservationOnConnectorId0Enabled(): boolean {
+ public getReserveConnectorZeroSupported(): boolean {
return convertToBoolean(
getConfigurationKey(this, StandardParametersKey.ReserveConnectorZeroSupported)!.value,
);
public async removeReservation(
reservation: Reservation,
- reason?: ReservationTerminationReason,
+ reason: ReservationTerminationReason,
): Promise<void> {
const connector = this.getConnectorStatus(reservation.connectorId)!;
switch (reason) {
delete connector.reservation;
break;
default:
- break;
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+ throw new BaseError(`Unknown reservation termination reason '${reason}'`);
}
}
public getReservationBy(
- filterKey: ReservationFilterKey,
+ filterKey: ReservationKey,
value: number | string,
): Reservation | undefined {
if (this.hasEvses) {
}
}
- public startReservationExpirationSetInterval(customInterval?: number): void {
+ public isConnectorReservable(
+ reservationId: number,
+ idTag?: string,
+ connectorId?: number,
+ ): boolean {
+ const reservation = this.getReservationBy('reservationId', reservationId);
+ const reservationExists = !isUndefined(reservation) && !hasReservationExpired(reservation!);
+ if (arguments.length === 1) {
+ return !reservationExists;
+ } else if (arguments.length > 1) {
+ const userReservation = !isUndefined(idTag)
+ ? this.getReservationBy('idTag', idTag!)
+ : undefined;
+ const userReservationExists =
+ !isUndefined(userReservation) && !hasReservationExpired(userReservation!);
+ const notConnectorZero = isUndefined(connectorId) ? true : connectorId! > 0;
+ const freeConnectorsAvailable = this.getNumberOfReservableConnectors() > 0;
+ return (
+ !reservationExists && !userReservationExists && notConnectorZero && freeConnectorsAvailable
+ );
+ }
+ return false;
+ }
+
+ private startReservationExpirationSetInterval(customInterval?: number): void {
const interval =
customInterval ?? Constants.DEFAULT_RESERVATION_EXPIRATION_OBSERVATION_INTERVAL;
if (interval > 0) {
)}`,
);
this.reservationExpirationSetInterval = setInterval((): void => {
- const currentDate = new Date();
- if (this.hasEvses) {
- for (const evseStatus of this.evses.values()) {
- for (const connectorStatus of evseStatus.connectors.values()) {
- if (
- connectorStatus.reservation &&
- connectorStatus.reservation.expiryDate < currentDate
- ) {
- this.removeReservation(
- connectorStatus.reservation,
- ReservationTerminationReason.EXPIRED,
- ).catch(Constants.EMPTY_FUNCTION);
- }
- }
- }
- } else {
- for (const connectorStatus of this.connectors.values()) {
- if (
- connectorStatus.reservation &&
- connectorStatus.reservation.expiryDate < currentDate
- ) {
- this.removeReservation(
- connectorStatus.reservation,
- ReservationTerminationReason.EXPIRED,
- ).catch(Constants.EMPTY_FUNCTION);
- }
- }
- }
+ removeExpiredReservations(this).catch(Constants.EMPTY_FUNCTION);
}, interval);
}
}
- public restartReservationExpiryDateSetInterval(): void {
- this.stopReservationExpirationSetInterval();
- this.startReservationExpirationSetInterval();
+ private stopReservationExpirationSetInterval(): void {
+ if (this.reservationExpirationSetInterval) {
+ clearInterval(this.reservationExpirationSetInterval);
+ }
}
- public validateIncomingRequestWithReservation(connectorId: number, idTag: string): boolean {
- return this.getReservationBy('connectorId', connectorId)?.idTag === idTag;
- }
-
- public isConnectorReservable(
- reservationId: number,
- idTag?: string,
- connectorId?: number,
- ): boolean {
- const reservationExists = !isUndefined(this.getReservationBy('reservationId', reservationId));
- const userReservationExists =
- !isUndefined(idTag) && isUndefined(this.getReservationBy('idTag', idTag!)) ? false : true;
- const notConnectorZero = isUndefined(connectorId) ? true : connectorId! > 0;
- const freeConnectorsAvailable = this.getNumberOfReservableConnectors() > 0;
- return (
- !reservationExists && !userReservationExists && notConnectorZero && freeConnectorsAvailable
- );
- }
+ // private restartReservationExpiryDateSetInterval(): void {
+ // this.stopReservationExpirationSetInterval();
+ // this.startReservationExpirationSetInterval();
+ // }
private getNumberOfReservableConnectors(): number {
- let reservableConnectors = 0;
+ let numberOfReservableConnectors = 0;
if (this.hasEvses) {
for (const evseStatus of this.evses.values()) {
- reservableConnectors += countReservableConnectors(evseStatus.connectors);
+ numberOfReservableConnectors += getNumberOfReservableConnectors(evseStatus.connectors);
}
} else {
- reservableConnectors = countReservableConnectors(this.connectors);
+ numberOfReservableConnectors = getNumberOfReservableConnectors(this.connectors);
}
- return reservableConnectors - this.getNumberOfReservationsOnConnectorZero();
+ return numberOfReservableConnectors - this.getNumberOfReservationsOnConnectorZero();
}
private getNumberOfReservationsOnConnectorZero(): number {
- let numberOfReservations = 0;
if (
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
(this.hasEvses && this.evses.get(0)?.connectors.get(0)?.reservation) ||
(!this.hasEvses && this.connectors.get(0)?.reservation)
) {
- ++numberOfReservations;
+ return 1;
}
- return numberOfReservations;
+ return 0;
}
private flushMessageBuffer(): void {
return this.stationInfo.supervisionUrlOcppConfiguration ?? false;
}
- private stopReservationExpirationSetInterval(): void {
- if (this.reservationExpirationSetInterval) {
- clearInterval(this.reservationExpirationSetInterval);
- }
- }
-
private getSupervisionUrlOcppKey(): string {
return this.stationInfo.supervisionUrlOcppKey ?? VendorParametersKey.ConnectionUrl;
}
} with evse id 0 with no connector id 0 configuration`,
);
}
+ if (Object.keys(stationTemplate?.Evses?.[0]?.Connectors as object).length > 1) {
+ logger.warn(
+ `${this.logPrefix()} Charging station information from template ${
+ this.templateFile
+ } with evse id 0 with more than one connector configuration, only connector id 0 configuration will be used`,
+ );
+ }
if (stationTemplate?.Evses) {
const evsesConfigHash = createHash(Constants.DEFAULT_HASH_ALGORITHM)
.update(JSON.stringify(stationTemplate?.Evses))
);
this.openWSConnection(
{
- ...(this.stationInfo?.wsOptions ?? {}),
handshakeTimeout: reconnectTimeout,
},
{ closeOpened: true },