UI services: fix connectors status update at transaction start/stop
[e-mobility-charging-stations-simulator.git] / src / charging-station / ChargingStation.ts
index 89fffc3bffc03b3399837d211a9d8d31ea5411c3..7d633eb07b97940002922cbefae236dad8e3e39a 100644 (file)
@@ -86,7 +86,7 @@ import SharedLRUCache from './SharedLRUCache';
 export default class ChargingStation {
   public readonly templateFile: string;
   public stationInfo!: ChargingStationInfo;
-  public stopped: boolean;
+  public started: boolean;
   public authorizedTagsCache: AuthorizedTagsCache;
   public automaticTransactionGenerator!: AutomaticTransactionGenerator;
   public ocppConfiguration!: ChargingStationOcppConfiguration;
@@ -122,7 +122,7 @@ export default class ChargingStation {
     this.sharedLRUCache = SharedLRUCache.getInstance();
     this.authorizedTagsCache = AuthorizedTagsCache.getInstance();
     this.chargingStationWorkerBroadcastChannel = new ChargingStationWorkerBroadcastChannel(this);
-    this.stopped = false;
+    this.started = false;
     this.wsConnectionRestarted = false;
     this.autoReconnectRetryCount = 0;
 
@@ -233,7 +233,7 @@ export default class ChargingStation {
     return this.connectors.get(0) ? this.connectors.size - 1 : this.connectors.size;
   }
 
-  public getConnectorStatus(id: number): ConnectorStatus {
+  public getConnectorStatus(id: number): ConnectorStatus | undefined {
     return this.connectors.get(id);
   }
 
@@ -425,13 +425,13 @@ export default class ChargingStation {
       );
       return;
     }
-    if (!this.getConnectorStatus(connectorId)?.transactionStarted) {
+    if (this.getConnectorStatus(connectorId)?.transactionStarted === false) {
       logger.error(
         `${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId} with no transaction started`
       );
       return;
     } else if (
-      this.getConnectorStatus(connectorId)?.transactionStarted &&
+      this.getConnectorStatus(connectorId)?.transactionStarted === true &&
       !this.getConnectorStatus(connectorId)?.transactionId
     ) {
       logger.error(
@@ -519,9 +519,7 @@ export default class ChargingStation {
     parentPort.postMessage(MessageChannelUtils.buildStartedMessage(this));
   }
 
-  public async stop(reason: StopTransactionReason = StopTransactionReason.NONE): Promise<void> {
-    // Stop message sequence
-    await this.stopMessageSequence(reason);
+  public async stop(): Promise<void> {
     for (const connectorId of this.connectors.keys()) {
       if (connectorId > 0) {
         await this.ocppRequestService.requestHandler<
@@ -543,12 +541,12 @@ export default class ChargingStation {
     this.templateFileWatcher.close();
     this.sharedLRUCache.deleteChargingStationTemplate(this.stationInfo?.templateHash);
     this.bootNotificationResponse = null;
-    this.stopped = true;
+    this.started = false;
     parentPort.postMessage(MessageChannelUtils.buildStoppedMessage(this));
   }
 
-  public async reset(reason?: StopTransactionReason): Promise<void> {
-    await this.stop(reason);
+  public async reset(): Promise<void> {
+    await this.stop();
     await Utils.sleep(this.stationInfo.resetTime);
     this.initialize();
     this.start();
@@ -596,7 +594,6 @@ export default class ChargingStation {
                 ? limit
                 : DCElectricUtils.power(this.getVoltageOut(), limit);
         }
-
         const connectorMaximumPower = this.getMaximumPower() / this.powerDivider;
         if (limit > connectorMaximumPower) {
           logger.error(
@@ -655,6 +652,7 @@ export default class ChargingStation {
     this.getConnectorStatus(connectorId).transactionEnergyActiveImportRegisterValue = 0;
     delete this.getConnectorStatus(connectorId).transactionBeginMeterValue;
     this.stopMeterValues(connectorId);
+    parentPort.postMessage(MessageChannelUtils.buildUpdatedMessage(this));
   }
 
   public hasFeatureProfile(featureProfile: SupportedFeatureProfiles) {
@@ -770,6 +768,62 @@ export default class ChargingStation {
     }
   }
 
+  public getNumberOfRunningTransactions(): number {
+    let trxCount = 0;
+    for (const connectorId of this.connectors.keys()) {
+      if (connectorId > 0 && this.getConnectorStatus(connectorId)?.transactionStarted === true) {
+        trxCount++;
+      }
+    }
+    return trxCount;
+  }
+
+  public async stopRunningTransactions(reason = StopTransactionReason.NONE): Promise<void> {
+    for (const connectorId of this.connectors.keys()) {
+      if (connectorId > 0 && this.getConnectorStatus(connectorId)?.transactionStarted === true) {
+        await this.stopTransactionOnConnector(connectorId, reason);
+      }
+    }
+  }
+
+  public async stopTransactionOnConnector(
+    connectorId: number,
+    reason = StopTransactionReason.NONE
+  ): Promise<StopTransactionResponse> {
+    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,
+        {
+          connectorId,
+          transactionId,
+          meterValue: [transactionEndMeterValue],
+        }
+      );
+    }
+    return this.ocppRequestService.requestHandler<StopTransactionRequest, StopTransactionResponse>(
+      this,
+      RequestCommand.STOP_TRANSACTION,
+      {
+        transactionId,
+        meterStop: this.getEnergyActiveImportRegisterByTransactionId(transactionId, true),
+        idTag: this.getTransactionIdTag(transactionId),
+        reason,
+      }
+    );
+  }
+
   private flushMessageBuffer(): void {
     if (this.messageBuffer.size > 0) {
       this.messageBuffer.forEach((message) => {
@@ -1233,7 +1287,7 @@ export default class ChargingStation {
     }
     // Initialize transaction attributes on connectors
     for (const connectorId of this.connectors.keys()) {
-      if (connectorId > 0 && !this.getConnectorStatus(connectorId)?.transactionStarted) {
+      if (connectorId > 0 && this.getConnectorStatus(connectorId)?.transactionStarted === false) {
         this.initializeConnectorStatus(connectorId);
       }
     }
@@ -1392,9 +1446,10 @@ export default class ChargingStation {
           `${this.logPrefix()} Registration failure: max retries reached (${this.getRegistrationMaxRetries()}) or retry disabled (${this.getRegistrationMaxRetries()})`
         );
       }
-      this.stopped && (this.stopped = false);
+      this.started === false && (this.started = true);
       this.autoReconnectRetryCount = 0;
       this.wsConnectionRestarted = false;
+      parentPort.postMessage(MessageChannelUtils.buildUpdatedMessage(this));
     } else {
       logger.warn(
         `${this.logPrefix()} Connection to OCPP server through ${this.wsConnectionUrl.toString()} failed`
@@ -1408,22 +1463,24 @@ export default class ChargingStation {
       case WebSocketCloseEventStatusCode.CLOSE_NORMAL:
       case WebSocketCloseEventStatusCode.CLOSE_NO_STATUS:
         logger.info(
-          `${this.logPrefix()} WebSocket normally closed with status '${ChargingStationUtils.getWebSocketCloseEventStatusString(
+          `${this.logPrefix()} WebSocket normally closed with status '${Utils.getWebSocketCloseEventStatusString(
             code
           )}' and reason '${reason}'`
         );
         this.autoReconnectRetryCount = 0;
+        await this.stopMessageSequence(StopTransactionReason.OTHER);
         break;
       // Abnormal close
       default:
         logger.error(
-          `${this.logPrefix()} WebSocket abnormally closed with status '${ChargingStationUtils.getWebSocketCloseEventStatusString(
+          `${this.logPrefix()} WebSocket abnormally closed with status '${Utils.getWebSocketCloseEventStatusString(
             code
           )}' and reason '${reason}'`
         );
         await this.reconnect(code);
         break;
     }
+    parentPort.postMessage(MessageChannelUtils.buildUpdatedMessage(this));
   }
 
   private async onMessage(data: Data): Promise<void> {
@@ -1606,16 +1663,6 @@ export default class ChargingStation {
       : true;
   }
 
-  private getNumberOfRunningTransactions(): number {
-    let trxCount = 0;
-    for (const connectorId of this.connectors.keys()) {
-      if (connectorId > 0 && this.getConnectorStatus(connectorId)?.transactionStarted) {
-        trxCount++;
-      }
-    }
-    return trxCount;
-  }
-
   // 0 for disabling
   private getConnectionTimeout(): number | undefined {
     if (
@@ -1732,7 +1779,7 @@ export default class ChargingStation {
       if (connectorId === 0) {
         continue;
       } else if (
-        !this.stopped &&
+        this.started === true &&
         !this.getConnectorStatus(connectorId)?.status &&
         this.getConnectorStatus(connectorId)?.bootStatus
       ) {
@@ -1748,7 +1795,7 @@ export default class ChargingStation {
         this.getConnectorStatus(connectorId).status =
           this.getConnectorStatus(connectorId).bootStatus;
       } else if (
-        this.stopped &&
+        this.started === false &&
         this.getConnectorStatus(connectorId)?.status &&
         this.getConnectorStatus(connectorId)?.bootStatus
       ) {
@@ -1763,7 +1810,7 @@ export default class ChargingStation {
         });
         this.getConnectorStatus(connectorId).status =
           this.getConnectorStatus(connectorId).bootStatus;
-      } else if (!this.stopped && this.getConnectorStatus(connectorId)?.status) {
+      } else if (this.started === true && this.getConnectorStatus(connectorId)?.status) {
         // Send previous status at template reload
         await this.ocppRequestService.requestHandler<
           StatusNotificationRequest,
@@ -1804,41 +1851,7 @@ export default class ChargingStation {
       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,
-                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, true),
-              idTag: this.getTransactionIdTag(transactionId),
-              reason,
-            });
-          }
-        }
+        await this.stopRunningTransactions(reason);
       }
     }
   }
@@ -1984,7 +1997,7 @@ export default class ChargingStation {
     // Stop heartbeat
     this.stopHeartbeat();
     // Stop the ATG if needed
-    if (this.automaticTransactionGenerator?.configuration?.stopOnConnectionFailure) {
+    if (this.automaticTransactionGenerator?.configuration?.stopOnConnectionFailure === true) {
       this.stopAutomaticTransactionGenerator();
     }
     if (
@@ -2018,6 +2031,7 @@ export default class ChargingStation {
       );
       this.wsConnectionRestarted = true;
     } else if (this.getAutoReconnectMaxRetries() !== -1) {
+      await this.stopMessageSequence(StopTransactionReason.OTHER);
       logger.error(
         `${this.logPrefix()} WebSocket reconnect failure: maximum retries reached (${
           this.autoReconnectRetryCount