fix: make ATG wait for CS/connector availability
authorJérôme Benoit <jerome.benoit@sap.com>
Sat, 11 Nov 2023 19:05:06 +0000 (20:05 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Sat, 11 Nov 2023 19:05:06 +0000 (20:05 +0100)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
src/charging-station/AutomaticTransactionGenerator.ts
src/charging-station/ChargingStation.ts
src/charging-station/ocpp/OCPPServiceUtils.ts
src/types/ChargingStation.ts
src/utils/Constants.ts

index f390d55fe20554291d068ba6f848963f0845cb9f..ebfd8c196fd026b0ca673bba3cb5a38bff8a3e9d 100644 (file)
@@ -189,20 +189,13 @@ export class AutomaticTransactionGenerator extends AsyncResource {
       )}`,
     );
     while (this.connectorsStatus.get(connectorId)?.start === true) {
+      await this.waitChargingStationServiceInitialization(connectorId);
+      await this.waitChargingStationAvailable(connectorId);
+      await this.waitConnectorAvailable(connectorId);
       if (!this.canStartConnector(connectorId)) {
         this.stopConnector(connectorId);
         break;
       }
-      if (!this.chargingStation?.ocppRequestService) {
-        logger.info(
-          `${this.logPrefix(
-            connectorId,
-          )} transaction loop waiting for charging station service to be initialized`,
-        );
-        do {
-          await sleep(Constants.CHARGING_STATION_ATG_INITIALIZATION_TIME);
-        } while (!this.chargingStation?.ocppRequestService);
-      }
       const wait = secondsToMilliseconds(
         getRandomInteger(
           this.chargingStation.getAutomaticTransactionGeneratorConfiguration()
@@ -326,6 +319,45 @@ export class AutomaticTransactionGenerator extends AsyncResource {
     return true;
   }
 
+  private async waitChargingStationServiceInitialization(connectorId: number): Promise<void> {
+    if (!this.chargingStation?.ocppRequestService) {
+      logger.info(
+        `${this.logPrefix(
+          connectorId,
+        )} transaction loop waiting for charging station service to be initialized`,
+      );
+      do {
+        await sleep(Constants.CHARGING_STATION_ATG_INITIALIZATION_TIME);
+      } while (!this.chargingStation?.ocppRequestService);
+    }
+  }
+
+  private async waitChargingStationAvailable(connectorId: number): Promise<void> {
+    if (!this.chargingStation.isChargingStationAvailable()) {
+      logger.info(
+        `${this.logPrefix(
+          connectorId,
+        )} transaction loop waiting for charging station to be available`,
+      );
+      do {
+        await sleep(Constants.CHARGING_STATION_ATG_AVAILABILITY_TIME);
+      } while (!this.chargingStation.isChargingStationAvailable());
+    }
+  }
+
+  private async waitConnectorAvailable(connectorId: number): Promise<void> {
+    if (!this.chargingStation.isConnectorAvailable(connectorId)) {
+      logger.info(
+        `${this.logPrefix(
+          connectorId,
+        )} transaction loop waiting for connector ${connectorId} to be available`,
+      );
+      do {
+        await sleep(Constants.CHARGING_STATION_ATG_AVAILABILITY_TIME);
+      } while (!this.chargingStation.isConnectorAvailable(connectorId));
+    }
+  }
+
   private initializeConnectorsStatus(): void {
     if (this.chargingStation.hasEvses) {
       for (const [evseId, evseStatus] of this.chargingStation.evses) {
index cd1ec4268d27fc7a3f799a7c38e6f76439dc74df..8cdde246cebb00a52d296ef0587a266f4d3eb5a7 100644 (file)
@@ -513,7 +513,7 @@ export class ChargingStation extends EventEmitter {
 
   public setSupervisionUrl(url: string): void {
     if (
-      this.stationInfo?.supervisionUrlOcppConfiguration &&
+      this.stationInfo?.supervisionUrlOcppConfiguration === true &&
       isNotEmptyString(this.stationInfo?.supervisionUrlOcppKey)
     ) {
       setConfigurationKeyValue(this, this.stationInfo.supervisionUrlOcppKey!, url);
@@ -1192,7 +1192,7 @@ export class ChargingStation extends EventEmitter {
     }
   }
 
-  private handleUnsupportedVersion(version: OCPPVersion) {
+  private handleUnsupportedVersion(version: OCPPVersion | undefined) {
     const errorMsg = `Unsupported protocol version '${version}' configured in template file ${this.templateFile}`;
     logger.error(`${this.logPrefix()} ${errorMsg}`);
     throw new BaseError(errorMsg);
@@ -1249,6 +1249,11 @@ export class ChargingStation extends EventEmitter {
     this.ocppConfiguration = this.getOcppConfiguration();
     this.initializeOcppConfiguration();
     this.initializeOcppServices();
+    this.once(ChargingStationEvents.accepted, () => {
+      this.startMessageSequence().catch((error) => {
+        logger.error(`${this.logPrefix()} Error while starting the message sequence:`, error);
+      });
+    });
     if (this.stationInfo?.autoRegister === true) {
       this.bootNotificationResponse = {
         currentTime: new Date(),
@@ -1763,7 +1768,6 @@ export class ChargingStation extends EventEmitter {
         this.emit(ChargingStationEvents.registered);
         if (this.inAcceptedState() === true) {
           this.emit(ChargingStationEvents.accepted);
-          await this.startMessageSequence();
         }
       } else {
         logger.error(
@@ -2152,12 +2156,12 @@ export class ChargingStation extends EventEmitter {
     this.stopWebSocketPing();
     // Stop heartbeat
     this.stopHeartbeat();
-    // Stop ongoing transactions
-    stopTransactions && (await this.stopRunningTransactions(reason));
     // Stop the ATG
     if (this.automaticTransactionGenerator?.started === true) {
       this.stopAutomaticTransactionGenerator();
     }
+    // Stop ongoing transactions
+    stopTransactions && (await this.stopRunningTransactions(reason));
     if (this.hasEvses) {
       for (const [evseId, evseStatus] of this.evses) {
         if (evseId > 0) {
index ee4134bb0a192f8db9f27075557f1e176ad4d58c..7da384d3a22e802928e10aeebd8208ff12e4792d 100644 (file)
@@ -15,6 +15,7 @@ import {
   type AuthorizeRequest,
   type AuthorizeResponse,
   ChargePointErrorCode,
+  ChargingStationEvents,
   type ConnectorStatus,
   type ConnectorStatusEnum,
   ErrorType,
@@ -226,6 +227,10 @@ export class OCPPServiceUtils {
       );
     }
     chargingStation.getConnectorStatus(connectorId)!.status = status;
+    chargingStation.emit(ChargingStationEvents.connectorStatusChanged, {
+      connectorId,
+      ...chargingStation.getConnectorStatus(connectorId),
+    });
   }
 
   public static async isIdTagAuthorized(
index cdd3a5e048841912590bb002a43e49e071574338..f7980b47b3f7e5a77473473383be465fc0689cd1 100644 (file)
@@ -4,4 +4,5 @@ export enum ChargingStationEvents {
   registered = 'registered',
   accepted = 'accepted',
   updated = 'updated',
+  connectorStatusChanged = 'connectorStatusChanged',
 }
index 93c5bd9f412cf7b1e82b35aa543ce1bcf3144ee0..2a184016371efa766b3d633051a1a17c9ff951d2 100644 (file)
@@ -6,6 +6,7 @@ export class Constants {
   static readonly DEFAULT_METER_VALUES_INTERVAL = 60000; // Ms
 
   static readonly CHARGING_STATION_DEFAULT_RESET_TIME = 60000; // Ms
+  static readonly CHARGING_STATION_ATG_AVAILABILITY_TIME = 1000; // Ms
   static readonly CHARGING_STATION_ATG_INITIALIZATION_TIME = 1000; // Ms
 
   static readonly DEFAULT_ATG_CONFIGURATION: AutomaticTransactionGeneratorConfiguration =