refactor: applied changes for pull request
authorJulian Buecher <julian.buecher@gmx.de>
Mon, 5 Jun 2023 14:19:22 +0000 (16:19 +0200)
committerJulian Buecher <julian.buecher@gmx.de>
Mon, 5 Jun 2023 14:20:06 +0000 (16:20 +0200)
21 files changed:
README.md
src/assets/station-templates/abb-atg.station-template.json
src/assets/station-templates/chargex.station-template.json
src/assets/station-templates/evlink.station-template.json
src/assets/station-templates/schneider-evses.station-template.json
src/assets/station-templates/schneider-imredd.station-template.json
src/assets/station-templates/siemens.station-template.json
src/assets/station-templates/virtual-simple.station-template.json
src/assets/station-templates/virtual.station-template.json
src/charging-station/ChargingStation.ts
src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts
src/charging-station/ocpp/1.6/OCPP16ResponseService.ts
src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts
src/charging-station/ocpp/OCPPConstants.ts
src/types/ChargingStationTemplate.ts
src/types/ConnectorStatus.ts
src/types/index.ts
src/types/ocpp/1.6/Reservation.ts
src/types/ocpp/1.6/Responses.ts
src/types/ocpp/Requests.ts
src/types/ocpp/Responses.ts

index 9fb599ae77d2589ba89b5a3539e8b0977a6238da..7ed5d2672d75d8871e064624bbd5b769e02da3b2 100644 (file)
--- 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
 
index 99a67c648f0c671fbbe8879b2d4e9ed378c26200..456089bfd0ef584960b086c8bb3d0da13cb6f7fe 100644 (file)
@@ -49,7 +49,7 @@
       {
         "key": "ReserveConnectorZeroSupported",
         "readonly": false,
-        "value": "true"
+        "value": "false"
       }
     ]
   },
index 6ff7562d823567d5b93db2449df6e7c36082a969..fca570940e21de8fa7206c78592e3d930cd9ffbf 100644 (file)
       {
         "key": "TransactionMessageRetryInterval",
         "value": "20"
+      },
+      {
+        "key": "ReserveConnectorZeroSupported",
+        "readonly": false,
+        "value": "false"
       }
     ]
   },
index 47938eeefc28da96e795a46769c3bc3ad192566d..5b9f9eacfe813d6a672c96cce6a092aa60c436da 100644 (file)
@@ -50,7 +50,7 @@
       {
         "key": "ReserveConnectorZeroSupported",
         "readonly": false,
-        "value": "true"
+        "value": "false"
       }
     ]
   },
index 7017f1e427f1382bf0cebee8ce4c87cc1175a364..f292519e1bed4eb5d0f3963b0c363758f91c78f3 100644 (file)
         "key": "WebSocketPingInterval",
         "readonly": false,
         "value": "60"
+      },
+      {
+        "key": "ReserveConnectorZeroSupported",
+        "readonly": false,
+        "value": "false"
       }
     ]
   },
index d6cc99f8a82d1f095b6f6427ce1dde3947233532..a4411047355b4b2da68f324b65dc679ad02d7b0a 100644 (file)
@@ -49,7 +49,7 @@
       {
         "key": "ReserveConnectorZeroSupported",
         "readonly": false,
-        "value": "true"
+        "value": "false"
       }
     ]
   },
index ac075ce6f58a77a9fdf5998b6f7dee8d29b2304b..439b5f92c53c2f67000d8c7a1c0bdbc6245bfeab 100644 (file)
@@ -44,7 +44,7 @@
       {
         "key": "ReserveConnectorZeroSupported",
         "readonly": false,
-        "value": "true"
+        "value": "false"
       }
     ]
   },
index 8a0c28011a8f9e0a7bef52754b0bc6843b15aa5f..37cbe8ad4236a868d18ff1c42e911cfb7545d265 100644 (file)
@@ -44,7 +44,7 @@
       {
         "key": "ReserveConnectorZeroSupported",
         "readonly": false,
-        "value": "true"
+        "value": "false"
       }
     ]
   },
index 962e792fe3ae444f3fc1188cd2cca7e04c1262cf..09086f8e4901a9015ec9e4d4c58883cdfdeca0d2 100644 (file)
         "key": "WebSocketPingInterval",
         "readonly": false,
         "value": "60"
+      },
+      {
+        "key": "ReserveConnectorZeroSupported",
+        "readonly": false,
+        "value": "false"
       }
     ]
   },
index 3eab1fc815956f94ae481f4bc2929206512a53aa..82ed3b8c0da8ad579e767e6695010352a66b1343 100644 (file)
@@ -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<void> {
-    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<void> {
-    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<Reservation>): [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<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 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<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`
-            );
+      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<number, ConnectorStatus>) {
+    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);
   }
index 6917533e529e56d353a247bf4b538780256c68aa..bfbe1232756dae8a34d456b8a6f62e417a71286e 100644 (file)
@@ -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<GenericResponse> {
-    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<StartTransactionRequest> = {
+          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<OCPP16ReserveNowResponse> {
+    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<OCPP16CancelReservationResponse> {
+    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;
       }
index 1999473d74690e03fce39af5a261d44b450c5930..543e2eda6e84e0a98dc8862c25c36c3d92b2dd93 100644 (file)
@@ -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<OCPP16ReserveNowResponse>(
+          'assets/json-schemas/ocpp/1.6/ReserveNowResponse.json',
+          moduleName,
+          'constructor'
+        ),
+      ],
+      [
+        OCPP16RequestCommand.CANCEL_RESERVATION,
+        OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16CancelReservationResponse>(
+          'assets/json-schemas/ocpp/1.6/CancelReservationResponse.json',
+          moduleName,
+          'constructor'
+        ),
+      ],
     ]);
     this.jsonIncomingRequestResponseSchemas = new Map([
       [
index 23381f9ec3dd63e5e47faed0301fc6fecbc01f75..c73c32dc2ce940f9c017550183a44dd45a3a7a43 100644 (file)
@@ -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<boolean> {
+    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<boolean> {
+    const authorizeResponse: OCPP16AuthorizeResponse =
+      await chargingStation.ocppRequestService.requestHandler<
+        OCPP16AuthorizeRequest,
+        OCPP16AuthorizeResponse
+      >(chargingStation, OCPP16RequestCommand.AUTHORIZE, {
+        idTag: idTag,
+      });
+    return authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED;
+  }
 }
index b8448fb63de6539d941646555dfbce427a8b0d30..2d2a34a0a22a25afc95c8e2fd8fb7b5f6e46a3d2 100644 (file)
@@ -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
index a11ac892779ff535f334f8052a671aa4d17243e1..e3594d02200701ce1502659300e265a8d07278fc 100644 (file)
@@ -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<string, EvseTemplate>;
   Connectors?: Record<string, ConnectorStatus>;
-  reservation?: Reservation[];
 };
index d7698107b9cc2146677db9624f66ae174d8c9dd8..33c72242b5e1983c4df4681295d6933a8cf2180a 100644 (file)
@@ -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;
 };
index fee7eab631dc797b16b1156d6cbaa5f8605f9bf1..7bfa2150abfaf1c26927024b6250d617541b24db 100644 (file)
@@ -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';
index 49bebd14049c5b7c55a841f12d8a1a015ad4df64..bbc3de7a93b8b03cb993c1720e322a749993f521 100644 (file)
@@ -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',
 }
index 15c76a71ee87d9206ec28c46d2720f9076c39be9..1a163aa82b72745c7ab42b560dae78c90f2642f5 100644 (file)
@@ -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;
 }
index 7eef7b5b14d35f2db1c3aebef13d8f1ad12aaf82..9ca43da713e29ad1112b074ab1dd78dc49e6f123 100644 (file)
@@ -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;
index a2f431d452d602b47875f1ee332ecb10a7748740..8735dc8ae10f2cf72362fe0ee9388a773794a9c2 100644 (file)
@@ -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;