refactor(simulator): permit to check if connector status transition is
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16IncomingRequestService.ts
index fb87345d00d9ccba36f3fa0164c0d21be3236453..49ed8c92ae99ffd93fb4495d4fb916c8c73da925 100644 (file)
@@ -23,6 +23,7 @@ import {
   type ClearChargingProfileResponse,
   ErrorType,
   type GenericResponse,
+  GenericStatus,
   type GetConfigurationRequest,
   type GetConfigurationResponse,
   type GetDiagnosticsRequest,
@@ -40,6 +41,7 @@ import {
   OCPP16ChargePointStatus,
   type OCPP16ChargingProfile,
   OCPP16ChargingProfilePurposeType,
+  type OCPP16ChargingSchedule,
   type OCPP16ClearCacheRequest,
   type OCPP16DataTransferRequest,
   type OCPP16DataTransferResponse,
@@ -51,6 +53,8 @@ import {
   OCPP16FirmwareStatus,
   type OCPP16FirmwareStatusNotificationRequest,
   type OCPP16FirmwareStatusNotificationResponse,
+  type OCPP16GetCompositeScheduleRequest,
+  type OCPP16GetCompositeScheduleResponse,
   type OCPP16HeartbeatRequest,
   type OCPP16HeartbeatResponse,
   OCPP16IncomingRequestCommand,
@@ -103,6 +107,10 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
         OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION,
         this.handleRequestChangeConfiguration.bind(this),
       ],
+      [
+        OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE,
+        this.handleRequestGetCompositeSchedule.bind(this),
+      ],
       [
         OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
         this.handleRequestSetChargingProfile.bind(this),
@@ -177,6 +185,14 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
           'constructor'
         ),
       ],
+      [
+        OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE,
+        OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16GetCompositeScheduleRequest>(
+          '../../../assets/json-schemas/ocpp/1.6/GetCompositeSchedule.json',
+          moduleName,
+          'constructor'
+        ),
+      ],
       [
         OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
         OCPP16ServiceUtils.parseJsonSchemaFile<SetChargingProfileRequest>(
@@ -242,7 +258,11 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
         ),
       ],
     ]);
-    this.validatePayload.bind(this);
+    this.validatePayload = this.validatePayload.bind(this) as (
+      chargingStation: ChargingStation,
+      commandName: OCPP16IncomingRequestCommand,
+      commandPayload: JsonType
+    ) => boolean;
   }
 
   public async incomingRequestHandler(
@@ -396,15 +416,11 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
       }
       return OCPPConstants.OCPP_RESPONSE_UNLOCK_FAILED;
     }
-    await chargingStation.ocppRequestService.requestHandler<
-      OCPP16StatusNotificationRequest,
-      OCPP16StatusNotificationResponse
-    >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
+    await OCPP16ServiceUtils.sendAndSetConnectorStatus(
+      chargingStation,
       connectorId,
-      status: OCPP16ChargePointStatus.Available,
-      errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
-    });
-    chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.Available;
+      OCPP16ChargePointStatus.Available
+    );
     return OCPPConstants.OCPP_RESPONSE_UNLOCKED;
   }
 
@@ -568,6 +584,56 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
     return OCPPConstants.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED;
   }
 
+  private handleRequestGetCompositeSchedule(
+    chargingStation: ChargingStation,
+    commandPayload: OCPP16GetCompositeScheduleRequest
+  ): OCPP16GetCompositeScheduleResponse {
+    if (
+      OCPP16ServiceUtils.checkFeatureProfile(
+        chargingStation,
+        OCPP16SupportedFeatureProfiles.SmartCharging,
+        OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE
+      ) === false
+    ) {
+      return OCPPConstants.OCPP_RESPONSE_REJECTED;
+    }
+    if (chargingStation.connectors.has(commandPayload.connectorId) === false) {
+      logger.error(
+        `${chargingStation.logPrefix()} Trying to get composite schedule to a non existing connector Id ${
+          commandPayload.connectorId
+        }`
+      );
+      return OCPPConstants.OCPP_RESPONSE_REJECTED;
+    }
+    if (
+      Utils.isEmptyArray(
+        chargingStation.getConnectorStatus(commandPayload.connectorId)?.chargingProfiles
+      )
+    ) {
+      return OCPPConstants.OCPP_RESPONSE_REJECTED;
+    }
+    const startDate = new Date();
+    const endDate = new Date(startDate.getTime() + commandPayload.duration * 1000);
+    let compositeSchedule: OCPP16ChargingSchedule;
+    for (const chargingProfile of chargingStation.getConnectorStatus(commandPayload.connectorId)
+      .chargingProfiles) {
+      // FIXME: build the composite schedule including the local power limit, the stack level, the charging rate unit, etc.
+      if (
+        chargingProfile.chargingSchedule?.startSchedule >= startDate &&
+        chargingProfile.chargingSchedule?.startSchedule <= endDate
+      ) {
+        compositeSchedule = chargingProfile.chargingSchedule;
+        break;
+      }
+    }
+    return {
+      status: GenericStatus.Accepted,
+      scheduleStart: compositeSchedule?.startSchedule,
+      connectorId: commandPayload.connectorId,
+      chargingSchedule: compositeSchedule,
+    };
+  }
+
   private handleRequestClearChargingProfile(
     chargingStation: ChargingStation,
     commandPayload: ClearChargingProfileRequest
@@ -674,15 +740,11 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
         }
         chargingStation.getConnectorStatus(id).availability = commandPayload.type;
         if (response === OCPPConstants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED) {
-          await chargingStation.ocppRequestService.requestHandler<
-            OCPP16StatusNotificationRequest,
-            OCPP16StatusNotificationResponse
-          >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
-            connectorId: id,
-            status: chargePointStatus,
-            errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
-          });
-          chargingStation.getConnectorStatus(id).status = chargePointStatus;
+          await OCPP16ServiceUtils.sendAndSetConnectorStatus(
+            chargingStation,
+            id,
+            chargePointStatus
+          );
         }
       }
       return response;
@@ -697,15 +759,11 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
         return OCPPConstants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
       }
       chargingStation.getConnectorStatus(connectorId).availability = commandPayload.type;
-      await chargingStation.ocppRequestService.requestHandler<
-        OCPP16StatusNotificationRequest,
-        OCPP16StatusNotificationResponse
-      >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
+      await OCPP16ServiceUtils.sendAndSetConnectorStatus(
+        chargingStation,
         connectorId,
-        status: chargePointStatus,
-        errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
-      });
-      chargingStation.getConnectorStatus(connectorId).status = chargePointStatus;
+        chargePointStatus
+      );
       return OCPPConstants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED;
     }
     return OCPPConstants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
@@ -720,16 +778,12 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
       const remoteStartTransactionLogMsg = `${chargingStation.logPrefix()} Transaction remotely STARTED on ${
         chargingStation.stationInfo.chargingStationId
       }#${transactionConnectorId.toString()} for idTag '${commandPayload.idTag}'`;
-      await chargingStation.ocppRequestService.requestHandler<
-        OCPP16StatusNotificationRequest,
-        OCPP16StatusNotificationResponse
-      >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
-        connectorId: transactionConnectorId,
-        status: OCPP16ChargePointStatus.Preparing,
-        errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
-      });
+      await OCPP16ServiceUtils.sendAndSetConnectorStatus(
+        chargingStation,
+        transactionConnectorId,
+        OCPP16ChargePointStatus.Preparing
+      );
       const connectorStatus = chargingStation.getConnectorStatus(transactionConnectorId);
-      connectorStatus.status = OCPP16ChargePointStatus.Preparing;
       if (
         chargingStation.isChargingStationAvailable() === true &&
         chargingStation.isConnectorAvailable(transactionConnectorId) === true
@@ -739,12 +793,10 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
           let authorized = false;
           if (
             chargingStation.getLocalAuthListEnabled() === true &&
-            chargingStation.hasAuthorizedTags() === true &&
+            chargingStation.hasIdTags() === true &&
             Utils.isNotEmptyString(
-              chargingStation.authorizedTagsCache
-                .getAuthorizedTags(
-                  ChargingStationUtils.getAuthorizationFile(chargingStation.stationInfo)
-                )
+              chargingStation.idTagsCache
+                .getIdTags(ChargingStationUtils.getIdTagsFile(chargingStation.stationInfo))
                 ?.find((idTag) => idTag === commandPayload.idTag)
             )
           ) {
@@ -866,15 +918,11 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
     if (
       chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.Available
     ) {
-      await chargingStation.ocppRequestService.requestHandler<
-        OCPP16StatusNotificationRequest,
-        OCPP16StatusNotificationResponse
-      >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
+      await OCPP16ServiceUtils.sendAndSetConnectorStatus(
+        chargingStation,
         connectorId,
-        status: OCPP16ChargePointStatus.Available,
-        errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
-      });
-      chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.Available;
+        OCPP16ChargePointStatus.Available
+      );
     }
     logger.warn(
       `${chargingStation.logPrefix()} Remote starting transaction REJECTED on connector Id ${connectorId.toString()}, idTag '${idTag}', availability '${
@@ -918,15 +966,11 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
         connectorId > 0 &&
         chargingStation.getConnectorStatus(connectorId)?.transactionId === transactionId
       ) {
-        await chargingStation.ocppRequestService.requestHandler<
-          OCPP16StatusNotificationRequest,
-          OCPP16StatusNotificationResponse
-        >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
+        await OCPP16ServiceUtils.sendAndSetConnectorStatus(
+          chargingStation,
           connectorId,
-          status: OCPP16ChargePointStatus.Finishing,
-          errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
-        });
-        chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.Finishing;
+          OCPP16ChargePointStatus.Finishing
+        );
         const stopResponse = await chargingStation.stopTransactionOnConnector(
           connectorId,
           OCPP16StopTransactionReason.REMOTE
@@ -992,22 +1036,22 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
     maxDelay = 30,
     minDelay = 15
   ): Promise<void> {
-    chargingStation.stopAutomaticTransactionGenerator();
+    if (
+      ChargingStationUtils.checkChargingStation(chargingStation, chargingStation.logPrefix()) ===
+      false
+    ) {
+      return;
+    }
     for (const connectorId of chargingStation.connectors.keys()) {
       if (
         connectorId > 0 &&
         chargingStation.getConnectorStatus(connectorId)?.transactionStarted === false
       ) {
-        await chargingStation.ocppRequestService.requestHandler<
-          OCPP16StatusNotificationRequest,
-          OCPP16StatusNotificationResponse
-        >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
+        await OCPP16ServiceUtils.sendAndSetConnectorStatus(
+          chargingStation,
           connectorId,
-          status: OCPP16ChargePointStatus.Unavailable,
-          errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
-        });
-        chargingStation.getConnectorStatus(connectorId).status =
-          OCPP16ChargePointStatus.Unavailable;
+          OCPP16ChargePointStatus.Unavailable
+        );
       }
     }
     await chargingStation.ocppRequestService.requestHandler<
@@ -1040,6 +1084,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
       status: OCPP16FirmwareStatus.Downloaded,
     });
     chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloaded;
+    let wasTransactionsStarted = false;
     let transactionsStarted: boolean;
     do {
       let trxCount = 0;
@@ -1060,6 +1105,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
         );
         await Utils.sleep(waitTime);
         transactionsStarted = true;
+        wasTransactionsStarted = true;
       } else {
         for (const connectorId of chargingStation.connectors.keys()) {
           if (
@@ -1067,22 +1113,24 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
             chargingStation.getConnectorStatus(connectorId)?.status !==
               OCPP16ChargePointStatus.Unavailable
           ) {
-            await chargingStation.ocppRequestService.requestHandler<
-              OCPP16StatusNotificationRequest,
-              OCPP16StatusNotificationResponse
-            >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
+            await OCPP16ServiceUtils.sendAndSetConnectorStatus(
+              chargingStation,
               connectorId,
-              status: OCPP16ChargePointStatus.Unavailable,
-              errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
-            });
-            chargingStation.getConnectorStatus(connectorId).status =
-              OCPP16ChargePointStatus.Unavailable;
+              OCPP16ChargePointStatus.Unavailable
+            );
           }
         }
         transactionsStarted = false;
       }
     } while (transactionsStarted);
-    await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
+    !wasTransactionsStarted &&
+      (await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000));
+    if (
+      ChargingStationUtils.checkChargingStation(chargingStation, chargingStation.logPrefix()) ===
+      false
+    ) {
+      return;
+    }
     await chargingStation.ocppRequestService.requestHandler<
       OCPP16FirmwareStatusNotificationRequest,
       OCPP16FirmwareStatusNotificationResponse