refactor: factor out change availability helper
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16ServiceUtils.ts
index f65e0c6efca4feecff0cef091dcb40a443b4597e..cf055beb949d05f5844c86a450fd6cfc27c7f8ee 100644 (file)
@@ -2,10 +2,11 @@
 
 import type { JSONSchemaType } from 'ajv';
 
-import { type ChargingStation, getIdTagsFile } from '../../../charging-station';
+import { OCPP16Constants } from './OCPP16Constants';
+import { type ChargingStation, hasFeatureProfile } from '../../../charging-station';
 import { OCPPError } from '../../../exception';
 import {
-  type ConnectorStatus,
+  type ClearChargingProfileRequest,
   CurrentType,
   ErrorType,
   type JsonType,
@@ -14,9 +15,9 @@ import {
   MeterValueContext,
   MeterValueLocation,
   MeterValueUnit,
-  OCPP16AuthorizationStatus,
-  type OCPP16AuthorizeRequest,
-  type OCPP16AuthorizeResponse,
+  OCPP16AvailabilityType,
+  type OCPP16ChangeAvailabilityResponse,
+  OCPP16ChargePointStatus,
   type OCPP16ChargingProfile,
   type OCPP16IncomingRequestCommand,
   type OCPP16MeterValue,
@@ -40,7 +41,6 @@ import {
   getRandomFloatRounded,
   getRandomInteger,
   isNotEmptyArray,
-  isNotEmptyString,
   isNullOrUndefined,
   isUndefined,
   logger,
@@ -54,7 +54,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
     featureProfile: OCPP16SupportedFeatureProfiles,
     command: OCPP16RequestCommand | OCPP16IncomingRequestCommand,
   ): boolean {
-    if (!chargingStation.hasFeatureProfile(featureProfile)) {
+    if (!hasFeatureProfile(chargingStation, featureProfile)) {
       logger.warn(
         `${chargingStation.logPrefix()} Trying to '${command}' without '${featureProfile}' feature enabled in ${
           OCPP16StandardParametersKey.SupportedFeatureProfiles
@@ -411,9 +411,10 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
         const phaseValue = `L${phase}-N`;
         meterValue.sampledValue.push(
           OCPP16ServiceUtils.buildSampledValue(
-            (powerPerPhaseSampledValueTemplates[`L${phase}`] as SampledValueTemplate) ??
-              powerSampledValueTemplate,
-            powerMeasurandValues[`L${phase}`] as number,
+            powerPerPhaseSampledValueTemplates[
+              `L${phase}` as keyof MeasurandPerPhaseSampledValueTemplates
+            ]! ?? powerSampledValueTemplate,
+            powerMeasurandValues[`L${phase}` as keyof MeasurandPerPhaseSampledValueTemplates],
             undefined,
             phaseValue as OCPP16MeterValuePhase,
           ),
@@ -632,9 +633,10 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
         const phaseValue = `L${phase}`;
         meterValue.sampledValue.push(
           OCPP16ServiceUtils.buildSampledValue(
-            (currentPerPhaseSampledValueTemplates[phaseValue] as SampledValueTemplate) ??
-              currentSampledValueTemplate,
-            currentMeasurandValues[phaseValue] as number,
+            currentPerPhaseSampledValueTemplates[
+              phaseValue as keyof MeasurandPerPhaseSampledValueTemplates
+            ]! ?? currentSampledValueTemplate,
+            currentMeasurandValues[phaseValue as keyof MeasurandPerPhaseSampledValueTemplates],
             undefined,
             phaseValue as OCPP16MeterValuePhase,
           ),
@@ -723,10 +725,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
           `${chargingStation.logPrefix()} MeterValues measurand ${
             meterValue.sampledValue[sampledValuesIndex].measurand ??
             OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
-          }: connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${energyValueRounded}/${connectorMaximumEnergyRounded}, duration: ${roundTo(
-            interval / (3600 * 1000),
-            4,
-          )}h`,
+          }: connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${energyValueRounded}/${connectorMaximumEnergyRounded}, duration: ${interval}ms`,
         );
       }
     }
@@ -793,6 +792,29 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
     return meterValues;
   }
 
+  public static changeAvailability = async (
+    chargingStation: ChargingStation,
+    connectorId: number,
+    chargePointStatus: OCPP16ChargePointStatus,
+    availabilityType: OCPP16AvailabilityType,
+  ): Promise<OCPP16ChangeAvailabilityResponse> => {
+    let response: OCPP16ChangeAvailabilityResponse =
+      OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED;
+    const connectorStatus = chargingStation.getConnectorStatus(connectorId)!;
+    if (connectorStatus?.transactionStarted === true) {
+      response = OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
+    }
+    connectorStatus.availability = availabilityType;
+    if (response === OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED) {
+      await OCPP16ServiceUtils.sendAndSetConnectorStatus(
+        chargingStation,
+        connectorId,
+        chargePointStatus,
+      );
+    }
+    return response;
+  };
+
   public static setChargingProfile(
     chargingStation: ChargingStation,
     connectorId: number,
@@ -830,6 +852,49 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
     !cpReplaced && chargingStation.getConnectorStatus(connectorId)?.chargingProfiles?.push(cp);
   }
 
+  public static clearChargingProfiles = (
+    chargingStation: ChargingStation,
+    commandPayload: ClearChargingProfileRequest,
+    chargingProfiles: OCPP16ChargingProfile[] | undefined,
+  ): boolean => {
+    let clearedCP = false;
+    if (isNotEmptyArray(chargingProfiles)) {
+      chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => {
+        let clearCurrentCP = false;
+        if (chargingProfile.chargingProfileId === commandPayload.id) {
+          clearCurrentCP = true;
+        }
+        if (
+          !commandPayload.chargingProfilePurpose &&
+          chargingProfile.stackLevel === commandPayload.stackLevel
+        ) {
+          clearCurrentCP = true;
+        }
+        if (
+          !chargingProfile.stackLevel &&
+          chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose
+        ) {
+          clearCurrentCP = true;
+        }
+        if (
+          chargingProfile.stackLevel === commandPayload.stackLevel &&
+          chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose
+        ) {
+          clearCurrentCP = true;
+        }
+        if (clearCurrentCP) {
+          chargingProfiles.splice(index, 1);
+          logger.debug(
+            `${chargingStation.logPrefix()} Matching charging profile(s) cleared: %j`,
+            chargingProfile,
+          );
+          clearedCP = true;
+        }
+      });
+    }
+    return clearedCP;
+  };
+
   public static parseJsonSchemaFile<T extends JsonType>(
     relativePath: string,
     moduleName?: string,
@@ -843,29 +908,6 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
     );
   }
 
-  public static async isIdTagAuthorized(
-    chargingStation: ChargingStation,
-    connectorId: number,
-    idTag: string,
-  ): Promise<boolean> {
-    let authorized = false;
-    const connectorStatus: ConnectorStatus = chargingStation.getConnectorStatus(connectorId)!;
-    if (OCPP16ServiceUtils.isIdTagLocalAuthorized(chargingStation, idTag)) {
-      connectorStatus.localAuthorizeIdTag = idTag;
-      connectorStatus.idTagLocalAuthorized = true;
-      authorized = true;
-    } else if (chargingStation.getMustAuthorizeAtRemoteStart() === true) {
-      connectorStatus.authorizeIdTag = idTag;
-      authorized = await OCPP16ServiceUtils.isIdTagRemoteAuthorized(chargingStation, idTag);
-    } else {
-      logger.warn(
-        `${chargingStation.logPrefix()} The charging station configuration expects authorize at
-          remote start transaction but local authorization or authorize isn't enabled`,
-      );
-    }
-    return authorized;
-  }
-
   private static buildSampledValue(
     sampledValueTemplate: SampledValueTemplate,
     value: number,
@@ -941,30 +983,4 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils {
         return MeterValueUnit.VOLT;
     }
   }
-
-  private static isIdTagLocalAuthorized(chargingStation: ChargingStation, idTag: string): boolean {
-    return (
-      chargingStation.getLocalAuthListEnabled() === true &&
-      chargingStation.hasIdTags() === true &&
-      isNotEmptyString(
-        chargingStation.idTagsCache
-          .getIdTags(getIdTagsFile(chargingStation.stationInfo)!)
-          ?.find((tag) => tag === idTag),
-      )
-    );
-  }
-
-  private static async isIdTagRemoteAuthorized(
-    chargingStation: ChargingStation,
-    idTag: string,
-  ): Promise<boolean> {
-    const authorizeResponse: OCPP16AuthorizeResponse =
-      await chargingStation.ocppRequestService.requestHandler<
-        OCPP16AuthorizeRequest,
-        OCPP16AuthorizeResponse
-      >(chargingStation, OCPP16RequestCommand.AUTHORIZE, {
-        idTag: idTag,
-      });
-    return authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED;
-  }
 }