Add return type to methods
[e-mobility-charging-stations-simulator.git] / src / charging-station / ChargingStation.ts
index 25d9733b0e67063939d7174bae430e39bb058841..a47bc68182a77de166b1dc9fd273a7d31d59265b 100644 (file)
@@ -11,19 +11,19 @@ import WebSocket, { Data, RawData } from 'ws';
 import BaseError from '../exception/BaseError';
 import OCPPError from '../exception/OCPPError';
 import PerformanceStatistics from '../performance/PerformanceStatistics';
-import { AutomaticTransactionGeneratorConfiguration } from '../types/AutomaticTransactionGenerator';
-import ChargingStationConfiguration from '../types/ChargingStationConfiguration';
-import ChargingStationInfo from '../types/ChargingStationInfo';
-import ChargingStationOcppConfiguration from '../types/ChargingStationOcppConfiguration';
+import type { AutomaticTransactionGeneratorConfiguration } from '../types/AutomaticTransactionGenerator';
+import type ChargingStationConfiguration from '../types/ChargingStationConfiguration';
+import type ChargingStationInfo from '../types/ChargingStationInfo';
+import type ChargingStationOcppConfiguration from '../types/ChargingStationOcppConfiguration';
 import ChargingStationTemplate, {
   CurrentType,
   PowerUnits,
   WsOptions,
 } from '../types/ChargingStationTemplate';
 import { SupervisionUrlDistribution } from '../types/ConfigurationData';
-import { ConnectorStatus } from '../types/ConnectorStatus';
+import type { ConnectorStatus } from '../types/ConnectorStatus';
 import { FileType } from '../types/FileType';
-import { JsonType } from '../types/JsonType';
+import type { JsonType } from '../types/JsonType';
 import { ChargePointErrorCode } from '../types/ocpp/ChargePointErrorCode';
 import { ChargePointStatus } from '../types/ocpp/ChargePointStatus';
 import { ChargingProfile, ChargingRateUnitType } from '../types/ocpp/ChargingProfile';
@@ -79,8 +79,8 @@ import OCPP16IncomingRequestService from './ocpp/1.6/OCPP16IncomingRequestServic
 import OCPP16RequestService from './ocpp/1.6/OCPP16RequestService';
 import OCPP16ResponseService from './ocpp/1.6/OCPP16ResponseService';
 import { OCPP16ServiceUtils } from './ocpp/1.6/OCPP16ServiceUtils';
-import OCPPIncomingRequestService from './ocpp/OCPPIncomingRequestService';
-import OCPPRequestService from './ocpp/OCPPRequestService';
+import type OCPPIncomingRequestService from './ocpp/OCPPIncomingRequestService';
+import type OCPPRequestService from './ocpp/OCPPRequestService';
 import SharedLRUCache from './SharedLRUCache';
 
 export default class ChargingStation {
@@ -96,12 +96,12 @@ export default class ChargingStation {
   public performanceStatistics!: PerformanceStatistics;
   public heartbeatSetInterval!: NodeJS.Timeout;
   public ocppRequestService!: OCPPRequestService;
+  public bootNotificationRequest!: BootNotificationRequest;
   public bootNotificationResponse!: BootNotificationResponse | null;
   public powerDivider!: number;
   private readonly index: number;
   private configurationFile!: string;
   private configurationFileHash!: string;
-  private bootNotificationRequest!: BootNotificationRequest;
   private connectorsConfigurationHash!: string;
   private ocppIncomingRequestService!: OCPPIncomingRequestService;
   private readonly messageBuffer: Set<string>;
@@ -152,10 +152,6 @@ export default class ChargingStation {
     );
   }
 
-  public getBootNotificationRequest(): BootNotificationRequest {
-    return this.bootNotificationRequest;
-  }
-
   public getRandomIdTag(): string {
     const authorizationFile = ChargingStationUtils.getAuthorizationFile(this.stationInfo);
     const index = Math.floor(
@@ -178,8 +174,8 @@ export default class ChargingStation {
       : true;
   }
 
-  public getMayAuthorizeAtRemoteStart(): boolean | undefined {
-    return this.stationInfo.mayAuthorizeAtRemoteStart ?? true;
+  public getMustAuthorizeAtRemoteStart(): boolean | undefined {
+    return this.stationInfo.mustAuthorizeAtRemoteStart ?? true;
   }
 
   public getPayloadSchemaValidation(): boolean | undefined {
@@ -507,7 +503,9 @@ export default class ChargingStation {
             this.initialize();
             // Restart the ATG
             this.stopAutomaticTransactionGenerator();
-            this.startAutomaticTransactionGenerator();
+            if (this.getAutomaticTransactionGeneratorConfigurationFromTemplate()?.enable) {
+              this.startAutomaticTransactionGenerator();
+            }
             if (this.getEnableStatistics()) {
               this.performanceStatistics.restart();
             } else {
@@ -550,8 +548,8 @@ export default class ChargingStation {
     this.templateFileWatcher.close();
     this.sharedLRUCache.deleteChargingStationTemplate(this.stationInfo?.templateHash);
     this.bootNotificationResponse = null;
-    parentPort.postMessage(MessageChannelUtils.buildStoppedMessage(this));
     this.stopped = true;
+    parentPort.postMessage(MessageChannelUtils.buildStoppedMessage(this));
   }
 
   public async reset(reason?: StopTransactionReason): Promise<void> {
@@ -583,7 +581,7 @@ export default class ChargingStation {
     if (!Utils.isEmptyArray(chargingProfiles)) {
       const result = ChargingStationUtils.getLimitFromChargingProfiles(
         chargingProfiles,
-        Utils.logPrefix()
+        this.logPrefix()
       );
       if (!Utils.isNullOrUndefined(result)) {
         limit = result.limit;
@@ -626,7 +624,7 @@ export default class ChargingStation {
       );
       this.getConnectorStatus(connectorId).chargingProfiles = [];
     }
-    if (!Array.isArray(this.getConnectorStatus(connectorId).chargingProfiles)) {
+    if (Array.isArray(this.getConnectorStatus(connectorId).chargingProfiles) === false) {
       logger.error(
         `${this.logPrefix()} Trying to set a charging profile on connectorId ${connectorId} with an improper attribute type for the charging profiles array, applying proper type initialization`
       );
@@ -707,8 +705,15 @@ export default class ChargingStation {
         break;
     }
 
+    if (this.isWebSocketConnectionOpened()) {
+      logger.warn(
+        `${this.logPrefix()} OCPP connection to URL ${this.wsConnectionUrl.toString()} is already opened`
+      );
+      return;
+    }
+
     logger.info(
-      this.logPrefix() + ' Open OCPP connection to URL ' + this.wsConnectionUrl.toString()
+      `${this.logPrefix()} Open OCPP connection to URL ${this.wsConnectionUrl.toString()}`
     );
 
     this.wsConnection = new WebSocket(this.wsConnectionUrl, protocol, options);
@@ -743,7 +748,26 @@ export default class ChargingStation {
     }
   }
 
-  private flushMessageBuffer() {
+  public startAutomaticTransactionGenerator(): void {
+    if (!this.automaticTransactionGenerator) {
+      this.automaticTransactionGenerator = AutomaticTransactionGenerator.getInstance(
+        this.getAutomaticTransactionGeneratorConfigurationFromTemplate(),
+        this
+      );
+    }
+    if (!this.automaticTransactionGenerator.started) {
+      this.automaticTransactionGenerator.start();
+    }
+  }
+
+  public stopAutomaticTransactionGenerator(): void {
+    if (this.automaticTransactionGenerator?.started) {
+      this.automaticTransactionGenerator.stop();
+      this.automaticTransactionGenerator = null;
+    }
+  }
+
+  private flushMessageBuffer(): void {
     if (this.messageBuffer.size > 0) {
       this.messageBuffer.forEach((message) => {
         // TODO: evaluate the need to track performance
@@ -846,14 +870,14 @@ export default class ChargingStation {
     ChargingStationUtils.checkConfiguredMaxConnectors(
       configuredMaxConnectors,
       this.templateFile,
-      Utils.logPrefix()
+      this.logPrefix()
     );
     const templateMaxConnectors =
       ChargingStationUtils.getTemplateMaxNumberOfConnectors(stationTemplate);
     ChargingStationUtils.checkTemplateMaxConnectors(
       templateMaxConnectors,
       this.templateFile,
-      Utils.logPrefix()
+      this.logPrefix()
     );
     if (
       configuredMaxConnectors >
@@ -920,10 +944,8 @@ export default class ChargingStation {
   }
 
   private handleUnsupportedVersion(version: OCPPVersion) {
-    const errMsg = `${this.logPrefix()} Unsupported protocol version '${version}' configured in template file ${
-      this.templateFile
-    }`;
-    logger.error(errMsg);
+    const errMsg = `Unsupported protocol version '${version}' configured in template file ${this.templateFile}`;
+    logger.error(`${this.logPrefix()} ${errMsg}`);
     throw new BaseError(errMsg);
   }
 
@@ -1142,10 +1164,8 @@ export default class ChargingStation {
     templateMaxConnectors: number
   ): void {
     if (!stationInfo?.Connectors && this.connectors.size === 0) {
-      const logMsg = `${this.logPrefix()} No already defined connectors and charging station information from template ${
-        this.templateFile
-      } with no connectors configuration defined`;
-      logger.error(logMsg);
+      const logMsg = `No already defined connectors and charging station information from template ${this.templateFile} with no connectors configuration defined`;
+      logger.error(`${this.logPrefix()} ${logMsg}`);
       throw new BaseError(logMsg);
     }
     if (!stationInfo?.Connectors[0]) {
@@ -1419,7 +1439,7 @@ export default class ChargingStation {
     let errMsg: string;
     try {
       const request = JSON.parse(data.toString()) as IncomingRequest | Response | ErrorResponse;
-      if (Utils.isIterable(request)) {
+      if (Array.isArray(request) === true) {
         [messageType, messageId] = request;
         // Check the type of message
         switch (messageType) {
@@ -1456,12 +1476,12 @@ export default class ChargingStation {
             }
             // Respond
             cachedRequest = this.requests.get(messageId);
-            if (Utils.isIterable(cachedRequest)) {
+            if (Array.isArray(cachedRequest) === true) {
               [responseCallback, , requestCommandName, requestPayload] = cachedRequest;
             } else {
               throw new OCPPError(
                 ErrorType.PROTOCOL_ERROR,
-                `Cached request for message id ${messageId} response is not iterable`,
+                `Cached request for message id ${messageId} response is not an array`,
                 null,
                 cachedRequest as unknown as JsonType
               );
@@ -1486,12 +1506,12 @@ export default class ChargingStation {
               );
             }
             cachedRequest = this.requests.get(messageId);
-            if (Utils.isIterable(cachedRequest)) {
+            if (Array.isArray(cachedRequest) === true) {
               [, errorCallback, requestCommandName] = cachedRequest;
             } else {
               throw new OCPPError(
                 ErrorType.PROTOCOL_ERROR,
-                `Cached request for message id ${messageId} error response is not iterable`,
+                `Cached request for message id ${messageId} error response is not an array`,
                 null,
                 cachedRequest as unknown as JsonType
               );
@@ -1506,32 +1526,31 @@ export default class ChargingStation {
           // Error
           default:
             // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
-            errMsg = `${this.logPrefix()} Wrong message type ${messageType}`;
-            logger.error(errMsg);
+            errMsg = `Wrong message type ${messageType}`;
+            logger.error(`${this.logPrefix()} ${errMsg}`);
             throw new OCPPError(ErrorType.PROTOCOL_ERROR, errMsg);
         }
         parentPort.postMessage(MessageChannelUtils.buildUpdatedMessage(this));
       } else {
-        throw new OCPPError(ErrorType.PROTOCOL_ERROR, 'Incoming message is not iterable', null, {
+        throw new OCPPError(ErrorType.PROTOCOL_ERROR, 'Incoming message is not an array', null, {
           payload: request,
         });
       }
     } catch (error) {
       // Log
       logger.error(
-        "%s Incoming OCPP '%s' message '%j' matching cached request '%j' processing error:",
-        this.logPrefix(),
-        commandName ?? requestCommandName ?? null,
-        data.toString(),
-        this.requests.get(messageId),
+        `${this.logPrefix()} Incoming OCPP command '${
+          commandName ?? requestCommandName ?? null
+        }' message '${data.toString()}' matching cached request '${JSON.stringify(
+          this.requests.get(messageId)
+        )}' processing error:`,
         error
       );
       if (!(error instanceof OCPPError)) {
         logger.warn(
-          "%s Error thrown at incoming OCPP '%s' message '%j' handling is not an OCPPError:",
-          this.logPrefix(),
-          commandName ?? requestCommandName ?? null,
-          data.toString(),
+          `${this.logPrefix()} Error thrown at incoming OCPP command '${
+            commandName ?? requestCommandName ?? null
+          }' message '${data.toString()}' handling is not an OCPPError:`,
           error
         );
       }
@@ -1747,27 +1766,8 @@ export default class ChargingStation {
       }
     }
     // Start the ATG
-    this.startAutomaticTransactionGenerator();
-  }
-
-  private startAutomaticTransactionGenerator() {
     if (this.getAutomaticTransactionGeneratorConfigurationFromTemplate()?.enable) {
-      if (!this.automaticTransactionGenerator) {
-        this.automaticTransactionGenerator = AutomaticTransactionGenerator.getInstance(
-          this.getAutomaticTransactionGeneratorConfigurationFromTemplate(),
-          this
-        );
-      }
-      if (!this.automaticTransactionGenerator.started) {
-        this.automaticTransactionGenerator.start();
-      }
-    }
-  }
-
-  private stopAutomaticTransactionGenerator(): void {
-    if (this.automaticTransactionGenerator?.started) {
-      this.automaticTransactionGenerator.stop();
-      this.automaticTransactionGenerator = null;
+      this.startAutomaticTransactionGenerator();
     }
   }
 
@@ -1779,42 +1779,44 @@ export default class ChargingStation {
     // Stop heartbeat
     this.stopHeartbeat();
     // Stop ongoing transactions
-    if (this.automaticTransactionGenerator?.configuration?.enable) {
-      this.stopAutomaticTransactionGenerator();
-    } else {
-      for (const connectorId of this.connectors.keys()) {
-        if (connectorId > 0 && this.getConnectorStatus(connectorId)?.transactionStarted) {
-          const transactionId = this.getConnectorStatus(connectorId).transactionId;
-          if (
-            this.getBeginEndMeterValues() &&
-            this.getOcppStrictCompliance() &&
-            !this.getOutOfOrderEndMeterValues()
-          ) {
-            // FIXME: Implement OCPP version agnostic helpers
-            const transactionEndMeterValue = OCPP16ServiceUtils.buildTransactionEndMeterValue(
-              this,
-              connectorId,
-              this.getEnergyActiveImportRegisterByTransactionId(transactionId)
-            );
-            await this.ocppRequestService.requestHandler<MeterValuesRequest, MeterValuesResponse>(
-              this,
-              RequestCommand.METER_VALUES,
-              {
+    if (this.getNumberOfRunningTransactions() > 0) {
+      if (this.automaticTransactionGenerator?.started) {
+        this.stopAutomaticTransactionGenerator();
+      } else {
+        for (const connectorId of this.connectors.keys()) {
+          if (connectorId > 0 && this.getConnectorStatus(connectorId)?.transactionStarted) {
+            const transactionId = this.getConnectorStatus(connectorId).transactionId;
+            if (
+              this.getBeginEndMeterValues() &&
+              this.getOcppStrictCompliance() &&
+              !this.getOutOfOrderEndMeterValues()
+            ) {
+              // FIXME: Implement OCPP version agnostic helpers
+              const transactionEndMeterValue = OCPP16ServiceUtils.buildTransactionEndMeterValue(
+                this,
                 connectorId,
-                transactionId,
-                meterValue: [transactionEndMeterValue],
-              }
-            );
+                this.getEnergyActiveImportRegisterByTransactionId(transactionId)
+              );
+              await this.ocppRequestService.requestHandler<MeterValuesRequest, MeterValuesResponse>(
+                this,
+                RequestCommand.METER_VALUES,
+                {
+                  connectorId,
+                  transactionId,
+                  meterValue: [transactionEndMeterValue],
+                }
+              );
+            }
+            await this.ocppRequestService.requestHandler<
+              StopTransactionRequest,
+              StopTransactionResponse
+            >(this, RequestCommand.STOP_TRANSACTION, {
+              transactionId,
+              meterStop: this.getEnergyActiveImportRegisterByTransactionId(transactionId),
+              idTag: this.getTransactionIdTag(transactionId),
+              reason,
+            });
           }
-          await this.ocppRequestService.requestHandler<
-            StopTransactionRequest,
-            StopTransactionResponse
-          >(this, RequestCommand.STOP_TRANSACTION, {
-            transactionId,
-            meterStop: this.getEnergyActiveImportRegisterByTransactionId(transactionId),
-            idTag: this.getTransactionIdTag(transactionId),
-            reason,
-          });
         }
       }
     }