MeterValueMeasurand,
type MeterValuesRequest,
type MeterValuesResponse,
+ OCPP16AuthorizationStatus,
+ type OCPP16AuthorizeRequest,
+ type OCPP16AuthorizeResponse,
+ OCPP16RequestCommand,
OCPP16SupportedFeatureProfiles,
- OCPP20ConnectorStatusEnumType,
OCPPVersion,
type OutgoingRequest,
PowerUnits,
WebSocketCloseEventStatusCode,
type WsOptions,
} from '../types';
+import { ReservationTerminationReason } from '../types/ocpp/1.6/Reservation';
import type { Reservation } from '../types/ocpp/Reservation';
import {
ACElectricUtils,
private webSocketPingSetInterval!: NodeJS.Timeout;
private readonly chargingStationWorkerBroadcastChannel: ChargingStationWorkerBroadcastChannel;
private reservations?: Reservation[];
+ private reservationExpiryDateSetInterval?: NodeJS.Timeout;
constructor(index: number, templateFile: string) {
this.started = false;
if (this.getEnableStatistics() === true) {
this.performanceStatistics?.start();
}
+ if (this.supportsReservations()) {
+ this.startReservationExpiryDateSetInterval();
+ }
this.openWSConnection();
// Monitor charging station template file
this.templateFileWatcher = FileUtils.watchJsonFile(
public supportsReservationsOnConnectorId0(): boolean {
logger.info(
- `Check for reservation support on connector 0 in charging station (CS): ${this.logPrefix()}`
+ ` ${this.logPrefix()} Check for reservation support on connector 0 in charging station (CS)`
);
return (
this.supportsReservations() &&
);
}
- public addReservation(newReservation: Reservation): void {
+ public async addReservation(reservation: Reservation): Promise<void> {
if (Utils.isNullOrUndefined(this.reservations)) {
this.reservations = [];
}
- const [exists, foundReservation] = this.doesReservationExist(newReservation.reservationId);
+ const [exists, reservationFound] = this.doesReservationExists(reservation);
if (exists) {
- this.replaceExistingReservation(foundReservation, newReservation);
- } else {
- this.reservations.push(newReservation);
+ await this.removeReservation(reservationFound);
+ }
+ this.reservations.push(reservation);
+ if (reservation.connectorId === 0) {
+ return;
}
+ this.getConnectorStatus(reservation.connectorId).status = ConnectorStatusEnum.Reserved;
+ await this.ocppRequestService.requestHandler<
+ StatusNotificationRequest,
+ StatusNotificationResponse
+ >(
+ this,
+ RequestCommand.STATUS_NOTIFICATION,
+ OCPPServiceUtils.buildStatusNotificationRequest(
+ this,
+ reservation.connectorId,
+ ConnectorStatusEnum.Reserved
+ )
+ );
}
- public removeReservation(existingReservationId: number): void {
- const index = this.reservations.findIndex((res) => res.reservationId === existingReservationId);
+ public async removeReservation(
+ reservation: Reservation,
+ reason?: ReservationTerminationReason
+ ): Promise<void> {
+ const sameReservation = (r: Reservation) => r.id === reservation.id;
+ const index = this.reservations?.findIndex(sameReservation);
this.reservations.splice(index, 1);
+ switch (reason) {
+ case ReservationTerminationReason.TRANSACTION_STARTED:
+ // No action needed
+ break;
+ case ReservationTerminationReason.CONNECTOR_STATE_CHANGED:
+ // No action needed
+ break;
+ default: // ReservationTerminationReason.EXPIRED, ReservationTerminationReason.CANCELED
+ this.getConnectorStatus(reservation.connectorId).status = ConnectorStatusEnum.Available;
+ await this.ocppRequestService.requestHandler<
+ StatusNotificationRequest,
+ StatusNotificationResponse
+ >(
+ this,
+ RequestCommand.STATUS_NOTIFICATION,
+ OCPPServiceUtils.buildStatusNotificationRequest(
+ this,
+ reservation.connectorId,
+ ConnectorStatusEnum.Available
+ )
+ );
+ break;
+ }
+ }
+
+ public getReservationById(id: number): Reservation {
+ return this.reservations?.find((reservation) => reservation.id === id);
+ }
+
+ public getReservationByIdTag(id: string): Reservation {
+ return this.reservations?.find((reservation) => reservation.idTag === id);
+ }
+
+ public getReservationByConnectorId(id: number): Reservation {
+ return this.reservations?.find((reservation) => reservation.connectorId === id);
+ }
+
+ public doesReservationExists(reservation: Partial<Reservation>): [boolean, Reservation] {
+ const sameReservation = (r: Reservation) => r.id === reservation.id;
+ const foundReservation = this.reservations?.find(sameReservation);
+ return Utils.isUndefined(foundReservation) ? [false, null] : [true, foundReservation];
}
- public getReservation(reservationId: number, reservationIndex?: number): Reservation {
- if (!Utils.isNullOrUndefined(reservationIndex)) {
- return this.reservations[reservationIndex];
+ public async isAuthorized(
+ connectorId: number,
+ idTag: string,
+ parentIdTag?: string
+ ): Promise<boolean> {
+ let authorized = false;
+ const connectorStatus = this.getConnectorStatus(connectorId);
+ if (
+ this.getLocalAuthListEnabled() === true &&
+ this.hasIdTags() === true &&
+ Utils.isNotEmptyString(
+ this.idTagsCache
+ .getIdTags(ChargingStationUtils.getIdTagsFile(this.stationInfo))
+ ?.find((tag) => tag === idTag)
+ )
+ ) {
+ connectorStatus.localAuthorizeIdTag = idTag;
+ connectorStatus.idTagLocalAuthorized = true;
+ authorized = true;
+ } else if (this.getMustAuthorizeAtRemoteStart() === true) {
+ connectorStatus.authorizeIdTag = idTag;
+ const authorizeResponse: OCPP16AuthorizeResponse =
+ await this.ocppRequestService.requestHandler<
+ OCPP16AuthorizeRequest,
+ OCPP16AuthorizeResponse
+ >(this, OCPP16RequestCommand.AUTHORIZE, {
+ idTag: idTag,
+ });
+ if (authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
+ authorized = true;
+ }
+ } else {
+ logger.warn(
+ `${this.logPrefix()} The charging station configuration expects authorize at
+ remote start transaction but local authorization or authorize isn't enabled`
+ );
}
- return this.reservations.find((r) => r.reservationId === reservationId);
+ return authorized;
}
- public doesReservationExist(
- reservationId: number,
- reservation?: Reservation
- ): [boolean, Reservation] {
- const foundReservation = this.reservations.find(
- (r) => r.reservationId === reservationId || r.reservationId === reservation.reservationId
+ public startReservationExpiryDateSetInterval(customInterval?: number): void {
+ const interval =
+ customInterval ?? Constants.DEFAULT_RESERVATION_EXPIRATION_OBSERVATION_INTERVAL;
+ logger.info(
+ `${this.logPrefix()} Reservation expiration date interval is set to ${interval}
+ and starts on CS now`
);
- return Utils.isUndefined(foundReservation) ? [false, null] : [true, foundReservation];
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
+ this.reservationExpiryDateSetInterval = setInterval(async (): Promise<void> => {
+ if (!Utils.isNullOrUndefined(this.reservations) && !Utils.isEmptyArray(this.reservations)) {
+ for (const reservation of this.reservations) {
+ if (reservation.expiryDate.toString() < new Date().toISOString()) {
+ await this.removeReservation(reservation);
+ logger.info(
+ `${this.logPrefix()} Reservation with ID ${
+ reservation.id
+ } reached expiration date and was removed from CS`
+ );
+ }
+ }
+ }
+ }, interval);
}
- public getReservationByConnectorId(connectorId: number): Reservation {
- return this.reservations.find((r) => r.connectorId === connectorId);
+ public restartReservationExpiryDateSetInterval(): void {
+ this.stopReservationExpiryDateSetInterval();
+ this.startReservationExpiryDateSetInterval();
}
- public getAvailableConnector(): Map<number, ConnectorStatus> {
- for (const connectorId in this.connectors) {
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
- const connector = this.connectors[Utils.convertToInt(connectorId)];
- if (
- this.isConnectorAvailable(Utils.convertToInt(connectorId)) &&
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
- connector.status === OCPP20ConnectorStatusEnumType.Available
- ) {
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
- return connector;
+ public validateIncomingRequestWithReservation(connectorId: number, idTag: string): boolean {
+ const reservation = this.getReservationByConnectorId(connectorId);
+ return Utils.isUndefined(reservation) || reservation.idTag !== idTag;
+ }
+
+ public isConnectorReservable(
+ reservationId: number,
+ connectorId?: number,
+ idTag?: string
+ ): boolean {
+ const [alreadyExists, _] = this.doesReservationExists({ id: reservationId });
+ if (alreadyExists) {
+ return alreadyExists;
+ }
+ const userReservedAlready = Utils.isUndefined(this.getReservationByIdTag(idTag)) ? false : true;
+ const notConnectorZero = Utils.isUndefined(connectorId) ? true : connectorId > 0;
+ const freeConnectorsAvailable = this.getNumberOfReservableConnectors() > 0;
+ return !alreadyExists && !userReservedAlready && notConnectorZero && freeConnectorsAvailable;
+ }
+
+ private getNumberOfReservableConnectors(): number {
+ let reservableConnectors = 0;
+ this.connectors.forEach((connector, id) => {
+ if (id === 0) {
+ return;
}
- }
+ if (connector.status === ConnectorStatusEnum.Available) {
+ reservableConnectors++;
+ }
+ });
+ return reservableConnectors - this.getNumberOfReservationsOnConnectorZero();
+ }
+
+ private getNumberOfReservationsOnConnectorZero(): number {
+ const reservations = this.reservations?.filter((reservation) => reservation.connectorId === 0);
+ return Utils.isNullOrUndefined(reservations) ? 0 : reservations.length;
}
private flushMessageBuffer(): void {
return this.stationInfo.supervisionUrlOcppConfiguration ?? false;
}
- private replaceExistingReservation(
- existingReservation: Reservation,
- newReservation: Reservation
- ): void {
- const existingReservationIndex = this.reservations.findIndex(
- (r) => r.reservationId === existingReservation.reservationId
- );
- this.reservations.splice(existingReservationIndex, 1, newReservation);
+ private stopReservationExpiryDateSetInterval(): void {
+ if (this.reservationExpiryDateSetInterval) {
+ clearInterval(this.reservationExpiryDateSetInterval);
+ }
}
private getSupervisionUrlOcppKey(): string {
private getStationInfoFromTemplate(): ChargingStationInfo {
const stationTemplate: ChargingStationTemplate | undefined = this.getTemplateFromFile();
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
ChargingStationUtils.checkTemplate(stationTemplate, this.logPrefix(), this.templateFile);
ChargingStationUtils.warnTemplateKeysDeprecation(
stationTemplate,
private initialize(): void {
const stationTemplate = this.getTemplateFromFile();
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
ChargingStationUtils.checkTemplate(stationTemplate, this.logPrefix(), this.templateFile);
this.configurationFile = path.join(
path.dirname(this.templateFile.replace('station-templates', 'configurations')),
type JsonObject,
type JsonType,
OCPP16AuthorizationStatus,
- type OCPP16AuthorizeRequest,
- type OCPP16AuthorizeResponse,
OCPP16AvailabilityType,
type OCPP16BootNotificationRequest,
type OCPP16BootNotificationResponse,
OCPP16CancelReservationRequest,
OCPP16ReserveNowRequest,
} from '../../../types/ocpp/1.6/Requests';
+import { ReservationTerminationReason } from '../../../types/ocpp/1.6/Reservation';
import type {
OCPP16CancelReservationResponse,
OCPP16ReserveNowResponse,
[OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, this.handleRequestTriggerMessage.bind(this)],
[OCPP16IncomingRequestCommand.DATA_TRANSFER, this.handleRequestDataTransfer.bind(this)],
[OCPP16IncomingRequestCommand.UPDATE_FIRMWARE, this.handleRequestUpdateFirmware.bind(this)],
+ [OCPP16IncomingRequestCommand.RESERVE_NOW, this.handleRequestReserveNow.bind(this)],
+ [
+ OCPP16IncomingRequestCommand.CANCEL_RESERVATION,
+ this.handleRequestCancelReservation.bind(this),
+ ],
]);
this.jsonSchemas = new Map<OCPP16IncomingRequestCommand, JSONSchemaType<JsonObject>>([
[
'constructor'
),
],
+ [
+ OCPP16IncomingRequestCommand.RESERVE_NOW,
+ OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16ReserveNowRequest>(
+ 'assets/json-schemas/ocpp/1.6/ReserveNow.json',
+ moduleName,
+ 'constructor'
+ ),
+ ],
+ [
+ OCPP16IncomingRequestCommand.CANCEL_RESERVATION,
+ OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16CancelReservationRequest>(
+ 'assets/json-schemas/ocpp/1.6/CancelReservation.json',
+ moduleName,
+ 'constructor'
+ ),
+ ],
]);
this.validatePayload = this.validatePayload.bind(this) as (
chargingStation: ChargingStation,
commandPayload: RemoteStartTransactionRequest
): Promise<GenericResponse> {
const transactionConnectorId = commandPayload.connectorId;
- const reserved: boolean =
+ const reserved =
chargingStation.getConnectorStatus(transactionConnectorId).status ===
OCPP16ChargePointStatus.Reserved;
+ if (
+ reserved &&
+ chargingStation.validateIncomingRequestWithReservation(
+ transactionConnectorId,
+ commandPayload.idTag
+ )
+ ) {
+ return OCPP16Constants.OCPP_RESPONSE_REJECTED;
+ }
if (chargingStation.hasConnector(transactionConnectorId) === false) {
return this.notifyRemoteStartTransactionRejected(
chargingStation,
);
}
if (
- chargingStation.isChargingStationAvailable() === false ||
- chargingStation.isConnectorAvailable(transactionConnectorId) === false ||
- reserved
+ !chargingStation.isChargingStationAvailable() ||
+ !chargingStation.isConnectorAvailable(transactionConnectorId)
) {
return this.notifyRemoteStartTransactionRejected(
chargingStation,
const connectorStatus = chargingStation.getConnectorStatus(transactionConnectorId);
// Check if authorized
if (chargingStation.getAuthorizeRemoteTxRequests() === true) {
- const authorized = await this.isAuthorized(
- chargingStation,
+ const authorized = await chargingStation.isAuthorized(
transactionConnectorId,
commandPayload.idTag
);
idTag: commandPayload.idTag,
};
if (reserved) {
- startTransactionData['reservationId'] =
- chargingStation.getReservationByConnectorId(transactionConnectorId).reservationId;
+ const reservation = chargingStation.getReservationByConnectorId(transactionConnectorId);
+ startTransactionData.reservationId = reservation.id;
+ await chargingStation.removeReservation(
+ reservation,
+ ReservationTerminationReason.TRANSACTION_STARTED
+ );
}
if (
(
commandPayload.idTag
);
}
- if (reserved) {
- await this.handleReservedRemoteStartTransaction(
- chargingStation,
- transactionConnectorId,
- commandPayload
- );
- }
// No authorization check required, start transaction
if (
this.setRemoteStartTransactionChargingProfile(
commandPayload: OCPP16ReserveNowRequest
): Promise<OCPP16ReserveNowResponse> {
const { reservationId, idTag, connectorId } = commandPayload;
- let connector: Map<number, ConnectorStatus>;
let response: OCPP16ReserveNowResponse;
try {
if (
if (connectorId === 0 && !chargingStation.supportsReservationsOnConnectorId0()) {
return OCPPConstants.OCPP_RESERVATION_RESPONSE_REJECTED;
}
- if (!(await this.isAuthorized(chargingStation, connectorId, commandPayload.idTag))) {
+ if (!(await chargingStation.isAuthorized(connectorId, idTag))) {
return OCPPConstants.OCPP_RESERVATION_RESPONSE_REJECTED;
}
switch (chargingStation.getConnectorStatus(connectorId).status) {
response = OCPPConstants.OCPP_RESERVATION_RESPONSE_UNAVAILABLE;
break;
case ConnectorStatusEnum.Reserved:
- if (Utils.isUndefined(chargingStation.getReservation(commandPayload.reservationId))) {
+ if (!chargingStation.isConnectorReservable(reservationId, connectorId, idTag)) {
response = OCPPConstants.OCPP_RESERVATION_RESPONSE_OCCUPIED;
break;
}
// eslint-disable-next-line no-fallthrough
default:
- logger.info(
- `${chargingStation.logPrefix()} on connector ${connectorId} is now reserved for ${
- commandPayload.idTag
- }`
- );
- chargingStation.getConnectorStatus(connectorId).status = ConnectorStatusEnum.Reserved;
- chargingStation.addReservation({ ...commandPayload });
- await chargingStation.ocppRequestService
- .requestHandler<OCPP16StatusNotificationRequest, OCPP16StatusNotificationResponse>(
- chargingStation,
- OCPP16RequestCommand.STATUS_NOTIFICATION,
- {
- connectorId,
- errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
- status: chargingStation.getConnectorStatus(connectorId).status,
- },
- {
- triggerMessage: true,
- }
- )
- .catch(Constants.EMPTY_FUNCTION);
+ if (!chargingStation.isConnectorReservable(reservationId)) {
+ response = OCPPConstants.OCPP_RESERVATION_RESPONSE_OCCUPIED;
+ break;
+ }
+ await chargingStation.addReservation({
+ id: commandPayload.reservationId,
+ ...commandPayload,
+ });
response = OCPPConstants.OCPP_RESERVATION_RESPONSE_ACCEPTED;
break;
}
return response;
} catch (error) {
+ chargingStation.getConnectorStatus(connectorId).status = ConnectorStatusEnum.Available;
return this.handleIncomingRequestError(
chargingStation,
OCPP16IncomingRequestCommand.RESERVE_NOW,
error as Error,
- { errorResponse: OCPP16Constants.OCPP_RESERVATION_RESPONSE_FAULTED }
+ { errorResponse: OCPPConstants.OCPP_RESERVATION_RESPONSE_FAULTED }
);
}
}
commandPayload: OCPP16CancelReservationRequest
): Promise<OCPP16CancelReservationResponse> {
try {
- const reservationId = commandPayload.reservationId;
- const [exists, reservation] = chargingStation.doesReservationExist(reservationId);
+ const { reservationId } = commandPayload;
+ const [exists, reservation] = chargingStation.doesReservationExists({ id: reservationId });
if (!exists) {
logger.error(
`${chargingStation.logPrefix()} Reservation with ID ${reservationId} does not exist on charging station`
);
- return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED;
+ return OCPPConstants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED;
}
- chargingStation.getConnectorStatus(reservation.connectorId).status =
- ConnectorStatusEnum.Available;
- chargingStation.removeReservation(reservation.reservationId);
- await chargingStation.ocppRequestService
- .requestHandler<OCPP16StatusNotificationRequest, OCPP16StatusNotificationResponse>(
- chargingStation,
- OCPP16RequestCommand.STATUS_NOTIFICATION,
- {
- connectorId: reservation.connectorId,
- errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
- status: chargingStation.getConnectorStatus(reservation.connectorId).status,
- },
- {
- triggerMessage: true,
- }
- )
- .catch(Constants.EMPTY_FUNCTION);
- return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_ACCEPTED;
+ await chargingStation.removeReservation(reservation);
+ return OCPPConstants.OCPP_CANCEL_RESERVATION_RESPONSE_ACCEPTED;
} catch (error) {
return this.handleIncomingRequestError(
chargingStation,
OCPP16IncomingRequestCommand.CANCEL_RESERVATION,
error as Error,
- { errorResponse: OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED }
- );
- }
- }
-
- /**
- * Check for authorized access on a connector with given ConnectorId and idTag for the user
- * @param {ChargingStation} chargingStation - Charging Station working on incoming request
- * @param {number} ConnectorId - Identifier of the connector at the charging station
- * @param {string} idTag - Identifier of the user
- * @param {string} parentIdTag - Identifier for a group of idTags, which is optional
- * @returns {Promise<boolean>} - 'true' if user is authorized, 'false' otherwise
- */
- private async isAuthorized(
- chargingStation: ChargingStation,
- connectorId: number,
- idTag: string,
- parentIdTag?: string
- ): Promise<boolean> {
- let authorized = false;
- const connectorStatus = chargingStation.getConnectorStatus(connectorId);
- if (
- chargingStation.getLocalAuthListEnabled() === true &&
- chargingStation.hasIdTags() === true &&
- Utils.isNotEmptyString(
- chargingStation.idTagsCache
- .getIdTags(ChargingStationUtils.getIdTagsFile(chargingStation.stationInfo))
- ?.find((tag) => tag === idTag)
- )
- ) {
- connectorStatus.localAuthorizeIdTag = idTag;
- connectorStatus.idTagLocalAuthorized = true;
- authorized = true;
- } else if (chargingStation.getMustAuthorizeAtRemoteStart() === true) {
- connectorStatus.authorizeIdTag = idTag;
- const authorizeResponse: OCPP16AuthorizeResponse =
- await chargingStation.ocppRequestService.requestHandler<
- OCPP16AuthorizeRequest,
- OCPP16AuthorizeResponse
- >(chargingStation, OCPP16RequestCommand.AUTHORIZE, {
- idTag: idTag,
- });
- if (authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
- authorized = true;
- }
- } else {
- logger.warn(
- `${chargingStation.logPrefix()} The charging station configuration expects authorize at remote start transaction but local authorization or authorize isn't enabled`
- );
- }
- return authorized;
- }
-
- private async handleReservedRemoteStartTransaction(
- chargingStation: ChargingStation,
- connectorId: number,
- commandPayload: RemoteStartTransactionRequest
- ): Promise<GenericResponse> {
- const reservation = chargingStation.getReservationByConnectorId(connectorId);
- if (
- !Utils.isUndefined(reservation) &&
- (await this.isAuthorized(chargingStation, connectorId, commandPayload.idTag)) &&
- reservation.idTag === commandPayload.idTag
- ) {
- const remoteStartTransactionLogMsg = `${chargingStation.logPrefix()} Transaction remotely STARTED on ${
- chargingStation.stationInfo.chargingStationId
- }#${connectorId.toString()} for idTag '${commandPayload.idTag}'`;
- await OCPP16ServiceUtils.sendAndSetConnectorStatus(
- chargingStation,
- connectorId,
- OCPP16ChargePointStatus.Preparing
+ { errorResponse: OCPPConstants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED }
);
- if (
- this.setRemoteStartTransactionChargingProfile(
- chargingStation,
- connectorId,
- commandPayload.chargingProfile
- ) === true
- ) {
- chargingStation.getConnectorStatus(connectorId).transactionRemoteStarted = true;
- if (
- (
- await chargingStation.ocppRequestService.requestHandler<
- OCPP16StartTransactionRequest,
- OCPP16StartTransactionResponse
- >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
- connectorId: connectorId,
- idTag: commandPayload.idTag,
- reservationId: reservation.reservationId,
- })
- ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
- ) {
- logger.debug(remoteStartTransactionLogMsg);
- return OCPP16Constants.OCPP_RESPONSE_ACCEPTED;
- }
- }
}
}
}
OCPPVersion,
type RequestParams,
} from '../../../types';
+import type {
+ OCPP16CancelReservationRequest,
+ OCPP16ReserveNowRequest,
+} from '../../../types/ocpp/1.6/Requests';
import { Constants, Utils } from '../../../utils';
import { OCPPRequestService } from '../OCPPRequestService';
import type { OCPPResponseService } from '../OCPPResponseService';
'constructor'
),
],
+ [
+ OCPP16RequestCommand.RESERVE_NOW,
+ OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16ReserveNowRequest>(
+ 'assets/json-schemas/ocpp/1.6/ReserveNow.json',
+ moduleName,
+ 'constructor'
+ ),
+ ],
+ [
+ OCPP16RequestCommand.CANCEL_RESERVATION,
+ OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16CancelReservationRequest>(
+ 'assets/json-schemas/ocpp/1.6/CancelReservation.json',
+ moduleName,
+ 'constructor'
+ ),
+ ],
]);
this.buildRequestPayload = this.buildRequestPayload.bind(this) as <Request extends JsonType>(
chargingStation: ChargingStation,
data?: string;
}
-export interface OCPP16ReserveNowRequest {
+export interface OCPP16ReserveNowRequest extends JsonObject {
connectorId: number;
expiryDate: Date;
idTag: string;
reservationId: number;
}
-export interface OCPP16CancelReservationRequest {
+export interface OCPP16CancelReservationRequest extends JsonObject {
reservationId: number;
}
export interface OCPP16Reservation {
+ id: number;
connectorId: number;
expiryDate: Date;
idTag: string;
parentIdTag?: string;
- reservationId: number;
+}
+
+export enum ReservationTerminationReason {
+ EXPIRED = 'Expired',
+ TRANSACTION_STARTED = 'TransactionStarted',
+ CONNECTOR_STATE_CHANGED = 'ConnectorStateChanged',
+ CANCELED = 'ReservationCanceled',
}
-import { OCPP16Reservation } from './1.6/Reservation';
+import { type OCPP16Reservation } from './1.6/Reservation';
export type Reservation = OCPP16Reservation;
public static getUIServer(): UIServerConfiguration {
if (Utils.hasOwnProp(Configuration.getConfig(), 'uiWebSocketServer')) {
console.error(
- chalk`{green ${Configuration.logPrefix()}} {red Deprecated configuration section 'uiWebSocketServer' usage. Use 'uiServer' instead}`
+ `${chalk.green(Configuration.logPrefix())} ${chalk.red(
+ "Deprecated configuration section 'uiWebSocketServer' usage. Use 'uiServer' instead"
+ )}`
);
}
let uiServerConfiguration: UIServerConfiguration = {
(stationTemplateUrl: StationTemplateUrl) => {
if (!Utils.isUndefined(stationTemplateUrl['numberOfStation'])) {
console.error(
- chalk`{green ${Configuration.logPrefix()}} {red Deprecated configuration key 'numberOfStation' usage for template file '${
- stationTemplateUrl.file
- }' in 'stationTemplateUrls'. Use 'numberOfStations' instead}`
+ `${chalk.green(Configuration.logPrefix())} ${chalk.red(
+ `Deprecated configuration key 'numberOfStation' usage for template file '${stationTemplateUrl.file}' in 'stationTemplateUrls'. Use 'numberOfStations' instead`
+ )}`
);
}
}
!Utils.isUndefined((Configuration.getConfig()[sectionName] as Record<string, unknown>)[key])
) {
console.error(
- chalk`{green ${Configuration.logPrefix()}} {red Deprecated configuration key '${key}' usage in section '${sectionName}'${
- logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
- }}`
+ `${chalk.green(Configuration.logPrefix())} ${chalk.red(
+ `Deprecated configuration key '${key}' usage in section '${sectionName}'${
+ logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
+ }`
+ )}`
);
} else if (!Utils.isUndefined(Configuration.getConfig()[key])) {
console.error(
- chalk`{green ${Configuration.logPrefix()}} {red Deprecated configuration key '${key}' usage${
- logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
- }}`
+ `${chalk.green(Configuration.logPrefix())} ${chalk.red(
+ `Deprecated configuration key '${key}' usage${
+ logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
+ }`
+ )}`
);
}
}
/* This is intentional */
});
+ static readonly DEFAULT_RESERVATION_EXPIRATION_OBSERVATION_INTERVAL = 5000; // Ms
+
private constructor() {
// This is intentional
}