From 66dd344779f5258bbf4c76b386d005c0c2160b11 Mon Sep 17 00:00:00 2001 From: Julian Buecher Date: Mon, 5 Jun 2023 16:19:22 +0200 Subject: [PATCH] refactor: applied changes for pull request --- README.md | 2 +- .../abb-atg.station-template.json | 2 +- .../chargex.station-template.json | 5 + .../evlink.station-template.json | 2 +- .../schneider-evses.station-template.json | 5 + .../schneider-imredd.station-template.json | 2 +- .../siemens.station-template.json | 2 +- .../virtual-simple.station-template.json | 2 +- .../virtual.station-template.json | 5 + src/charging-station/ChargingStation.ts | 229 +++++++++--------- .../ocpp/1.6/OCPP16IncomingRequestService.ts | 223 +++++++++-------- .../ocpp/1.6/OCPP16ResponseService.ts | 18 ++ .../ocpp/1.6/OCPP16ServiceUtils.ts | 55 ++++- src/charging-station/ocpp/OCPPConstants.ts | 36 ++- src/types/ChargingStationTemplate.ts | 2 - src/types/ConnectorStatus.ts | 2 + src/types/index.ts | 6 + src/types/ocpp/1.6/Reservation.ts | 10 +- src/types/ocpp/1.6/Responses.ts | 14 +- src/types/ocpp/Requests.ts | 6 + src/types/ocpp/Responses.ts | 7 +- 21 files changed, 379 insertions(+), 256 deletions(-) diff --git a/README.md b/README.md index 9fb599ae..7ed5d267 100644 --- a/README.md +++ b/README.md @@ -460,7 +460,7 @@ All kind of OCPP parameters are supported in a charging station configuration or #### Reservation Profile -- _none_ +- :white_check_mark: ReserveConnectorZeroSupported (type: boolean) (units: -) #### Smart Charging Profile diff --git a/src/assets/station-templates/abb-atg.station-template.json b/src/assets/station-templates/abb-atg.station-template.json index 99a67c64..456089bf 100644 --- a/src/assets/station-templates/abb-atg.station-template.json +++ b/src/assets/station-templates/abb-atg.station-template.json @@ -49,7 +49,7 @@ { "key": "ReserveConnectorZeroSupported", "readonly": false, - "value": "true" + "value": "false" } ] }, diff --git a/src/assets/station-templates/chargex.station-template.json b/src/assets/station-templates/chargex.station-template.json index 6ff7562d..fca57094 100644 --- a/src/assets/station-templates/chargex.station-template.json +++ b/src/assets/station-templates/chargex.station-template.json @@ -69,6 +69,11 @@ { "key": "TransactionMessageRetryInterval", "value": "20" + }, + { + "key": "ReserveConnectorZeroSupported", + "readonly": false, + "value": "false" } ] }, diff --git a/src/assets/station-templates/evlink.station-template.json b/src/assets/station-templates/evlink.station-template.json index 47938eee..5b9f9eac 100644 --- a/src/assets/station-templates/evlink.station-template.json +++ b/src/assets/station-templates/evlink.station-template.json @@ -50,7 +50,7 @@ { "key": "ReserveConnectorZeroSupported", "readonly": false, - "value": "true" + "value": "false" } ] }, diff --git a/src/assets/station-templates/schneider-evses.station-template.json b/src/assets/station-templates/schneider-evses.station-template.json index 7017f1e4..f292519e 100644 --- a/src/assets/station-templates/schneider-evses.station-template.json +++ b/src/assets/station-templates/schneider-evses.station-template.json @@ -43,6 +43,11 @@ "key": "WebSocketPingInterval", "readonly": false, "value": "60" + }, + { + "key": "ReserveConnectorZeroSupported", + "readonly": false, + "value": "false" } ] }, diff --git a/src/assets/station-templates/schneider-imredd.station-template.json b/src/assets/station-templates/schneider-imredd.station-template.json index d6cc99f8..a4411047 100644 --- a/src/assets/station-templates/schneider-imredd.station-template.json +++ b/src/assets/station-templates/schneider-imredd.station-template.json @@ -49,7 +49,7 @@ { "key": "ReserveConnectorZeroSupported", "readonly": false, - "value": "true" + "value": "false" } ] }, diff --git a/src/assets/station-templates/siemens.station-template.json b/src/assets/station-templates/siemens.station-template.json index ac075ce6..439b5f92 100644 --- a/src/assets/station-templates/siemens.station-template.json +++ b/src/assets/station-templates/siemens.station-template.json @@ -44,7 +44,7 @@ { "key": "ReserveConnectorZeroSupported", "readonly": false, - "value": "true" + "value": "false" } ] }, diff --git a/src/assets/station-templates/virtual-simple.station-template.json b/src/assets/station-templates/virtual-simple.station-template.json index 8a0c2801..37cbe8ad 100644 --- a/src/assets/station-templates/virtual-simple.station-template.json +++ b/src/assets/station-templates/virtual-simple.station-template.json @@ -44,7 +44,7 @@ { "key": "ReserveConnectorZeroSupported", "readonly": false, - "value": "true" + "value": "false" } ] }, diff --git a/src/assets/station-templates/virtual.station-template.json b/src/assets/station-templates/virtual.station-template.json index 962e792f..09086f8e 100644 --- a/src/assets/station-templates/virtual.station-template.json +++ b/src/assets/station-templates/virtual.station-template.json @@ -40,6 +40,11 @@ "key": "WebSocketPingInterval", "readonly": false, "value": "60" + }, + { + "key": "ReserveConnectorZeroSupported", + "readonly": false, + "value": "false" } ] }, diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index 3eab1fc8..82ed3b8c 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -26,7 +26,6 @@ import { type OCPPRequestService, OCPPServiceUtils, } from './ocpp'; -import { OCPPConstants } from './ocpp/OCPPConstants'; import { SharedLRUCache } from './SharedLRUCache'; import { BaseError, OCPPError } from '../exception'; import { PerformanceStatistics } from '../performance'; @@ -63,16 +62,14 @@ import { MeterValueMeasurand, type MeterValuesRequest, type MeterValuesResponse, - OCPP16AuthorizationStatus, - type OCPP16AuthorizeRequest, - type OCPP16AuthorizeResponse, - OCPP16RequestCommand, - OCPP16SupportedFeatureProfiles, OCPPVersion, type OutgoingRequest, PowerUnits, RegistrationStatusEnumType, RequestCommand, + type Reservation, + ReservationFilterKey, + ReservationTerminationReason, type Response, StandardParametersKey, type StatusNotificationRequest, @@ -87,8 +84,6 @@ import { WebSocketCloseEventStatusCode, type WsOptions, } from '../types'; -import { ReservationTerminationReason } from '../types/ocpp/1.6/Reservation'; -import type { Reservation } from '../types/ocpp/Reservation'; import { ACElectricUtils, AsyncLock, @@ -140,7 +135,6 @@ export class ChargingStation { private readonly sharedLRUCache: SharedLRUCache; private webSocketPingSetInterval!: NodeJS.Timeout; private readonly chargingStationWorkerBroadcastChannel: ChargingStationWorkerBroadcastChannel; - private reservations?: Reservation[]; private reservationExpiryDateSetInterval?: NodeJS.Timeout; constructor(index: number, templateFile: string) { @@ -558,7 +552,8 @@ export class ChargingStation { ); } else { logger.error( - `${this.logPrefix()} Heartbeat interval set to ${this.getHeartbeatInterval()}, not starting the heartbeat` + `${this.logPrefix()} Heartbeat interval set to ${this.getHeartbeatInterval()}, + not starting the heartbeat` ); } } @@ -586,13 +581,15 @@ export class ChargingStation { } if (!this.getConnectorStatus(connectorId)) { logger.error( - `${this.logPrefix()} Trying to start MeterValues on non existing connector id ${connectorId.toString()}` + `${this.logPrefix()} Trying to start MeterValues on non existing connector id + ${connectorId.toString()}` ); return; } if (this.getConnectorStatus(connectorId)?.transactionStarted === false) { logger.error( - `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId} with no transaction started` + `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId} + with no transaction started` ); return; } else if ( @@ -600,7 +597,8 @@ export class ChargingStation { Utils.isNullOrUndefined(this.getConnectorStatus(connectorId)?.transactionId) ) { logger.error( - `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId} with no transaction id` + `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId} + with no transaction id` ); return; } @@ -652,7 +650,7 @@ export class ChargingStation { if (this.getEnableStatistics() === true) { this.performanceStatistics?.start(); } - if (this.supportsReservations()) { + if (this.hasFeatureProfile(SupportedFeatureProfiles.Reservation)) { this.startReservationExpiryDateSetInterval(); } this.openWSConnection(); @@ -764,7 +762,8 @@ export class ChargingStation { 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` + `${this.logPrefix()} Cannot open OCPP connection to URL ${this.wsConnectionUrl.toString()} + on stopped charging station` ); return; } @@ -783,7 +782,8 @@ export class ChargingStation { if (this.isWebSocketConnectionOpened() === true) { logger.warn( - `${this.logPrefix()} OCPP connection to URL ${this.wsConnectionUrl.toString()} is already opened` + `${this.logPrefix()} OCPP connection to URL ${this.wsConnectionUrl.toString()} + is already opened` ); return; } @@ -911,40 +911,26 @@ export class ChargingStation { ); } - public supportsReservations(): boolean { - logger.info(`${this.logPrefix()} Check for reservation support in charging station`); - return ChargingStationConfigurationUtils.getConfigurationKey( - this, - StandardParametersKey.SupportedFeatureProfiles - ).value.includes(OCPP16SupportedFeatureProfiles.Reservation); - } - - public supportsReservationsOnConnectorId0(): boolean { - logger.info( - ` ${this.logPrefix()} Check for reservation support on connector 0 in charging station (CS)` - ); - return ( - this.supportsReservations() && + public getReservationOnConnectorId0Enabled(): boolean { + return Utils.convertToBoolean( ChargingStationConfigurationUtils.getConfigurationKey( this, - OCPPConstants.OCPP_RESERVE_CONNECTOR_ZERO_SUPPORTED - ).value === 'true' + StandardParametersKey.ReserveConnectorZeroSupported + ).value ); } public async addReservation(reservation: Reservation): Promise { - if (Utils.isNullOrUndefined(this.reservations)) { - this.reservations = []; - } const [exists, reservationFound] = this.doesReservationExists(reservation); if (exists) { await this.removeReservation(reservationFound); } - this.reservations.push(reservation); + const connectorStatus = this.getConnectorStatus(reservation.connectorId); + connectorStatus.reservation = reservation; + connectorStatus.status = ConnectorStatusEnum.Reserved; if (reservation.connectorId === 0) { return; } - this.getConnectorStatus(reservation.connectorId).status = ConnectorStatusEnum.Reserved; await this.ocppRequestService.requestHandler< StatusNotificationRequest, StatusNotificationResponse @@ -963,18 +949,23 @@ export class ChargingStation { reservation: Reservation, reason?: ReservationTerminationReason ): Promise { - const sameReservation = (r: Reservation) => r.id === reservation.id; - const index = this.reservations?.findIndex(sameReservation); - this.reservations.splice(index, 1); + const connector = this.getConnectorStatus(reservation.connectorId); switch (reason) { - case ReservationTerminationReason.TRANSACTION_STARTED: - // No action needed + case ReservationTerminationReason.TRANSACTION_STARTED: { + delete connector.reservation; + if (reservation.connectorId === 0) { + connector.status = ConnectorStatusEnum.Available; + } break; - case ReservationTerminationReason.CONNECTOR_STATE_CHANGED: - // No action needed + } + case ReservationTerminationReason.CONNECTOR_STATE_CHANGED: { + delete connector.reservation; break; - default: // ReservationTerminationReason.EXPIRED, ReservationTerminationReason.CANCELED - this.getConnectorStatus(reservation.connectorId).status = ConnectorStatusEnum.Available; + } + default: { + // ReservationTerminationReason.EXPIRED, ReservationTerminationReason.CANCELED + connector.status = ConnectorStatusEnum.Available; + delete connector.reservation; await this.ocppRequestService.requestHandler< StatusNotificationRequest, StatusNotificationResponse @@ -988,67 +979,36 @@ export class ChargingStation { ) ); 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 getReservationBy(key: string, value: number | string): Reservation { + if (this.hasEvses) { + for (const evse of this.evses.values()) { + for (const connector of evse.connectors.values()) { + if (connector?.reservation?.[key] === value) { + return connector.reservation; + } + } + } + } else { + for (const connector of this.connectors.values()) { + if (connector?.reservation?.[key] === value) { + return connector.reservation; + } + } + } } public doesReservationExists(reservation: Partial): [boolean, Reservation] { - const sameReservation = (r: Reservation) => r.id === reservation.id; - const foundReservation = this.reservations?.find(sameReservation); + const foundReservation = this.getReservationBy( + ReservationFilterKey.RESERVATION_ID, + reservation?.id + ); return Utils.isUndefined(foundReservation) ? [false, null] : [true, foundReservation]; } - public async isAuthorized( - connectorId: number, - idTag: string, - parentIdTag?: string - ): Promise { - 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 authorized; - } - public startReservationExpiryDateSetInterval(customInterval?: number): void { const interval = customInterval ?? Constants.DEFAULT_RESERVATION_EXPIRATION_OBSERVATION_INTERVAL; @@ -1058,15 +1018,18 @@ export class ChargingStation { ); // eslint-disable-next-line @typescript-eslint/no-misused-promises this.reservationExpiryDateSetInterval = setInterval(async (): Promise => { - 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` - ); + if (this.hasEvses) { + for (const evse of this.evses.values()) { + for (const connector of evse.connectors.values()) { + if (connector?.reservation?.expiryDate.toString() < new Date().toISOString()) { + await this.removeReservation(connector.reservation); + } + } + } + } else { + for (const connector of this.connectors.values()) { + if (connector?.reservation?.expiryDate.toString() < new Date().toISOString()) { + await this.removeReservation(connector.reservation); } } } @@ -1079,20 +1042,24 @@ export class ChargingStation { } public validateIncomingRequestWithReservation(connectorId: number, idTag: string): boolean { - const reservation = this.getReservationByConnectorId(connectorId); - return Utils.isUndefined(reservation) || reservation.idTag !== idTag; + const reservation = this.getReservationBy(ReservationFilterKey.CONNECTOR_ID, connectorId); + return !Utils.isUndefined(reservation) && reservation.idTag === idTag; } public isConnectorReservable( reservationId: number, - connectorId?: number, - idTag?: string + idTag?: string, + connectorId?: number ): boolean { const [alreadyExists] = this.doesReservationExists({ id: reservationId }); if (alreadyExists) { return alreadyExists; } - const userReservedAlready = Utils.isUndefined(this.getReservationByIdTag(idTag)) ? false : true; + const userReservedAlready = Utils.isUndefined( + this.getReservationBy(ReservationFilterKey.ID_TAG, idTag) + ) + ? false + : true; const notConnectorZero = Utils.isUndefined(connectorId) ? true : connectorId > 0; const freeConnectorsAvailable = this.getNumberOfReservableConnectors() > 0; return !alreadyExists && !userReservedAlready && notConnectorZero && freeConnectorsAvailable; @@ -1100,20 +1067,41 @@ export class ChargingStation { private getNumberOfReservableConnectors(): number { let reservableConnectors = 0; - this.connectors.forEach((connector, id) => { + if (this.hasEvses) { + for (const evse of this.evses.values()) { + reservableConnectors = this.countReservableConnectors(evse.connectors); + } + } else { + reservableConnectors = this.countReservableConnectors(this.connectors); + } + return reservableConnectors - this.getNumberOfReservationsOnConnectorZero(); + } + + private countReservableConnectors(connectors: Map) { + let reservableConnectors = 0; + for (const [id, connector] of connectors) { if (id === 0) { - return; + continue; } if (connector.status === ConnectorStatusEnum.Available) { - reservableConnectors++; + ++reservableConnectors; } - }); - return reservableConnectors - this.getNumberOfReservationsOnConnectorZero(); + } + return reservableConnectors; } private getNumberOfReservationsOnConnectorZero(): number { - const reservations = this.reservations?.filter((reservation) => reservation.connectorId === 0); - return Utils.isNullOrUndefined(reservations) ? 0 : reservations.length; + let numberOfReservations = 0; + if (this.hasEvses) { + for (const evse of this.evses.values()) { + if (evse.connectors.get(0)?.reservation) { + ++numberOfReservations; + } + } + } else if (this.connectors.get(0)?.reservation) { + ++numberOfReservations; + } + return numberOfReservations; } private flushMessageBuffer(): void { @@ -1293,7 +1281,8 @@ export class ChargingStation { } private handleUnsupportedVersion(version: OCPPVersion) { - const errorMsg = `Unsupported protocol version '${version}' configured in template file ${this.templateFile}`; + const errorMsg = `Unsupported protocol version '${version}' configured + in template file ${this.templateFile}`; logger.error(`${this.logPrefix()} ${errorMsg}`); throw new BaseError(errorMsg); } diff --git a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts index 6917533e..bfbe1232 100644 --- a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts @@ -39,6 +39,8 @@ import { OCPP16AvailabilityType, type OCPP16BootNotificationRequest, type OCPP16BootNotificationResponse, + type OCPP16CancelReservationRequest, + type OCPP16CancelReservationResponse, OCPP16ChargePointErrorCode, OCPP16ChargePointStatus, type OCPP16ChargingProfile, @@ -62,6 +64,8 @@ import { OCPP16IncomingRequestCommand, OCPP16MessageTrigger, OCPP16RequestCommand, + type OCPP16ReserveNowRequest, + type OCPP16ReserveNowResponse, OCPP16StandardParametersKey, type OCPP16StartTransactionRequest, type OCPP16StartTransactionResponse, @@ -77,21 +81,15 @@ import { OCPPVersion, type RemoteStartTransactionRequest, type RemoteStopTransactionRequest, + ReservationFilterKey, + ReservationTerminationReason, type ResetRequest, type SetChargingProfileRequest, type SetChargingProfileResponse, + type StartTransactionRequest, type UnlockConnectorRequest, type UnlockConnectorResponse, } from '../../../types'; -import type { - OCPP16CancelReservationRequest, - OCPP16ReserveNowRequest, -} from '../../../types/ocpp/1.6/Requests'; -import { ReservationTerminationReason } from '../../../types/ocpp/1.6/Reservation'; -import type { - OCPP16CancelReservationResponse, - OCPP16ReserveNowResponse, -} from '../../../types/ocpp/1.6/Responses'; import { Constants, Utils, logger } from '../../../utils'; import { OCPPConstants } from '../OCPPConstants'; import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService'; @@ -341,7 +339,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { } catch (error) { // Log logger.error( - `${chargingStation.logPrefix()} ${moduleName}.incomingRequestHandler: Handle incoming request error:`, + `${chargingStation.logPrefix()} ${moduleName}.incomingRequestHandler: + Handle incoming request error:`, error ); throw error; @@ -394,7 +393,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { ); } logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation` + `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found + for command '${commandName}' PDU validation` ); return false; } @@ -415,9 +415,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { logger.info( `${chargingStation.logPrefix()} ${ commandPayload.type - } reset command received, simulating it. The station will be back online in ${Utils.formatDurationMilliSeconds( - chargingStation.stationInfo.resetTime - )}` + } reset command received, simulating it. The station will be + back online in ${Utils.formatDurationMilliSeconds(chargingStation.stationInfo.resetTime)}` ); return OCPP16Constants.OCPP_RESPONSE_ACCEPTED; } @@ -429,7 +428,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { const connectorId = commandPayload.connectorId; if (chargingStation.hasConnector(connectorId) === false) { logger.error( - `${chargingStation.logPrefix()} Trying to unlock a non existing connector id ${connectorId.toString()}` + `${chargingStation.logPrefix()} Trying to unlock a non existing + connector id ${connectorId.toString()}` ); return OCPP16Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED; } @@ -576,9 +576,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { } if (chargingStation.hasConnector(commandPayload.connectorId) === false) { logger.error( - `${chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector id ${ - commandPayload.connectorId - }` + `${chargingStation.logPrefix()} Trying to set charging profile(s) to a + non existing connector id ${commandPayload.connectorId}` ); return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED; } @@ -597,9 +596,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { false) ) { logger.error( - `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${ - commandPayload.connectorId - } without a started transaction` + `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) + on connector ${commandPayload.connectorId} without a started transaction` ); return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED; } @@ -632,9 +630,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { } if (chargingStation.hasConnector(commandPayload.connectorId) === false) { logger.error( - `${chargingStation.logPrefix()} Trying to get composite schedule to a non existing connector id ${ - commandPayload.connectorId - }` + `${chargingStation.logPrefix()} Trying to get composite schedule to a + non existing connector id ${commandPayload.connectorId}` ); return OCPP16Constants.OCPP_RESPONSE_REJECTED; } @@ -682,9 +679,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { } if (chargingStation.hasConnector(commandPayload.connectorId) === false) { logger.error( - `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector id ${ - commandPayload.connectorId - }` + `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to + a non existing connector id ${commandPayload.connectorId}` ); return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN; } @@ -767,7 +763,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { const connectorId: number = commandPayload.connectorId; if (chargingStation.hasConnector(connectorId) === false) { logger.error( - `${chargingStation.logPrefix()} Trying to change the availability of a non existing connector id ${connectorId.toString()}` + `${chargingStation.logPrefix()} Trying to change the availability of a + non existing connector id ${connectorId.toString()}` ); return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED; } @@ -828,16 +825,16 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { chargingStation: ChargingStation, commandPayload: RemoteStartTransactionRequest ): Promise { - const transactionConnectorId = commandPayload.connectorId; + const { connectorId: transactionConnectorId, idTag, chargingProfile } = commandPayload; const reserved = chargingStation.getConnectorStatus(transactionConnectorId).status === OCPP16ChargePointStatus.Reserved; + const reservedOnConnectorZero = + chargingStation.getConnectorStatus(0).status === OCPP16ChargePointStatus.Reserved; if ( - reserved && - chargingStation.validateIncomingRequestWithReservation( - transactionConnectorId, - commandPayload.idTag - ) + (reserved && + !chargingStation.validateIncomingRequestWithReservation(transactionConnectorId, idTag)) || + (reservedOnConnectorZero && !chargingStation.validateIncomingRequestWithReservation(0, idTag)) ) { return OCPP16Constants.OCPP_RESPONSE_REJECTED; } @@ -845,7 +842,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { return this.notifyRemoteStartTransactionRejected( chargingStation, transactionConnectorId, - commandPayload.idTag + idTag ); } if ( @@ -855,12 +852,13 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { return this.notifyRemoteStartTransactionRejected( chargingStation, transactionConnectorId, - commandPayload.idTag + idTag ); } - const remoteStartTransactionLogMsg = `${chargingStation.logPrefix()} Transaction remotely STARTED on ${ + const remoteStartTransactionLogMsg = ` + ${chargingStation.logPrefix()} Transaction remotely STARTED on ${ chargingStation.stationInfo.chargingStationId - }#${transactionConnectorId.toString()} for idTag '${commandPayload.idTag}'`; + }#${transactionConnectorId.toString()} for idTag '${idTag}'`; await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, transactionConnectorId, @@ -868,60 +866,55 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { ); const connectorStatus = chargingStation.getConnectorStatus(transactionConnectorId); // Check if authorized - if (chargingStation.getAuthorizeRemoteTxRequests() === true) { - const authorized = await chargingStation.isAuthorized( - transactionConnectorId, - commandPayload.idTag - ); - if (authorized === true) { - // Authorization successful, start transaction + if ( + chargingStation.getAuthorizeRemoteTxRequests() && + (await OCPP16ServiceUtils.isIdTagAuthorized(chargingStation, transactionConnectorId, idTag)) + ) { + // Authorization successful, start transaction + if ( + this.setRemoteStartTransactionChargingProfile( + chargingStation, + transactionConnectorId, + chargingProfile + ) === true + ) { + connectorStatus.transactionRemoteStarted = true; + const startTransactionPayload: Partial = { + connectorId: transactionConnectorId, + idTag: idTag, + }; + if (reserved || reservedOnConnectorZero) { + const reservation = chargingStation.getReservationBy( + ReservationFilterKey.CONNECTOR_ID, + reservedOnConnectorZero ? 0 : transactionConnectorId + ); + startTransactionPayload.reservationId = reservation.id; + await chargingStation.removeReservation( + reservation, + ReservationTerminationReason.TRANSACTION_STARTED + ); + } if ( - this.setRemoteStartTransactionChargingProfile( - chargingStation, - transactionConnectorId, - commandPayload.chargingProfile - ) === true + ( + await chargingStation.ocppRequestService.requestHandler< + OCPP16StartTransactionRequest, + OCPP16StartTransactionResponse + >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, startTransactionPayload) + ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED ) { - connectorStatus.transactionRemoteStarted = true; - const startTransactionPayload: JsonType = { - connectorId: transactionConnectorId, - idTag: commandPayload.idTag, - }; - if (reserved) { - const reservation = chargingStation.getReservationByConnectorId(transactionConnectorId); - startTransactionData.reservationId = reservation.id; - await chargingStation.removeReservation( - reservation, - ReservationTerminationReason.TRANSACTION_STARTED - ); - } - if ( - ( - await chargingStation.ocppRequestService.requestHandler< - OCPP16StartTransactionRequest, - OCPP16StartTransactionResponse - >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, startTransactionData) - ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED - ) { - logger.debug(remoteStartTransactionLogMsg); - return OCPP16Constants.OCPP_RESPONSE_ACCEPTED; - } - return this.notifyRemoteStartTransactionRejected( - chargingStation, - transactionConnectorId, - commandPayload.idTag - ); + logger.debug(remoteStartTransactionLogMsg); + return OCPP16Constants.OCPP_RESPONSE_ACCEPTED; } return this.notifyRemoteStartTransactionRejected( chargingStation, transactionConnectorId, - commandPayload.idTag + idTag ); } return this.notifyRemoteStartTransactionRejected( chargingStation, transactionConnectorId, - commandPayload.idTag + idTag ); } // No authorization check required, start transaction @@ -929,7 +922,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { this.setRemoteStartTransactionChargingProfile( chargingStation, transactionConnectorId, - commandPayload.chargingProfile + chargingProfile ) === true ) { connectorStatus.transactionRemoteStarted = true; @@ -940,7 +933,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { OCPP16StartTransactionResponse >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, { connectorId: transactionConnectorId, - idTag: commandPayload.idTag, + idTag, }) ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED ) { @@ -950,13 +943,13 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { return this.notifyRemoteStartTransactionRejected( chargingStation, transactionConnectorId, - commandPayload.idTag + idTag ); } return this.notifyRemoteStartTransactionRejected( chargingStation, transactionConnectorId, - commandPayload.idTag + idTag ); } @@ -975,7 +968,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { ); } logger.warn( - `${chargingStation.logPrefix()} Remote starting transaction REJECTED on connector id ${connectorId.toString()}, idTag '${idTag}', availability '${ + `${chargingStation.logPrefix()} Remote starting transaction REJECTED on connector id + ${connectorId.toString()}, idTag '${idTag}', availability '${ chargingStation.getConnectorStatus(connectorId)?.availability }', status '${chargingStation.getConnectorStatus(connectorId)?.status}'` ); @@ -990,7 +984,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { if (cp && cp.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.TX_PROFILE) { OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, cp); logger.debug( - `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction on connector id ${connectorId}: %j`, + `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction + on connector id ${connectorId}: %j`, cp ); return true; @@ -1047,7 +1042,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { } } logger.warn( - `${chargingStation.logPrefix()} Trying to remote stop a non existing transaction with id: ${transactionId.toString()}` + `${chargingStation.logPrefix()} Trying to remote stop a non existing transaction with id: + ${transactionId.toString()}` ); return OCPP16Constants.OCPP_RESPONSE_REJECTED; } @@ -1064,7 +1060,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { ) === false ) { logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: feature profile not supported` + `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: + Cannot simulate firmware update: feature profile not supported` ); return OCPP16Constants.OCPP_RESPONSE_EMPTY; } @@ -1073,7 +1070,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { chargingStation.stationInfo.firmwareStatus !== OCPP16FirmwareStatus.Installed ) { logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: firmware update is already in progress` + `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: + Cannot simulate firmware update: firmware update is already in progress` ); return OCPP16Constants.OCPP_RESPONSE_EMPTY; } @@ -1172,7 +1170,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { if (runningTransactions > 0) { const waitTime = 15 * 1000; logger.debug( - `${chargingStation.logPrefix()} ${moduleName}.updateFirmwareSimulation: ${runningTransactions} transaction(s) in progress, waiting ${ + `${chargingStation.logPrefix()} ${moduleName}.updateFirmwareSimulation: + ${runningTransactions} transaction(s) in progress, waiting ${ waitTime / 1000 } seconds before continuing firmware update simulation` ); @@ -1260,7 +1259,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { ) === false ) { logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Cannot get diagnostics: feature profile not supported` + `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: + Cannot get diagnostics: feature profile not supported` ); return OCPP16Constants.OCPP_RESPONSE_EMPTY; } @@ -1298,9 +1298,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { }) .catch((error) => { logger.error( - `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Error while sending '${ - OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION - }'`, + `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: + Error while sending '${OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION}'`, error ); }); @@ -1534,19 +1533,25 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { chargingStation: ChargingStation, commandPayload: OCPP16ReserveNowRequest ): Promise { + if ( + !OCPP16ServiceUtils.checkFeatureProfile( + chargingStation, + OCPP16SupportedFeatureProfiles.Reservation, + OCPP16IncomingRequestCommand.RESERVE_NOW + ) + ) { + return OCPPConstants.OCPP_RESERVATION_RESPONSE_REJECTED; + } const { reservationId, idTag, connectorId } = commandPayload; let response: OCPP16ReserveNowResponse; try { - if ( - !chargingStation.supportsReservations() && - chargingStation.isConnectorAvailable(connectorId) - ) { + if (!chargingStation.isConnectorAvailable(connectorId) && connectorId > 0) { return OCPPConstants.OCPP_RESERVATION_RESPONSE_REJECTED; } - if (connectorId === 0 && !chargingStation.supportsReservationsOnConnectorId0()) { + if (connectorId === 0 && !chargingStation.getReservationOnConnectorId0Enabled()) { return OCPPConstants.OCPP_RESERVATION_RESPONSE_REJECTED; } - if (!(await chargingStation.isAuthorized(connectorId, idTag))) { + if (!(await OCPP16ServiceUtils.isIdTagAuthorized(chargingStation, connectorId, idTag))) { return OCPPConstants.OCPP_RESERVATION_RESPONSE_REJECTED; } switch (chargingStation.getConnectorStatus(connectorId).status) { @@ -1560,13 +1565,13 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { response = OCPPConstants.OCPP_RESERVATION_RESPONSE_UNAVAILABLE; break; case ConnectorStatusEnum.Reserved: - if (!chargingStation.isConnectorReservable(reservationId, connectorId, idTag)) { + if (!chargingStation.isConnectorReservable(reservationId, idTag, connectorId)) { response = OCPPConstants.OCPP_RESERVATION_RESPONSE_OCCUPIED; break; } // eslint-disable-next-line no-fallthrough default: - if (!chargingStation.isConnectorReservable(reservationId)) { + if (!chargingStation.isConnectorReservable(reservationId, idTag)) { response = OCPPConstants.OCPP_RESERVATION_RESPONSE_OCCUPIED; break; } @@ -1593,12 +1598,22 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { chargingStation: ChargingStation, commandPayload: OCPP16CancelReservationRequest ): Promise { + if ( + !OCPP16ServiceUtils.checkFeatureProfile( + chargingStation, + OCPP16SupportedFeatureProfiles.Reservation, + OCPP16IncomingRequestCommand.CANCEL_RESERVATION + ) + ) { + return OCPPConstants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED; + } try { 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` + `${chargingStation.logPrefix()} Reservation with ID ${reservationId} + does not exist on charging station` ); return OCPPConstants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED; } diff --git a/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts b/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts index 1999473d..543e2eda 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts @@ -25,6 +25,7 @@ import { type OCPP16AuthorizeRequest, type OCPP16AuthorizeResponse, type OCPP16BootNotificationResponse, + type OCPP16CancelReservationResponse, OCPP16ChargePointStatus, type OCPP16DataTransferResponse, type OCPP16DiagnosticsStatusNotificationResponse, @@ -35,6 +36,7 @@ import { type OCPP16MeterValuesRequest, type OCPP16MeterValuesResponse, OCPP16RequestCommand, + type OCPP16ReserveNowResponse, OCPP16StandardParametersKey, type OCPP16StartTransactionRequest, type OCPP16StartTransactionResponse, @@ -161,6 +163,22 @@ export class OCPP16ResponseService extends OCPPResponseService { 'constructor' ), ], + [ + OCPP16RequestCommand.RESERVE_NOW, + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/ReserveNowResponse.json', + moduleName, + 'constructor' + ), + ], + [ + OCPP16RequestCommand.CANCEL_RESERVATION, + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/CancelReservationResponse.json', + moduleName, + 'constructor' + ), + ], ]); this.jsonIncomingRequestResponseSchemas = new Map([ [ diff --git a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts index 23381f9e..c73c32dc 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts @@ -2,7 +2,7 @@ import type { JSONSchemaType } from 'ajv'; -import type { ChargingStation } from '../../../charging-station'; +import { type ChargingStation, ChargingStationUtils } from '../../../charging-station'; import { OCPPError } from '../../../exception'; import { CurrentType, @@ -13,6 +13,9 @@ import { MeterValueContext, MeterValueLocation, MeterValueUnit, + OCPP16AuthorizationStatus, + type OCPP16AuthorizeRequest, + type OCPP16AuthorizeResponse, type OCPP16ChargingProfile, type OCPP16IncomingRequestCommand, type OCPP16MeterValue, @@ -837,6 +840,30 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { ); } + public static async isIdTagAuthorized( + chargingStation: ChargingStation, + connectorId: number, + idTag: string, + parentIdTag?: string + ): Promise { + let authorized = false; + const connectorStatus = chargingStation.getConnectorStatus(connectorId); + if (OCPP16ServiceUtils.isIdTagLocalAuthorized(chargingStation, idTag)) { + connectorStatus.localAuthorizeIdTag = idTag; + connectorStatus.idTagLocalAuthorized = true; + authorized = true; + } else if (chargingStation.getMustAuthorizeAtRemoteStart() === true) { + connectorStatus.authorizeIdTag = idTag; + authorized = await OCPP16ServiceUtils.isIdTagRemoteAuthorized(chargingStation, idTag); + } 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 static buildSampledValue( sampledValueTemplate: SampledValueTemplate, value: number, @@ -912,4 +939,30 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { return MeterValueUnit.VOLT; } } + + private static isIdTagLocalAuthorized(chargingStation: ChargingStation, idTag: string): boolean { + return ( + chargingStation.getLocalAuthListEnabled() === true && + chargingStation.hasIdTags() === true && + Utils.isNotEmptyString( + chargingStation.idTagsCache + .getIdTags(ChargingStationUtils.getIdTagsFile(chargingStation.stationInfo)) + ?.find((tag) => tag === idTag) + ) + ); + } + + private static async isIdTagRemoteAuthorized( + chargingStation: ChargingStation, + idTag: string + ): Promise { + const authorizeResponse: OCPP16AuthorizeResponse = + await chargingStation.ocppRequestService.requestHandler< + OCPP16AuthorizeRequest, + OCPP16AuthorizeResponse + >(chargingStation, OCPP16RequestCommand.AUTHORIZE, { + idTag: idTag, + }); + return authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED; + } } diff --git a/src/charging-station/ocpp/OCPPConstants.ts b/src/charging-station/ocpp/OCPPConstants.ts index b8448fb6..2d2a34a0 100644 --- a/src/charging-station/ocpp/OCPPConstants.ts +++ b/src/charging-station/ocpp/OCPPConstants.ts @@ -9,7 +9,7 @@ import { TriggerMessageStatus, UnlockStatus, } from '../../types'; -import { CancelReservationStatus, ReservationStatus } from '../../types/ocpp/Responses'; +import { ReservationStatus } from '../../types/ocpp/Responses'; import { Constants } from '../../utils'; export class OCPPConstants { @@ -102,17 +102,33 @@ export class OCPPConstants { status: DataTransferStatus.REJECTED, }); - static readonly OCPP_RESERVATION_RESPONSE_ACCEPTED = Object.freeze({ status: ReservationStatus.ACCEPTED }); // Reservation has been made - static readonly OCPP_RESERVATION_RESPONSE_FAULTED = Object.freeze({ status: ReservationStatus.FAULTED }); // Reservation has not been made, because of connector in FAULTED state - static readonly OCPP_RESERVATION_RESPONSE_OCCUPIED = Object.freeze({ status: ReservationStatus.OCCUPIED }); // Reservation has not been made, because all connectors are OCCUPIED - static readonly OCPP_RESERVATION_RESPONSE_REJECTED = Object.freeze({ status: ReservationStatus.REJECTED }); // Reservation has not been made, because CS is not configured to accept reservations - static readonly OCPP_RESERVATION_RESPONSE_UNAVAILABLE = Object.freeze({ status: ReservationStatus.UNAVAILABLE }); // Reservation has not been made, because connectors are spec. connector is in UNAVAILABLE state + static readonly OCPP_RESERVATION_RESPONSE_ACCEPTED = Object.freeze({ + status: ReservationStatus.ACCEPTED, + }); // Reservation has been made - static readonly OCPP_CANCEL_RESERVATION_RESPONSE_ACCEPTED = Object.freeze({ status: CancelReservationStatus.ACCEPTED }); // Reservation for id has been cancelled has been made - static readonly OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED = Object.freeze({ status: CancelReservationStatus.REJECTED }); // Reservation could not be cancelled, because there is no reservation active for id + static readonly OCPP_RESERVATION_RESPONSE_FAULTED = Object.freeze({ + status: ReservationStatus.FAULTED, + }); // Reservation has not been made, because of connector in FAULTED state - static readonly OCPP_SUPPORTED_FEATURE_PROFILE_RESERVATION = 'Reservation'; - static readonly OCPP_RESERVE_CONNECTOR_ZERO_SUPPORTED = 'ReserveConnectorZeroSupported'; + static readonly OCPP_RESERVATION_RESPONSE_OCCUPIED = Object.freeze({ + status: ReservationStatus.OCCUPIED, + }); // Reservation has not been made, because all connectors are OCCUPIED + + static readonly OCPP_RESERVATION_RESPONSE_REJECTED = Object.freeze({ + status: ReservationStatus.REJECTED, + }); // Reservation has not been made, because CS is not configured to accept reservations + + static readonly OCPP_RESERVATION_RESPONSE_UNAVAILABLE = Object.freeze({ + status: ReservationStatus.UNAVAILABLE, + }); // Reservation has not been made, because connectors are spec. connector is in UNAVAILABLE state + + static readonly OCPP_CANCEL_RESERVATION_RESPONSE_ACCEPTED = Object.freeze({ + status: GenericStatus.Accepted, + }); // Reservation for id has been cancelled has been made + + static readonly OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED = Object.freeze({ + status: GenericStatus.Rejected, + }); // Reservation could not be cancelled, because there is no reservation active for id protected constructor() { // This is intentional diff --git a/src/types/ChargingStationTemplate.ts b/src/types/ChargingStationTemplate.ts index a11ac892..e3594d02 100644 --- a/src/types/ChargingStationTemplate.ts +++ b/src/types/ChargingStationTemplate.ts @@ -14,7 +14,6 @@ import type { MessageTrigger, RequestCommand, } from './ocpp/Requests'; -import type { Reservation } from './ocpp/Reservation'; export enum CurrentType { AC = 'AC', @@ -116,5 +115,4 @@ export type ChargingStationTemplate = { AutomaticTransactionGenerator?: AutomaticTransactionGeneratorConfiguration; Evses?: Record; Connectors?: Record; - reservation?: Reservation[]; }; diff --git a/src/types/ConnectorStatus.ts b/src/types/ConnectorStatus.ts index d7698107..33c72242 100644 --- a/src/types/ConnectorStatus.ts +++ b/src/types/ConnectorStatus.ts @@ -3,6 +3,7 @@ import type { ChargingProfile } from './ocpp/ChargingProfile'; import type { ConnectorStatusEnum } from './ocpp/ConnectorStatusEnum'; import type { MeterValue } from './ocpp/MeterValues'; import type { AvailabilityType } from './ocpp/Requests'; +import type { Reservation } from './ocpp/Reservation'; export type ConnectorStatus = { availability: AvailabilityType; @@ -22,4 +23,5 @@ export type ConnectorStatus = { transactionEnergyActiveImportRegisterValue?: number; // In Wh transactionBeginMeterValue?: MeterValue; chargingProfiles?: ChargingProfile[]; + reservation?: Reservation; }; diff --git a/src/types/index.ts b/src/types/index.ts index fee7eab6..7bfa2150 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -108,6 +108,8 @@ export { type ResetRequest, type SetChargingProfileRequest, type UnlockConnectorRequest, + type OCPP16ReserveNowRequest, + type OCPP16CancelReservationRequest, } from './ocpp/1.6/Requests'; export { type ChangeAvailabilityResponse, @@ -127,6 +129,8 @@ export { type OCPP16UpdateFirmwareResponse, type SetChargingProfileResponse, type UnlockConnectorResponse, + type OCPP16ReserveNowResponse, + type OCPP16CancelReservationResponse, } from './ocpp/1.6/Responses'; export { ChargePointErrorCode } from './ocpp/ChargePointErrorCode'; export { @@ -251,3 +255,5 @@ export { WebSocketCloseEventStatusCode, WebSocketCloseEventStatusString, } from './WebSocket'; +export { ReservationFilterKey, ReservationTerminationReason } from './ocpp/1.6/Reservation'; +export { type Reservation } from './ocpp/Reservation'; diff --git a/src/types/ocpp/1.6/Reservation.ts b/src/types/ocpp/1.6/Reservation.ts index 49bebd14..bbc3de7a 100644 --- a/src/types/ocpp/1.6/Reservation.ts +++ b/src/types/ocpp/1.6/Reservation.ts @@ -10,5 +10,13 @@ export enum ReservationTerminationReason { EXPIRED = 'Expired', TRANSACTION_STARTED = 'TransactionStarted', CONNECTOR_STATE_CHANGED = 'ConnectorStateChanged', - CANCELED = 'ReservationCanceled', + RESERVATION_CANCELED = 'ReservationCanceled', +} + +export enum ReservationFilterKey { + RESERVATION_ID = 'id', + ID_TAG = 'idTag', + PARENT_ID_TAG = 'parentIdTag', + CONNECTOR_ID = 'connectorId', + EVSE_ID = 'evseId', } diff --git a/src/types/ocpp/1.6/Responses.ts b/src/types/ocpp/1.6/Responses.ts index 15c76a71..1a163aa8 100644 --- a/src/types/ocpp/1.6/Responses.ts +++ b/src/types/ocpp/1.6/Responses.ts @@ -2,7 +2,7 @@ import type { OCPP16ChargingSchedule } from './ChargingProfile'; import type { EmptyObject } from '../../EmptyObject'; import type { JsonObject } from '../../JsonType'; import type { OCPPConfigurationKey } from '../Configuration'; -import type { GenericStatus, RegistrationStatusEnumType } from '../Responses'; +import { GenericStatus, type RegistrationStatusEnumType } from '../Responses'; export interface OCPP16HeartbeatResponse extends JsonObject { currentTime: Date; @@ -110,13 +110,8 @@ export interface OCPP16DataTransferResponse extends JsonObject { data?: string; } -export enum OCPP16CancelReservationStatus { - ACCEPTED = 'Accepted', - REJECTED = 'Rejected', -} - -export interface OCPP16CancelReservationResponse { - status: OCPP16CancelReservationStatus; +export interface OCPP16CancelReservationResponse extends JsonObject { + status: GenericStatus; } export enum OCPP16ReservationStatus { @@ -125,8 +120,9 @@ export enum OCPP16ReservationStatus { OCCUPIED = 'Occupied', REJECTED = 'Rejected', UNAVAILABLE = 'Unavailable', + NOT_SUPPORTED = 'NotSupported', } -export interface OCPP16ReserveNowResponse { +export interface OCPP16ReserveNowResponse extends JsonObject { status: OCPP16ReservationStatus; } diff --git a/src/types/ocpp/Requests.ts b/src/types/ocpp/Requests.ts index 7eef7b5b..9ca43da7 100644 --- a/src/types/ocpp/Requests.ts +++ b/src/types/ocpp/Requests.ts @@ -3,6 +3,7 @@ import type { OCPP16MeterValuesRequest } from './1.6/MeterValues'; import { OCPP16AvailabilityType, type OCPP16BootNotificationRequest, + type OCPP16CancelReservationRequest, type OCPP16DataTransferRequest, type OCPP16DiagnosticsStatusNotificationRequest, OCPP16FirmwareStatus, @@ -11,6 +12,7 @@ import { OCPP16IncomingRequestCommand, OCPP16MessageTrigger, OCPP16RequestCommand, + type OCPP16ReserveNowRequest, type OCPP16StatusNotificationRequest, } from './1.6/Requests'; import { OperationalStatusEnumType } from './2.0/Common'; @@ -101,3 +103,7 @@ export const FirmwareStatus = { export type FirmwareStatus = OCPP16FirmwareStatus; export type ResponseType = JsonType | OCPPError; + +export type ReserveNowRequest = OCPP16ReserveNowRequest; + +export type CancelReservationRequest = OCPP16CancelReservationRequest; diff --git a/src/types/ocpp/Responses.ts b/src/types/ocpp/Responses.ts index a2f431d4..8735dc8a 100644 --- a/src/types/ocpp/Responses.ts +++ b/src/types/ocpp/Responses.ts @@ -2,7 +2,7 @@ import type { OCPP16MeterValuesResponse } from './1.6/MeterValues'; import { OCPP16AvailabilityStatus, type OCPP16BootNotificationResponse, - OCPP16CancelReservationStatus, + type OCPP16CancelReservationResponse, OCPP16ChargingProfileStatus, OCPP16ClearChargingProfileStatus, OCPP16ConfigurationStatus, @@ -111,7 +111,8 @@ export const ReservationStatus = { ...OCPP16ReservationStatus, }; -export type CancelReservationStatus = OCPP16CancelReservationStatus; export const CancelReservationStatus = { - ...OCPP16CancelReservationStatus, + ...GenericStatus, }; + +export type CancelReservationResponse = OCPP16CancelReservationResponse; -- 2.34.1