Moved ui folder
[e-mobility-charging-stations-simulator.git] / src / charging-station / ChargingStation.ts
index 8240e5b0521a1ec80ab093d9646997daed6bbc11..457369ff1ad029f30046e47432cb40c3546cc551 100644 (file)
@@ -5,9 +5,12 @@ import {
   AvailabilityType,
   BootNotificationRequest,
   CachedRequest,
+  HeartbeatRequest,
   IncomingRequest,
   IncomingRequestCommand,
+  MeterValuesRequest,
   RequestCommand,
+  StatusNotificationRequest,
 } from '../types/ocpp/Requests';
 import {
   BootNotificationResponse,
@@ -39,7 +42,11 @@ import {
   VendorDefaultParametersKey,
 } from '../types/ocpp/Configuration';
 import { MeterValue, MeterValueMeasurand, MeterValuePhase } from '../types/ocpp/MeterValues';
-import { StopTransactionReason, StopTransactionResponse } from '../types/ocpp/Transaction';
+import {
+  StopTransactionReason,
+  StopTransactionRequest,
+  StopTransactionResponse,
+} from '../types/ocpp/Transaction';
 import { WSError, WebSocketCloseEventStatusCode } from '../types/WebSocket';
 import WebSocket, { Data, OPEN, RawData } from 'ws';
 
@@ -236,23 +243,25 @@ export default class ChargingStation {
   }
 
   public getConnectorMaximumAvailablePower(connectorId: number): number {
-    let amperageLimitationPowerLimit: number;
-    if (this.getAmperageLimitation() < this.stationInfo.maximumAmperage) {
-      amperageLimitationPowerLimit =
-        this.getCurrentOutType() === CurrentType.AC
+    let connectorAmperageLimitationPowerLimit: number;
+    if (
+      !Utils.isNullOrUndefined(this.getAmperageLimitation()) &&
+      this.getAmperageLimitation() < this.stationInfo.maximumAmperage
+    ) {
+      connectorAmperageLimitationPowerLimit =
+        (this.getCurrentOutType() === CurrentType.AC
           ? ACElectricUtils.powerTotal(
               this.getNumberOfPhases(),
               this.getVoltageOut(),
               this.getAmperageLimitation() * this.getNumberOfConnectors()
             )
-          : DCElectricUtils.power(this.getVoltageOut(), this.getAmperageLimitation());
+          : DCElectricUtils.power(this.getVoltageOut(), this.getAmperageLimitation())) /
+        this.stationInfo.powerDivider;
     }
-    const connectorChargingProfilePowerLimit = this.getChargingProfilePowerLimit(connectorId);
     const connectorMaximumPower =
       ((this.stationInfo['maxPower'] as number) ?? this.stationInfo.maximumPower) /
       this.stationInfo.powerDivider;
-    const connectorAmperageLimitationPowerLimit =
-      amperageLimitationPowerLimit / this.stationInfo.powerDivider;
+    const connectorChargingProfilePowerLimit = this.getChargingProfilePowerLimit(connectorId);
     return Math.min(
       isNaN(connectorMaximumPower) ? Infinity : connectorMaximumPower,
       isNaN(connectorAmperageLimitationPowerLimit)
@@ -360,7 +369,7 @@ export default class ChargingStation {
     }
     if (
       measurand !== MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER &&
-      !this.getConfigurationKey(StandardParametersKey.MeterValuesSampledData).value.includes(
+      !this.getConfigurationKey(StandardParametersKey.MeterValuesSampledData)?.value.includes(
         measurand
       )
     ) {
@@ -391,7 +400,7 @@ export default class ChargingStation {
         phase &&
         sampledValueTemplates[index]?.phase === phase &&
         sampledValueTemplates[index]?.measurand === measurand &&
-        this.getConfigurationKey(StandardParametersKey.MeterValuesSampledData).value.includes(
+        this.getConfigurationKey(StandardParametersKey.MeterValuesSampledData)?.value.includes(
           measurand
         )
       ) {
@@ -400,7 +409,7 @@ export default class ChargingStation {
         !phase &&
         !sampledValueTemplates[index].phase &&
         sampledValueTemplates[index]?.measurand === measurand &&
-        this.getConfigurationKey(StandardParametersKey.MeterValuesSampledData).value.includes(
+        this.getConfigurationKey(StandardParametersKey.MeterValuesSampledData)?.value.includes(
           measurand
         )
       ) {
@@ -435,7 +444,7 @@ export default class ChargingStation {
     ) {
       // eslint-disable-next-line @typescript-eslint/no-misused-promises
       this.heartbeatSetInterval = setInterval(async (): Promise<void> => {
-        await this.ocppRequestService.sendMessageHandler<HeartbeatResponse>(
+        await this.ocppRequestService.sendMessageHandler<HeartbeatRequest, HeartbeatResponse>(
           RequestCommand.HEARTBEAT
         );
       }, this.getHeartbeatInterval());
@@ -507,7 +516,7 @@ export default class ChargingStation {
             this.getConnectorStatus(connectorId).transactionId,
             interval
           );
-          await this.ocppRequestService.sendMessageHandler<MeterValuesResponse>(
+          await this.ocppRequestService.sendMessageHandler<MeterValuesRequest, MeterValuesResponse>(
             RequestCommand.METER_VALUES,
             {
               connectorId,
@@ -612,14 +621,14 @@ export default class ChargingStation {
     await this.stopMessageSequence(reason);
     for (const connectorId of this.connectors.keys()) {
       if (connectorId > 0) {
-        await this.ocppRequestService.sendMessageHandler<StatusNotificationResponse>(
-          RequestCommand.STATUS_NOTIFICATION,
-          {
-            connectorId,
-            status: ChargePointStatus.UNAVAILABLE,
-            errorCode: ChargePointErrorCode.NO_ERROR,
-          }
-        );
+        await this.ocppRequestService.sendMessageHandler<
+          StatusNotificationRequest,
+          StatusNotificationResponse
+        >(RequestCommand.STATUS_NOTIFICATION, {
+          connectorId,
+          status: ChargePointStatus.UNAVAILABLE,
+          errorCode: ChargePointErrorCode.NO_ERROR,
+        });
         this.getConnectorStatus(connectorId).status = ChargePointStatus.UNAVAILABLE;
       }
     }
@@ -731,7 +740,7 @@ export default class ChargingStation {
           timestamp >= chargingProfile.chargingSchedule?.startSchedule.getTime() &&
           timestamp <
             chargingProfile.chargingSchedule?.startSchedule.getTime() +
-              chargingProfile.chargingSchedule.duration &&
+              chargingProfile.chargingSchedule.duration * 1000 &&
           chargingProfile?.stackLevel === Math.max(...chargingProfiles.map((cp) => cp?.stackLevel))
       );
       if (!Utils.isEmptyArray(chargingProfiles)) {
@@ -742,12 +751,14 @@ export default class ChargingStation {
                 (chargingSchedulePeriod, index) => {
                   timestamp >=
                     chargingProfile.chargingSchedule.startSchedule.getTime() +
-                      chargingSchedulePeriod.startPeriod &&
-                    chargingProfile.chargingSchedule.chargingSchedulePeriod[index + 1] &&
-                    timestamp <
-                      chargingProfile.chargingSchedule.startSchedule.getTime() +
-                        chargingProfile.chargingSchedule.chargingSchedulePeriod[index + 1]
-                          ?.startPeriod;
+                      chargingSchedulePeriod.startPeriod * 1000 &&
+                    ((chargingProfile.chargingSchedule.chargingSchedulePeriod[index + 1] &&
+                      timestamp <
+                        chargingProfile.chargingSchedule.startSchedule.getTime() +
+                          chargingProfile.chargingSchedule.chargingSchedulePeriod[index + 1]
+                            ?.startPeriod *
+                            1000) ||
+                      !chargingProfile.chargingSchedule.chargingSchedulePeriod[index + 1]);
                 }
               );
             if (!Utils.isEmptyArray(chargingSchedulePeriods)) {
@@ -783,7 +794,9 @@ export default class ChargingStation {
       this.stationInfo.powerDivider;
     if (limit > connectorMaximumPower) {
       logger.error(
-        `${this.logPrefix()} Charging profile limit is greater than connector id ${connectorId} maximum, dump their stack: %j`,
+        `${this.logPrefix()} Charging profile id ${
+          matchingChargingProfile.chargingProfileId
+        } limit is greater than connector id ${connectorId} maximum, dump charging profiles' stack: %j`,
         this.getConnectorStatus(connectorId).chargingProfiles
       );
       limit = connectorMaximumPower;
@@ -824,6 +837,12 @@ export default class ChargingStation {
     this.stopMeterValues(connectorId);
   }
 
+  public hasFeatureProfile(featureProfile: SupportedFeatureProfiles) {
+    return this.getConfigurationKey(StandardParametersKey.SupportedFeatureProfiles)?.value.includes(
+      featureProfile
+    );
+  }
+
   public bufferMessage(message: string): void {
     this.messageBuffer.add(message);
   }
@@ -907,6 +926,8 @@ export default class ChargingStation {
         (stationInfo.chargePointSerialNumber = existingStationInfo.chargePointSerialNumber);
       existingStationInfo?.chargeBoxSerialNumber &&
         (stationInfo.chargeBoxSerialNumber = existingStationInfo.chargeBoxSerialNumber);
+      existingStationInfo?.meterSerialNumber &&
+        (stationInfo.meterSerialNumber = existingStationInfo.meterSerialNumber);
     } else {
       const serialNumberSuffix = params?.randomSerialNumber
         ? this.getRandomSerialNumberSuffix({ upperCase: params.randomSerialNumberUpperCase })
@@ -917,6 +938,9 @@ export default class ChargingStation {
       stationInfo.chargeBoxSerialNumber =
         stationInfo?.chargeBoxSerialNumberPrefix &&
         stationInfo.chargeBoxSerialNumberPrefix + serialNumberSuffix;
+      stationInfo.meterSerialNumber =
+        stationInfo?.meterSerialNumberPrefix &&
+        stationInfo.meterSerialNumberPrefix + serialNumberSuffix;
     }
   }
 
@@ -1040,8 +1064,8 @@ export default class ChargingStation {
       }),
       ...(!Utils.isUndefined(stationInfo.iccid) && { iccid: stationInfo.iccid }),
       ...(!Utils.isUndefined(stationInfo.imsi) && { imsi: stationInfo.imsi }),
-      ...(!Utils.isUndefined(stationInfo.meterSerialNumber) && {
-        meterSerialNumber: stationInfo.meterSerialNumber,
+      ...(!Utils.isUndefined(stationInfo.meterSerialNumberPrefix) && {
+        meterSerialNumber: stationInfo.meterSerialNumberPrefix,
       }),
       ...(!Utils.isUndefined(stationInfo.meterType) && {
         meterType: stationInfo.meterType,
@@ -1217,7 +1241,7 @@ export default class ChargingStation {
     if (!this.getConfigurationKey(StandardParametersKey.SupportedFeatureProfiles)) {
       this.addConfigurationKey(
         StandardParametersKey.SupportedFeatureProfiles,
-        `${SupportedFeatureProfiles.Core},${SupportedFeatureProfiles.Local_Auth_List_Management},${SupportedFeatureProfiles.Smart_Charging}`
+        `${SupportedFeatureProfiles.Core},${SupportedFeatureProfiles.FirmwareManagement},${SupportedFeatureProfiles.LocalAuthListManagement},${SupportedFeatureProfiles.SmartCharging},${SupportedFeatureProfiles.RemoteTrigger}`
       );
     }
     this.addConfigurationKey(
@@ -1257,8 +1281,8 @@ export default class ChargingStation {
     }
     if (
       !this.getConfigurationKey(StandardParametersKey.LocalAuthListEnabled) &&
-      this.getConfigurationKey(StandardParametersKey.SupportedFeatureProfiles).value.includes(
-        SupportedFeatureProfiles.Local_Auth_List_Management
+      this.getConfigurationKey(StandardParametersKey.SupportedFeatureProfiles)?.value.includes(
+        SupportedFeatureProfiles.LocalAuthListManagement
       )
     ) {
       this.addConfigurationKey(StandardParametersKey.LocalAuthListEnabled, 'false');
@@ -1373,22 +1397,24 @@ export default class ChargingStation {
       // Send BootNotification
       let registrationRetryCount = 0;
       do {
-        this.bootNotificationResponse =
-          await this.ocppRequestService.sendMessageHandler<BootNotificationResponse>(
-            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 }
-          );
+        this.bootNotificationResponse = await this.ocppRequestService.sendMessageHandler<
+          BootNotificationRequest,
+          BootNotificationResponse
+        >(
+          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 }
+        );
         if (!this.isInAcceptedState()) {
           this.getRegistrationMaxRetries() !== -1 && registrationRetryCount++;
           await Utils.sleep(
@@ -1718,7 +1744,10 @@ export default class ChargingStation {
 
   private async startMessageSequence(): Promise<void> {
     if (this.stationInfo.autoRegister) {
-      await this.ocppRequestService.sendMessageHandler<BootNotificationResponse>(
+      await this.ocppRequestService.sendMessageHandler<
+        BootNotificationRequest,
+        BootNotificationResponse
+      >(
         RequestCommand.BOOT_NOTIFICATION,
         {
           chargePointModel: this.bootNotificationRequest.chargePointModel,
@@ -1748,14 +1777,14 @@ export default class ChargingStation {
         this.getConnectorStatus(connectorId)?.bootStatus
       ) {
         // Send status in template at startup
-        await this.ocppRequestService.sendMessageHandler<StatusNotificationResponse>(
-          RequestCommand.STATUS_NOTIFICATION,
-          {
-            connectorId,
-            status: this.getConnectorStatus(connectorId).bootStatus,
-            errorCode: ChargePointErrorCode.NO_ERROR,
-          }
-        );
+        await this.ocppRequestService.sendMessageHandler<
+          StatusNotificationRequest,
+          StatusNotificationResponse
+        >(RequestCommand.STATUS_NOTIFICATION, {
+          connectorId,
+          status: this.getConnectorStatus(connectorId).bootStatus,
+          errorCode: ChargePointErrorCode.NO_ERROR,
+        });
         this.getConnectorStatus(connectorId).status =
           this.getConnectorStatus(connectorId).bootStatus;
       } else if (
@@ -1764,36 +1793,36 @@ export default class ChargingStation {
         this.getConnectorStatus(connectorId)?.bootStatus
       ) {
         // Send status in template after reset
-        await this.ocppRequestService.sendMessageHandler<StatusNotificationResponse>(
-          RequestCommand.STATUS_NOTIFICATION,
-          {
-            connectorId,
-            status: this.getConnectorStatus(connectorId).bootStatus,
-            errorCode: ChargePointErrorCode.NO_ERROR,
-          }
-        );
+        await this.ocppRequestService.sendMessageHandler<
+          StatusNotificationRequest,
+          StatusNotificationResponse
+        >(RequestCommand.STATUS_NOTIFICATION, {
+          connectorId,
+          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.sendMessageHandler<StatusNotificationResponse>(
-          RequestCommand.STATUS_NOTIFICATION,
-          {
-            connectorId,
-            status: this.getConnectorStatus(connectorId).status,
-            errorCode: ChargePointErrorCode.NO_ERROR,
-          }
-        );
+        await this.ocppRequestService.sendMessageHandler<
+          StatusNotificationRequest,
+          StatusNotificationResponse
+        >(RequestCommand.STATUS_NOTIFICATION, {
+          connectorId,
+          status: this.getConnectorStatus(connectorId).status,
+          errorCode: ChargePointErrorCode.NO_ERROR,
+        });
       } else {
         // Send default status
-        await this.ocppRequestService.sendMessageHandler<StatusNotificationResponse>(
-          RequestCommand.STATUS_NOTIFICATION,
-          {
-            connectorId,
-            status: ChargePointStatus.AVAILABLE,
-            errorCode: ChargePointErrorCode.NO_ERROR,
-          }
-        );
+        await this.ocppRequestService.sendMessageHandler<
+          StatusNotificationRequest,
+          StatusNotificationResponse
+        >(RequestCommand.STATUS_NOTIFICATION, {
+          connectorId,
+          status: ChargePointStatus.AVAILABLE,
+          errorCode: ChargePointErrorCode.NO_ERROR,
+        });
         this.getConnectorStatus(connectorId).status = ChargePointStatus.AVAILABLE;
       }
     }
@@ -1840,24 +1869,24 @@ export default class ChargingStation {
               connectorId,
               this.getEnergyActiveImportRegisterByTransactionId(transactionId)
             );
-            await this.ocppRequestService.sendMessageHandler<MeterValuesResponse>(
-              RequestCommand.METER_VALUES,
-              {
-                connectorId,
-                transactionId,
-                meterValue: transactionEndMeterValue,
-              }
-            );
-          }
-          await this.ocppRequestService.sendMessageHandler<StopTransactionResponse>(
-            RequestCommand.STOP_TRANSACTION,
-            {
+            await this.ocppRequestService.sendMessageHandler<
+              MeterValuesRequest,
+              MeterValuesResponse
+            >(RequestCommand.METER_VALUES, {
+              connectorId,
               transactionId,
-              meterStop: this.getEnergyActiveImportRegisterByTransactionId(transactionId),
-              idTag: this.getTransactionIdTag(transactionId),
-              reason,
-            }
-          );
+              meterValue: transactionEndMeterValue,
+            });
+          }
+          await this.ocppRequestService.sendMessageHandler<
+            StopTransactionRequest,
+            StopTransactionResponse
+          >(RequestCommand.STOP_TRANSACTION, {
+            transactionId,
+            meterStop: this.getEnergyActiveImportRegisterByTransactionId(transactionId),
+            idTag: this.getTransactionIdTag(transactionId),
+            reason,
+          });
         }
       }
     }