test: improve ErrorUtils coverage
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16IncomingRequestService.ts
index d92d526e4a062884a55021b0e858767ee267e68e..64cefe58653da3c955c6ec1d0537d1597f5a710e 100644 (file)
@@ -26,6 +26,7 @@ import {
   getConnectorChargingProfiles,
   prepareChargingProfileKind,
   removeExpiredReservations,
+  resetAuthorizeConnectorStatus,
   setConfigurationKeyValue
 } from '../../../charging-station/index.js'
 import { OCPPError } from '../../../exception/index.js'
@@ -105,6 +106,7 @@ import {
   convertToDate,
   convertToInt,
   formatDurationMilliSeconds,
+  handleIncomingRequestError,
   isAsyncFunction,
   isNotEmptyArray,
   isNotEmptyString,
@@ -431,7 +433,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
             }
           )
             .then(response => {
-              if (response.status === OCPP16AuthorizationStatus.ACCEPTED) {
+              if (response.idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) {
                 logger.debug(
                   `${chargingStation.logPrefix()} Remote start transaction ACCEPTED on ${
                     chargingStation.stationInfo?.chargingStationId
@@ -510,15 +512,10 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
         switch (requestedMessage) {
           case OCPP16MessageTrigger.BootNotification:
             chargingStation.ocppRequestService
-              .requestHandler<OCPP16BootNotificationRequest, OCPP16BootNotificationResponse>(
-              chargingStation,
-              OCPP16RequestCommand.BOOT_NOTIFICATION,
-              chargingStation.bootNotificationRequest as OCPP16BootNotificationRequest,
-              { skipBufferingOnError: true, triggerMessage: true }
-            )
-              .then(response => {
-                chargingStation.bootNotificationResponse = response
-              })
+              .requestHandler<
+            OCPP16BootNotificationRequest,
+            OCPP16BootNotificationResponse
+            >(chargingStation, OCPP16RequestCommand.BOOT_NOTIFICATION, chargingStation.bootNotificationRequest as OCPP16BootNotificationRequest, { skipBufferingOnError: true, triggerMessage: true })
               .catch(errorHandler)
             break
           case OCPP16MessageTrigger.Heartbeat:
@@ -655,7 +652,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
         // Throw exception
         throw new OCPPError(
           ErrorType.NOT_IMPLEMENTED,
-          `'${commandName}' is not implemented to handle request PDU ${JSON.stringify(
+          `${commandName} is not implemented to handle request PDU ${JSON.stringify(
             commandPayload,
             undefined,
             2
@@ -1166,17 +1163,27 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
     commandPayload: RemoteStartTransactionRequest
   ): Promise<GenericResponse> {
     if (commandPayload.connectorId == null) {
-      do {
-        commandPayload.connectorId = randomInt(1, chargingStation.getNumberOfConnectors())
-      } while (
-        chargingStation.getConnectorStatus(commandPayload.connectorId)?.transactionStarted ===
-          true &&
-        OCPP16ServiceUtils.hasReservation(
-          chargingStation,
-          commandPayload.connectorId,
-          commandPayload.idTag
+      for (
+        let connectorId = 1;
+        connectorId <= chargingStation.getNumberOfConnectors();
+        connectorId++
+      ) {
+        if (
+          chargingStation.getConnectorStatus(connectorId)?.transactionStarted === false &&
+          !OCPP16ServiceUtils.hasReservation(chargingStation, connectorId, commandPayload.idTag)
+        ) {
+          commandPayload.connectorId = connectorId
+          break
+        }
+      }
+      if (commandPayload.connectorId == null) {
+        logger.debug(
+          `${chargingStation.logPrefix()} Remote start transaction REJECTED on ${
+            chargingStation.stationInfo?.chargingStationId
+          }, idTag '${commandPayload.idTag}': no available connector found`
         )
-      )
+        return OCPP16Constants.OCPP_RESPONSE_REJECTED
+      }
     }
     const { connectorId: transactionConnectorId, idTag, chargingProfile } = commandPayload
     if (!chargingStation.hasConnector(transactionConnectorId)) {
@@ -1304,7 +1311,10 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
     commandPayload.retrieveDate = convertToDate(commandPayload.retrieveDate)!
     const { retrieveDate } = commandPayload
-    if (chargingStation.stationInfo?.firmwareStatus !== OCPP16FirmwareStatus.Installed) {
+    if (
+      chargingStation.stationInfo?.firmwareStatus != null &&
+      chargingStation.stationInfo.firmwareStatus !== OCPP16FirmwareStatus.Installed
+    ) {
       logger.warn(
         `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: firmware update is already in progress`
       )
@@ -1568,7 +1578,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
         })
         ftpClient?.close()
         // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-        return this.handleIncomingRequestError<GetDiagnosticsResponse>(
+        return handleIncomingRequestError<GetDiagnosticsResponse>(
           chargingStation,
           OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
           error as Error,
@@ -1638,7 +1648,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
       return OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_UNKNOWN_VENDOR_ID
     } catch (error) {
       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      return this.handleIncomingRequestError<OCPP16DataTransferResponse>(
+      return handleIncomingRequestError<OCPP16DataTransferResponse>(
         chargingStation,
         OCPP16IncomingRequestCommand.DATA_TRANSFER,
         error as Error,
@@ -1663,19 +1673,28 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
     commandPayload.expiryDate = convertToDate(commandPayload.expiryDate)!
     const { reservationId, idTag, connectorId } = commandPayload
+    if (!chargingStation.hasConnector(connectorId)) {
+      logger.error(
+        `${chargingStation.logPrefix()} Trying to reserve a non existing connector id ${connectorId}`
+      )
+      return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED
+    }
+    if (connectorId > 0 && !chargingStation.isConnectorAvailable(connectorId)) {
+      return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED
+    }
+    if (connectorId === 0 && !chargingStation.getReserveConnectorZeroSupported()) {
+      return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED
+    }
+    if (!(await OCPP16ServiceUtils.isIdTagAuthorized(chargingStation, connectorId, idTag))) {
+      return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED
+    }
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    const connectorStatus = chargingStation.getConnectorStatus(connectorId)!
+    resetAuthorizeConnectorStatus(connectorStatus)
     let response: OCPP16ReserveNowResponse
     try {
-      if (connectorId > 0 && !chargingStation.isConnectorAvailable(connectorId)) {
-        return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED
-      }
-      if (connectorId === 0 && !chargingStation.getReserveConnectorZeroSupported()) {
-        return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED
-      }
-      if (!(await OCPP16ServiceUtils.isIdTagAuthorized(chargingStation, connectorId, idTag))) {
-        return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED
-      }
       await removeExpiredReservations(chargingStation)
-      switch (chargingStation.getConnectorStatus(connectorId)?.status) {
+      switch (connectorStatus.status) {
         case OCPP16ChargePointStatus.Faulted:
           response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_FAULTED
           break
@@ -1712,7 +1731,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
       chargingStation.getConnectorStatus(connectorId)!.status = OCPP16ChargePointStatus.Available
       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      return this.handleIncomingRequestError<OCPP16ReserveNowResponse>(
+      return handleIncomingRequestError<OCPP16ReserveNowResponse>(
         chargingStation,
         OCPP16IncomingRequestCommand.RESERVE_NOW,
         error as Error,
@@ -1750,7 +1769,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
       return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_ACCEPTED
     } catch (error) {
       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      return this.handleIncomingRequestError<GenericResponse>(
+      return handleIncomingRequestError<GenericResponse>(
         chargingStation,
         OCPP16IncomingRequestCommand.CANCEL_RESERVATION,
         error as Error,