refactor: split ChargingStationUtils class static methods into functions
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16IncomingRequestService.ts
index bfbe1232756dae8a34d456b8a6f62e417a71286e..218f97ea26a00bde102d61db8417aac0659420d7 100644 (file)
@@ -1,19 +1,19 @@
 // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
 
-import fs from 'node:fs';
-import path from 'node:path';
+import { createWriteStream, readdirSync } from 'node:fs';
+import { dirname, join, resolve } from 'node:path';
 import { URL, fileURLToPath } from 'node:url';
 
 import type { JSONSchemaType } from 'ajv';
 import { Client, type FTPResponse } from 'basic-ftp';
-import tar from 'tar';
+import { create } from 'tar';
 
 import { OCPP16Constants } from './OCPP16Constants';
 import { OCPP16ServiceUtils } from './OCPP16ServiceUtils';
 import {
   type ChargingStation,
   ChargingStationConfigurationUtils,
-  ChargingStationUtils,
+  checkChargingStation,
 } from '../../../charging-station';
 import { OCPPError } from '../../../exception';
 import {
@@ -24,7 +24,6 @@ import {
   type ClearChargingProfileRequest,
   type ClearChargingProfileResponse,
   type ConnectorStatus,
-  ConnectorStatusEnum,
   ErrorType,
   type GenericResponse,
   GenericStatus,
@@ -40,7 +39,6 @@ import {
   type OCPP16BootNotificationRequest,
   type OCPP16BootNotificationResponse,
   type OCPP16CancelReservationRequest,
-  type OCPP16CancelReservationResponse,
   OCPP16ChargePointErrorCode,
   OCPP16ChargePointStatus,
   type OCPP16ChargingProfile,
@@ -49,7 +47,6 @@ import {
   type OCPP16ClearCacheRequest,
   type OCPP16DataTransferRequest,
   type OCPP16DataTransferResponse,
-  OCPP16DataTransferStatus,
   OCPP16DataTransferVendorId,
   OCPP16DiagnosticsStatus,
   type OCPP16DiagnosticsStatusNotificationRequest,
@@ -90,8 +87,20 @@ import {
   type UnlockConnectorRequest,
   type UnlockConnectorResponse,
 } from '../../../types';
-import { Constants, Utils, logger } from '../../../utils';
-import { OCPPConstants } from '../OCPPConstants';
+import {
+  Constants,
+  convertToDate,
+  convertToInt,
+  formatDurationMilliSeconds,
+  getRandomInteger,
+  isEmptyArray,
+  isNotEmptyArray,
+  isNotEmptyString,
+  isNullOrUndefined,
+  isUndefined,
+  logger,
+  sleep,
+} from '../../../utils';
 import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService';
 
 const moduleName = 'OCPP16IncomingRequestService';
@@ -416,7 +425,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
       `${chargingStation.logPrefix()} ${
         commandPayload.type
       } reset command received, simulating it. The station will be
-        back online in ${Utils.formatDurationMilliSeconds(chargingStation.stationInfo.resetTime)}`
+        back online in ${formatDurationMilliSeconds(chargingStation.stationInfo.resetTime)}`
     );
     return OCPP16Constants.OCPP_RESPONSE_ACCEPTED;
   }
@@ -463,9 +472,9 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
   ): GetConfigurationResponse {
     const configurationKey: OCPPConfigurationKey[] = [];
     const unknownKey: string[] = [];
-    if (Utils.isUndefined(commandPayload.key) === true) {
+    if (isUndefined(commandPayload.key) === true) {
       for (const configuration of chargingStation.ocppConfiguration.configurationKey) {
-        if (Utils.isUndefined(configuration.visible) === true) {
+        if (isUndefined(configuration.visible) === true) {
           configuration.visible = true;
         }
         if (configuration.visible === false) {
@@ -477,7 +486,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
           value: configuration.value,
         });
       }
-    } else if (Utils.isNotEmptyArray(commandPayload.key) === true) {
+    } else if (isNotEmptyArray(commandPayload.key) === true) {
       for (const key of commandPayload.key) {
         const keyFound = ChargingStationConfigurationUtils.getConfigurationKey(
           chargingStation,
@@ -485,7 +494,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
           true
         );
         if (keyFound) {
-          if (Utils.isUndefined(keyFound.visible) === true) {
+          if (isUndefined(keyFound.visible) === true) {
             keyFound.visible = true;
           }
           if (keyFound.visible === false) {
@@ -636,9 +645,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
       return OCPP16Constants.OCPP_RESPONSE_REJECTED;
     }
     if (
-      Utils.isEmptyArray(
-        chargingStation.getConnectorStatus(commandPayload.connectorId)?.chargingProfiles
-      )
+      isEmptyArray(chargingStation.getConnectorStatus(commandPayload.connectorId)?.chargingProfiles)
     ) {
       return OCPP16Constants.OCPP_RESPONSE_REJECTED;
     }
@@ -685,8 +692,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
       return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
     }
     if (
-      !Utils.isNullOrUndefined(commandPayload.connectorId) &&
-      Utils.isNotEmptyArray(
+      !isNullOrUndefined(commandPayload.connectorId) &&
+      isNotEmptyArray(
         chargingStation.getConnectorStatus(commandPayload.connectorId)?.chargingProfiles
       )
     ) {
@@ -698,10 +705,10 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
       );
       return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED;
     }
-    if (Utils.isNullOrUndefined(commandPayload.connectorId)) {
+    if (isNullOrUndefined(commandPayload.connectorId)) {
       let clearedCP = false;
       const clearChargingProfiles = (connectorStatus: ConnectorStatus) => {
-        if (Utils.isNotEmptyArray(connectorStatus?.chargingProfiles)) {
+        if (isNotEmptyArray(connectorStatus?.chargingProfiles)) {
           connectorStatus?.chargingProfiles?.forEach(
             (chargingProfile: OCPP16ChargingProfile, index: number) => {
               let clearCurrentCP = false;
@@ -881,7 +888,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
         connectorStatus.transactionRemoteStarted = true;
         const startTransactionPayload: Partial<StartTransactionRequest> = {
           connectorId: transactionConnectorId,
-          idTag: idTag,
+          idTag,
         };
         if (reserved || reservedOnConnectorZero) {
           const reservation = chargingStation.getReservationBy(
@@ -1066,7 +1073,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
       return OCPP16Constants.OCPP_RESPONSE_EMPTY;
     }
     if (
-      !Utils.isNullOrUndefined(chargingStation.stationInfo.firmwareStatus) &&
+      !isNullOrUndefined(chargingStation.stationInfo.firmwareStatus) &&
       chargingStation.stationInfo.firmwareStatus !== OCPP16FirmwareStatus.Installed
     ) {
       logger.warn(
@@ -1075,7 +1082,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
       );
       return OCPP16Constants.OCPP_RESPONSE_EMPTY;
     }
-    const retrieveDate = Utils.convertToDate(commandPayload.retrieveDate);
+    const retrieveDate = convertToDate(commandPayload.retrieveDate);
     const now = Date.now();
     if (retrieveDate?.getTime() <= now) {
       this.runInAsyncScope(
@@ -1088,7 +1095,14 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
       ).catch(Constants.EMPTY_FUNCTION);
     } else {
       setTimeout(() => {
-        this.updateFirmwareSimulation(chargingStation).catch(Constants.EMPTY_FUNCTION);
+        this.runInAsyncScope(
+          this.updateFirmwareSimulation.bind(this) as (
+            this: OCPP16IncomingRequestService,
+            ...args: any[]
+          ) => Promise<void>,
+          this,
+          chargingStation
+        ).catch(Constants.EMPTY_FUNCTION);
       }, retrieveDate?.getTime() - now);
     }
     return OCPP16Constants.OCPP_RESPONSE_EMPTY;
@@ -1099,10 +1113,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
     maxDelay = 30,
     minDelay = 15
   ): Promise<void> {
-    if (
-      ChargingStationUtils.checkChargingStation(chargingStation, chargingStation.logPrefix()) ===
-      false
-    ) {
+    if (checkChargingStation(chargingStation, chargingStation.logPrefix()) === false) {
       return;
     }
     if (chargingStation.hasEvses) {
@@ -1144,7 +1155,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
       chargingStation.stationInfo?.firmwareUpgrade?.failureStatus ===
       OCPP16FirmwareStatus.DownloadFailed
     ) {
-      await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
+      await sleep(getRandomInteger(maxDelay, minDelay) * 1000);
       await chargingStation.ocppRequestService.requestHandler<
         OCPP16FirmwareStatusNotificationRequest,
         OCPP16FirmwareStatusNotificationResponse
@@ -1155,7 +1166,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
         chargingStation.stationInfo?.firmwareUpgrade?.failureStatus;
       return;
     }
-    await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
+    await sleep(getRandomInteger(maxDelay, minDelay) * 1000);
     await chargingStation.ocppRequestService.requestHandler<
       OCPP16FirmwareStatusNotificationRequest,
       OCPP16FirmwareStatusNotificationResponse
@@ -1175,7 +1186,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
             waitTime / 1000
           } seconds before continuing firmware update simulation`
         );
-        await Utils.sleep(waitTime);
+        await sleep(waitTime);
         transactionsStarted = true;
         wasTransactionsStarted = true;
       } else {
@@ -1211,12 +1222,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
         transactionsStarted = false;
       }
     } while (transactionsStarted);
-    !wasTransactionsStarted &&
-      (await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000));
-    if (
-      ChargingStationUtils.checkChargingStation(chargingStation, chargingStation.logPrefix()) ===
-      false
-    ) {
+    !wasTransactionsStarted && (await sleep(getRandomInteger(maxDelay, minDelay) * 1000));
+    if (checkChargingStation(chargingStation, chargingStation.logPrefix()) === false) {
       return;
     }
     await chargingStation.ocppRequestService.requestHandler<
@@ -1230,7 +1237,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
       chargingStation.stationInfo?.firmwareUpgrade?.failureStatus ===
       OCPP16FirmwareStatus.InstallationFailed
     ) {
-      await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
+      await sleep(getRandomInteger(maxDelay, minDelay) * 1000);
       await chargingStation.ocppRequestService.requestHandler<
         OCPP16FirmwareStatusNotificationRequest,
         OCPP16FirmwareStatusNotificationResponse
@@ -1242,7 +1249,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
       return;
     }
     if (chargingStation.stationInfo?.firmwareUpgrade?.reset === true) {
-      await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
+      await sleep(getRandomInteger(maxDelay, minDelay) * 1000);
       await chargingStation.reset(OCPP16StopTransactionReason.REBOOT);
     }
   }
@@ -1268,18 +1275,17 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
     if (uri.protocol.startsWith('ftp:')) {
       let ftpClient: Client;
       try {
-        const logFiles = fs
-          .readdirSync(path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../'))
+        const logFiles = readdirSync(resolve(dirname(fileURLToPath(import.meta.url)), '../'))
           .filter((file) => file.endsWith('.log'))
-          .map((file) => path.join('./', file));
+          .map((file) => join('./', file));
         const diagnosticsArchive = `${chargingStation.stationInfo.chargingStationId}_logs.tar.gz`;
-        tar.create({ gzip: true }, logFiles).pipe(fs.createWriteStream(diagnosticsArchive));
+        create({ gzip: true }, logFiles).pipe(createWriteStream(diagnosticsArchive));
         ftpClient = new Client();
         const accessResponse = await ftpClient.access({
           host: uri.host,
-          ...(Utils.isNotEmptyString(uri.port) && { port: Utils.convertToInt(uri.port) }),
-          ...(Utils.isNotEmptyString(uri.username) && { user: uri.username }),
-          ...(Utils.isNotEmptyString(uri.password) && { password: uri.password }),
+          ...(isNotEmptyString(uri.port) && { port: convertToInt(uri.port) }),
+          ...(isNotEmptyString(uri.username) && { user: uri.username }),
+          ...(isNotEmptyString(uri.password) && { password: uri.password }),
         });
         let uploadResponse: FTPResponse;
         if (accessResponse.code === 220) {
@@ -1305,10 +1311,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
               });
           });
           uploadResponse = await ftpClient.uploadFrom(
-            path.join(
-              path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../'),
-              diagnosticsArchive
-            ),
+            join(resolve(dirname(fileURLToPath(import.meta.url)), '../'), diagnosticsArchive),
             `${uri.pathname}${diagnosticsArchive}`
           );
           if (uploadResponse.code === 226) {
@@ -1430,7 +1433,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
           return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
         case OCPP16MessageTrigger.StatusNotification:
           setTimeout(() => {
-            if (!Utils.isNullOrUndefined(commandPayload?.connectorId)) {
+            if (!isNullOrUndefined(commandPayload?.connectorId)) {
               chargingStation.ocppRequestService
                 .requestHandler<OCPP16StatusNotificationRequest, OCPP16StatusNotificationResponse>(
                   chargingStation,
@@ -1512,13 +1515,9 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
   ): OCPP16DataTransferResponse {
     try {
       if (Object.values(OCPP16DataTransferVendorId).includes(commandPayload.vendorId)) {
-        return {
-          status: OCPP16DataTransferStatus.ACCEPTED,
-        };
+        return OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_ACCEPTED;
       }
-      return {
-        status: OCPP16DataTransferStatus.UNKNOWN_VENDOR_ID,
-      };
+      return OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_UNKNOWN_VENDOR_ID;
     } catch (error) {
       return this.handleIncomingRequestError(
         chargingStation,
@@ -1540,56 +1539,60 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
         OCPP16IncomingRequestCommand.RESERVE_NOW
       )
     ) {
-      return OCPPConstants.OCPP_RESERVATION_RESPONSE_REJECTED;
+      return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED;
     }
     const { reservationId, idTag, connectorId } = commandPayload;
     let response: OCPP16ReserveNowResponse;
     try {
       if (!chargingStation.isConnectorAvailable(connectorId) && connectorId > 0) {
-        return OCPPConstants.OCPP_RESERVATION_RESPONSE_REJECTED;
+        return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED;
       }
       if (connectorId === 0 && !chargingStation.getReservationOnConnectorId0Enabled()) {
-        return OCPPConstants.OCPP_RESERVATION_RESPONSE_REJECTED;
+        return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED;
       }
       if (!(await OCPP16ServiceUtils.isIdTagAuthorized(chargingStation, connectorId, idTag))) {
-        return OCPPConstants.OCPP_RESERVATION_RESPONSE_REJECTED;
+        return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED;
       }
       switch (chargingStation.getConnectorStatus(connectorId).status) {
-        case ConnectorStatusEnum.Faulted:
-          response = OCPPConstants.OCPP_RESERVATION_RESPONSE_FAULTED;
+        case OCPP16ChargePointStatus.Faulted:
+          response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_FAULTED;
           break;
-        case ConnectorStatusEnum.Occupied:
-          response = OCPPConstants.OCPP_RESERVATION_RESPONSE_OCCUPIED;
+        case OCPP16ChargePointStatus.Preparing:
+        case OCPP16ChargePointStatus.Charging:
+        case OCPP16ChargePointStatus.SuspendedEV:
+        case OCPP16ChargePointStatus.SuspendedEVSE:
+        case OCPP16ChargePointStatus.Finishing:
+          response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED;
           break;
-        case ConnectorStatusEnum.Unavailable:
-          response = OCPPConstants.OCPP_RESERVATION_RESPONSE_UNAVAILABLE;
+        case OCPP16ChargePointStatus.Unavailable:
+          response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_UNAVAILABLE;
           break;
-        case ConnectorStatusEnum.Reserved:
+        case OCPP16ChargePointStatus.Reserved:
           if (!chargingStation.isConnectorReservable(reservationId, idTag, connectorId)) {
-            response = OCPPConstants.OCPP_RESERVATION_RESPONSE_OCCUPIED;
+            response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED;
             break;
           }
         // eslint-disable-next-line no-fallthrough
         default:
           if (!chargingStation.isConnectorReservable(reservationId, idTag)) {
-            response = OCPPConstants.OCPP_RESERVATION_RESPONSE_OCCUPIED;
+            response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED;
             break;
           }
           await chargingStation.addReservation({
             id: commandPayload.reservationId,
             ...commandPayload,
           });
-          response = OCPPConstants.OCPP_RESERVATION_RESPONSE_ACCEPTED;
+          response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_ACCEPTED;
           break;
       }
       return response;
     } catch (error) {
-      chargingStation.getConnectorStatus(connectorId).status = ConnectorStatusEnum.Available;
+      chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.Available;
       return this.handleIncomingRequestError(
         chargingStation,
         OCPP16IncomingRequestCommand.RESERVE_NOW,
         error as Error,
-        { errorResponse: OCPPConstants.OCPP_RESERVATION_RESPONSE_FAULTED }
+        { errorResponse: OCPP16Constants.OCPP_RESERVATION_RESPONSE_FAULTED }
       );
     }
   }
@@ -1597,7 +1600,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
   private async handleRequestCancelReservation(
     chargingStation: ChargingStation,
     commandPayload: OCPP16CancelReservationRequest
-  ): Promise<OCPP16CancelReservationResponse> {
+  ): Promise<GenericResponse> {
     if (
       !OCPP16ServiceUtils.checkFeatureProfile(
         chargingStation,
@@ -1605,7 +1608,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
         OCPP16IncomingRequestCommand.CANCEL_RESERVATION
       )
     ) {
-      return OCPPConstants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED;
+      return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED;
     }
     try {
       const { reservationId } = commandPayload;
@@ -1615,16 +1618,19 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
           `${chargingStation.logPrefix()} Reservation with ID ${reservationId}
             does not exist on charging station`
         );
-        return OCPPConstants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED;
+        return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED;
       }
-      await chargingStation.removeReservation(reservation);
-      return OCPPConstants.OCPP_CANCEL_RESERVATION_RESPONSE_ACCEPTED;
+      await chargingStation.removeReservation(
+        reservation,
+        ReservationTerminationReason.RESERVATION_CANCELED
+      );
+      return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_ACCEPTED;
     } catch (error) {
       return this.handleIncomingRequestError(
         chargingStation,
         OCPP16IncomingRequestCommand.CANCEL_RESERVATION,
         error as Error,
-        { errorResponse: OCPPConstants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED }
+        { errorResponse: OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED }
       );
     }
   }