Fix docker image build
[e-mobility-charging-stations-simulator.git] / src / charging-station / ChargingStation.ts
index d136a1504e473d221c5b22cfa1df5fb40eeca143..a451c5ee4668b31cbad728fa4881cf2356a812a8 100644 (file)
@@ -23,11 +23,12 @@ import {
   SupportedFeatureProfiles,
   VendorDefaultParametersKey,
 } from '../types/ocpp/Configuration';
-import { MeterValueMeasurand, MeterValuePhase } from '../types/ocpp/MeterValues';
+import { MeterValue, MeterValueMeasurand, MeterValuePhase } from '../types/ocpp/MeterValues';
 import { WSError, WebSocketCloseEventStatusCode } from '../types/WebSocket';
 import WebSocket, { ClientOptions, Data, OPEN, RawData } from 'ws';
 
 import AutomaticTransactionGenerator from './AutomaticTransactionGenerator';
+import { ChargePointErrorCode } from '../types/ocpp/ChargePointErrorCode';
 import { ChargePointStatus } from '../types/ocpp/ChargePointStatus';
 import { ChargingProfile } from '../types/ocpp/ChargingProfile';
 import ChargingStationInfo from '../types/ChargingStationInfo';
@@ -43,6 +44,7 @@ import { MessageType } from '../types/ocpp/MessageType';
 import OCPP16IncomingRequestService from './ocpp/1.6/OCPP16IncomingRequestService';
 import OCPP16RequestService from './ocpp/1.6/OCPP16RequestService';
 import OCPP16ResponseService from './ocpp/1.6/OCPP16ResponseService';
+import { OCPP16ServiceUtils } from './ocpp/1.6/OCPP16ServiceUtils';
 import OCPPError from '../exception/OCPPError';
 import OCPPIncomingRequestService from './ocpp/OCPPIncomingRequestService';
 import OCPPRequestService from './ocpp/OCPPRequestService';
@@ -253,29 +255,33 @@ export default class ChargingStation {
     return this.stationInfo.phaseLineToLineVoltageMeterValues ?? false;
   }
 
-  public getEnergyActiveImportRegisterByTransactionId(transactionId: number): number | undefined {
-    if (this.getMeteringPerTransaction()) {
-      for (const connectorId of this.connectors.keys()) {
-        if (
-          connectorId > 0 &&
-          this.getConnectorStatus(connectorId).transactionId === transactionId
-        ) {
-          return this.getConnectorStatus(connectorId).transactionEnergyActiveImportRegisterValue;
-        }
-      }
-    }
+  public getConnectorIdByTransactionId(transactionId: number): number | undefined {
     for (const connectorId of this.connectors.keys()) {
-      if (connectorId > 0 && this.getConnectorStatus(connectorId).transactionId === transactionId) {
-        return this.getConnectorStatus(connectorId).energyActiveImportRegisterValue;
+      if (
+        connectorId > 0 &&
+        this.getConnectorStatus(connectorId)?.transactionId === transactionId
+      ) {
+        return connectorId;
       }
     }
   }
 
+  public getEnergyActiveImportRegisterByTransactionId(transactionId: number): number | undefined {
+    const transactionConnectorStatus = this.getConnectorStatus(
+      this.getConnectorIdByTransactionId(transactionId)
+    );
+    if (this.getMeteringPerTransaction()) {
+      return transactionConnectorStatus?.transactionEnergyActiveImportRegisterValue;
+    }
+    return transactionConnectorStatus?.energyActiveImportRegisterValue;
+  }
+
   public getEnergyActiveImportRegisterByConnectorId(connectorId: number): number | undefined {
+    const connectorStatus = this.getConnectorStatus(connectorId);
     if (this.getMeteringPerTransaction()) {
-      return this.getConnectorStatus(connectorId).transactionEnergyActiveImportRegisterValue;
+      return connectorStatus?.transactionEnergyActiveImportRegisterValue;
     }
-    return this.getConnectorStatus(connectorId).energyActiveImportRegisterValue;
+    return connectorStatus?.energyActiveImportRegisterValue;
   }
 
   public getAuthorizeRemoteTxRequests(): boolean {
@@ -390,7 +396,7 @@ export default class ChargingStation {
     ) {
       // eslint-disable-next-line @typescript-eslint/no-misused-promises
       this.heartbeatSetInterval = setInterval(async (): Promise<void> => {
-        await this.ocppRequestService.sendHeartbeat();
+        await this.ocppRequestService.sendMessageHandler(RequestCommand.HEARTBEAT);
       }, this.getHeartbeatInterval());
       logger.info(
         this.logPrefix() +
@@ -453,11 +459,18 @@ export default class ChargingStation {
       this.getConnectorStatus(connectorId).transactionSetInterval = setInterval(
         // eslint-disable-next-line @typescript-eslint/no-misused-promises
         async (): Promise<void> => {
-          await this.ocppRequestService.sendMeterValues(
+          // FIXME: Implement OCPP version agnostic helpers
+          const meterValue: MeterValue = OCPP16ServiceUtils.buildMeterValue(
+            this,
             connectorId,
             this.getConnectorStatus(connectorId).transactionId,
             interval
           );
+          await this.ocppRequestService.sendMessageHandler(RequestCommand.METER_VALUES, {
+            connectorId,
+            transactionId: this.getConnectorStatus(connectorId).transactionId,
+            meterValue: [meterValue],
+          });
         },
         interval
       );
@@ -513,10 +526,11 @@ export default class ChargingStation {
     await this.stopMessageSequence(reason);
     for (const connectorId of this.connectors.keys()) {
       if (connectorId > 0) {
-        await this.ocppRequestService.sendStatusNotification(
+        await this.ocppRequestService.sendMessageHandler(RequestCommand.STATUS_NOTIFICATION, {
           connectorId,
-          ChargePointStatus.UNAVAILABLE
-        );
+          status: ChargePointStatus.UNAVAILABLE,
+          errorCode: ChargePointErrorCode.NO_ERROR,
+        });
         this.getConnectorStatus(connectorId).status = ChargePointStatus.UNAVAILABLE;
       }
     }
@@ -643,14 +657,13 @@ export default class ChargingStation {
     // In case of multiple instances: add instance index to charging station id
     const instanceIndex = process.env.CF_INSTANCE_INDEX ?? 0;
     const idSuffix = stationTemplate.nameSuffix ?? '';
+    const idStr = '000000000' + this.index.toString();
     return stationTemplate.fixedName
       ? stationTemplate.baseName
       : stationTemplate.baseName +
           '-' +
           instanceIndex.toString() +
-          ('000000000' + this.index.toString()).substr(
-            ('000000000' + this.index.toString()).length - 4
-          ) +
+          idStr.substring(idStr.length - 4) +
           idSuffix;
   }
 
@@ -937,12 +950,21 @@ export default class ChargingStation {
       // Send BootNotification
       let registrationRetryCount = 0;
       do {
-        this.bootNotificationResponse = await this.ocppRequestService.sendBootNotification(
-          this.bootNotificationRequest.chargePointModel,
-          this.bootNotificationRequest.chargePointVendor,
-          this.bootNotificationRequest.chargeBoxSerialNumber,
-          this.bootNotificationRequest.firmwareVersion
-        );
+        this.bootNotificationResponse = (await this.ocppRequestService.sendMessageHandler(
+          RequestCommand.BOOT_NOTIFICATION,
+          {
+            chargePointModel: this.bootNotificationRequest.chargePointModel,
+            chargePointVendor: this.bootNotificationRequest.chargePointVendor,
+            chargeBoxSerialNumber: this.bootNotificationRequest.chargeBoxSerialNumber,
+            firmwareVersion: this.bootNotificationRequest.firmwareVersion,
+            chargePointSerialNumber: this.bootNotificationRequest.chargePointSerialNumber,
+            iccid: this.bootNotificationRequest.iccid,
+            imsi: this.bootNotificationRequest.imsi,
+            meterSerialNumber: this.bootNotificationRequest.meterSerialNumber,
+            meterType: this.bootNotificationRequest.meterType,
+          },
+          { skipBufferingOnError: true }
+        )) as BootNotificationResponse;
         if (!this.isInAcceptedState()) {
           this.getRegistrationMaxRetries() !== -1 && registrationRetryCount++;
           await Utils.sleep(
@@ -1237,11 +1259,20 @@ export default class ChargingStation {
 
   private async startMessageSequence(): Promise<void> {
     if (this.stationInfo.autoRegister) {
-      await this.ocppRequestService.sendBootNotification(
-        this.bootNotificationRequest.chargePointModel,
-        this.bootNotificationRequest.chargePointVendor,
-        this.bootNotificationRequest.chargeBoxSerialNumber,
-        this.bootNotificationRequest.firmwareVersion
+      await this.ocppRequestService.sendMessageHandler(
+        RequestCommand.BOOT_NOTIFICATION,
+        {
+          chargePointModel: this.bootNotificationRequest.chargePointModel,
+          chargePointVendor: this.bootNotificationRequest.chargePointVendor,
+          chargeBoxSerialNumber: this.bootNotificationRequest.chargeBoxSerialNumber,
+          firmwareVersion: this.bootNotificationRequest.firmwareVersion,
+          chargePointSerialNumber: this.bootNotificationRequest.chargePointSerialNumber,
+          iccid: this.bootNotificationRequest.iccid,
+          imsi: this.bootNotificationRequest.imsi,
+          meterSerialNumber: this.bootNotificationRequest.meterSerialNumber,
+          meterType: this.bootNotificationRequest.meterType,
+        },
+        { skipBufferingOnError: true }
       );
     }
     // Start WebSocket ping
@@ -1258,10 +1289,11 @@ export default class ChargingStation {
         this.getConnectorStatus(connectorId)?.bootStatus
       ) {
         // Send status in template at startup
-        await this.ocppRequestService.sendStatusNotification(
+        await this.ocppRequestService.sendMessageHandler(RequestCommand.STATUS_NOTIFICATION, {
           connectorId,
-          this.getConnectorStatus(connectorId).bootStatus
-        );
+          status: this.getConnectorStatus(connectorId).bootStatus,
+          errorCode: ChargePointErrorCode.NO_ERROR,
+        });
         this.getConnectorStatus(connectorId).status =
           this.getConnectorStatus(connectorId).bootStatus;
       } else if (
@@ -1270,24 +1302,27 @@ export default class ChargingStation {
         this.getConnectorStatus(connectorId)?.bootStatus
       ) {
         // Send status in template after reset
-        await this.ocppRequestService.sendStatusNotification(
+        await this.ocppRequestService.sendMessageHandler(RequestCommand.STATUS_NOTIFICATION, {
           connectorId,
-          this.getConnectorStatus(connectorId).bootStatus
-        );
+          status: this.getConnectorStatus(connectorId).bootStatus,
+          errorCode: ChargePointErrorCode.NO_ERROR,
+        });
         this.getConnectorStatus(connectorId).status =
           this.getConnectorStatus(connectorId).bootStatus;
       } else if (!this.stopped && this.getConnectorStatus(connectorId)?.status) {
         // Send previous status at template reload
-        await this.ocppRequestService.sendStatusNotification(
+        await this.ocppRequestService.sendMessageHandler(RequestCommand.STATUS_NOTIFICATION, {
           connectorId,
-          this.getConnectorStatus(connectorId).status
-        );
+          status: this.getConnectorStatus(connectorId).status,
+          errorCode: ChargePointErrorCode.NO_ERROR,
+        });
       } else {
         // Send default status
-        await this.ocppRequestService.sendStatusNotification(
+        await this.ocppRequestService.sendMessageHandler(RequestCommand.STATUS_NOTIFICATION, {
           connectorId,
-          ChargePointStatus.AVAILABLE
-        );
+          status: ChargePointStatus.AVAILABLE,
+          errorCode: ChargePointErrorCode.NO_ERROR,
+        });
         this.getConnectorStatus(connectorId).status = ChargePointStatus.AVAILABLE;
       }
     }
@@ -1323,12 +1358,29 @@ export default class ChargingStation {
       for (const connectorId of this.connectors.keys()) {
         if (connectorId > 0 && this.getConnectorStatus(connectorId)?.transactionStarted) {
           const transactionId = this.getConnectorStatus(connectorId).transactionId;
-          await this.ocppRequestService.sendStopTransaction(
+          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.sendMessageHandler(RequestCommand.METER_VALUES, {
+              connectorId,
+              transactionId,
+              meterValue: transactionEndMeterValue,
+            });
+          }
+          await this.ocppRequestService.sendMessageHandler(RequestCommand.STOP_TRANSACTION, {
             transactionId,
-            this.getEnergyActiveImportRegisterByTransactionId(transactionId),
-            this.getTransactionIdTag(transactionId),
-            reason
-          );
+            meterStop: this.getEnergyActiveImportRegisterByTransactionId(transactionId),
+            idTag: this.getTransactionIdTag(transactionId),
+            reason,
+          });
         }
       }
     }
@@ -1401,8 +1453,7 @@ export default class ChargingStation {
     key: string
   ): void {
     if (!Utils.isUndefined(template[deprecatedKey])) {
-      // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
-      template[key] = template[deprecatedKey];
+      template[key] = template[deprecatedKey] as unknown;
       delete template[deprecatedKey];
     }
   }