fix: ensure OCPP request timeouting cancel it
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / OCPPServiceUtils.ts
index b74740875b2753642eb673ae3dcd9a8cb5a3c89a..d9d712981c816790cb4e2b6f1c09fa6cd10e7dac 100644 (file)
@@ -15,6 +15,7 @@ import {
   type AuthorizeRequest,
   type AuthorizeResponse,
   ChargePointErrorCode,
+  ChargingStationEvents,
   type ConnectorStatus,
   type ConnectorStatusEnum,
   ErrorType,
@@ -40,6 +41,8 @@ import {
   isNotEmptyString,
   logPrefix,
   logger,
+  max,
+  min,
 } from '../../utils';
 
 export class OCPPServiceUtils {
@@ -47,17 +50,19 @@ export class OCPPServiceUtils {
     // This is intentional
   }
 
-  public static ajvErrorsToErrorType(errors: ErrorObject[]): ErrorType {
-    for (const error of errors as DefinedError[]) {
-      switch (error.keyword) {
-        case 'type':
-          return ErrorType.TYPE_CONSTRAINT_VIOLATION;
-        case 'dependencies':
-        case 'required':
-          return ErrorType.OCCURRENCE_CONSTRAINT_VIOLATION;
-        case 'pattern':
-        case 'format':
-          return ErrorType.PROPERTY_CONSTRAINT_VIOLATION;
+  public static ajvErrorsToErrorType(errors: ErrorObject[] | null | undefined): ErrorType {
+    if (isNotEmptyArray(errors) === true) {
+      for (const error of errors as DefinedError[]) {
+        switch (error.keyword) {
+          case 'type':
+            return ErrorType.TYPE_CONSTRAINT_VIOLATION;
+          case 'dependencies':
+          case 'required':
+            return ErrorType.OCCURRENCE_CONSTRAINT_VIOLATION;
+          case 'pattern':
+          case 'format':
+            return ErrorType.PROPERTY_CONSTRAINT_VIOLATION;
+        }
       }
     }
     return ErrorType.FORMAT_VIOLATION;
@@ -88,9 +93,9 @@ export class OCPPServiceUtils {
       return true;
     } else if (
       isRequestCommand === true &&
-      chargingStation.stationInfo?.commandsSupport?.outgoingCommands
+      chargingStation.stationInfo?.commandsSupport?.outgoingCommands?.[command]
     ) {
-      return chargingStation.stationInfo?.commandsSupport?.outgoingCommands[command] ?? false;
+      return chargingStation.stationInfo?.commandsSupport?.outgoingCommands[command];
     }
     logger.error(`${chargingStation.logPrefix()} Unknown outgoing OCPP command '${command}'`);
     return false;
@@ -109,9 +114,9 @@ export class OCPPServiceUtils {
       return true;
     } else if (
       isIncomingRequestCommand === true &&
-      chargingStation.stationInfo?.commandsSupport?.incomingCommands
+      chargingStation.stationInfo?.commandsSupport?.incomingCommands?.[command]
     ) {
-      return chargingStation.stationInfo?.commandsSupport?.incomingCommands[command] ?? false;
+      return chargingStation.stationInfo?.commandsSupport?.incomingCommands[command];
     }
     logger.error(`${chargingStation.logPrefix()} Unknown incoming OCPP command '${command}'`);
     return false;
@@ -124,8 +129,11 @@ export class OCPPServiceUtils {
     const isMessageTrigger = Object.values(MessageTrigger).includes(messageTrigger);
     if (isMessageTrigger === true && !chargingStation.stationInfo?.messageTriggerSupport) {
       return true;
-    } else if (isMessageTrigger === true && chargingStation.stationInfo?.messageTriggerSupport) {
-      return chargingStation.stationInfo?.messageTriggerSupport[messageTrigger] ?? false;
+    } else if (
+      isMessageTrigger === true &&
+      chargingStation.stationInfo?.messageTriggerSupport?.[messageTrigger]
+    ) {
+      return chargingStation.stationInfo?.messageTriggerSupport[messageTrigger];
     }
     logger.error(
       `${chargingStation.logPrefix()} Unknown incoming OCPP message trigger '${messageTrigger}'`,
@@ -167,7 +175,7 @@ export class OCPPServiceUtils {
     status: ConnectorStatusEnum,
     evseId?: number,
   ): StatusNotificationRequest {
-    switch (chargingStation.stationInfo.ocppVersion ?? OCPPVersion.VERSION_16) {
+    switch (chargingStation.stationInfo?.ocppVersion) {
       case OCPPVersion.VERSION_16:
         return {
           connectorId,
@@ -220,6 +228,10 @@ export class OCPPServiceUtils {
       );
     }
     chargingStation.getConnectorStatus(connectorId)!.status = status;
+    chargingStation.emit(ChargingStationEvents.connectorStatusChanged, {
+      connectorId,
+      ...chargingStation.getConnectorStatus(connectorId),
+    });
   }
 
   public static async isIdTagAuthorized(
@@ -227,12 +239,14 @@ export class OCPPServiceUtils {
     connectorId: number,
     idTag: string,
   ): Promise<boolean> {
-    if (!chargingStation.getLocalAuthListEnabled() && !chargingStation.getRemoteAuthorization()) {
+    if (
+      !chargingStation.getLocalAuthListEnabled() &&
+      !chargingStation.stationInfo?.remoteAuthorization
+    ) {
       logger.warn(
         `${chargingStation.logPrefix()} The charging station expects to authorize RFID tags but nor local authorization nor remote authorization are enabled. Misbehavior may occur`,
       );
     }
-    let authorized = false;
     if (
       chargingStation.getLocalAuthListEnabled() === true &&
       OCPPServiceUtils.isIdTagLocalAuthorized(chargingStation, idTag)
@@ -240,15 +254,11 @@ export class OCPPServiceUtils {
       const connectorStatus: ConnectorStatus = chargingStation.getConnectorStatus(connectorId)!;
       connectorStatus.localAuthorizeIdTag = idTag;
       connectorStatus.idTagLocalAuthorized = true;
-      authorized = true;
-    } else if (chargingStation.getRemoteAuthorization()) {
-      authorized = await OCPPServiceUtils.isIdTagRemoteAuthorized(
-        chargingStation,
-        connectorId,
-        idTag,
-      );
+      return true;
+    } else if (chargingStation.stationInfo?.remoteAuthorization) {
+      return await OCPPServiceUtils.isIdTagRemoteAuthorized(chargingStation, connectorId, idTag);
     }
-    return authorized;
+    return false;
   }
 
   protected static checkConnectorStatusTransition(
@@ -258,7 +268,7 @@ export class OCPPServiceUtils {
   ): boolean {
     const fromStatus = chargingStation.getConnectorStatus(connectorId)!.status;
     let transitionAllowed = false;
-    switch (chargingStation.stationInfo.ocppVersion) {
+    switch (chargingStation.stationInfo?.ocppVersion) {
       case OCPPVersion.VERSION_16:
         if (
           (connectorId === 0 &&
@@ -291,14 +301,13 @@ export class OCPPServiceUtils {
       default:
         throw new BaseError(
           // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
-          `Cannot check connector status transition: OCPP version ${chargingStation.stationInfo.ocppVersion} not supported`,
+          `Cannot check connector status transition: OCPP version ${chargingStation.stationInfo?.ocppVersion} not supported`,
         );
     }
     if (transitionAllowed === false) {
       logger.warn(
-        `${chargingStation.logPrefix()} OCPP ${
-          chargingStation.stationInfo.ocppVersion
-        } connector id ${connectorId} status transition from '${
+        `${chargingStation.logPrefix()} OCPP ${chargingStation.stationInfo
+          ?.ocppVersion} connector id ${connectorId} status transition from '${
           chargingStation.getConnectorStatus(connectorId)!.status
         }' to '${status}' is not allowed`,
       );
@@ -382,7 +391,7 @@ export class OCPPServiceUtils {
         return sampledValueTemplates[index];
       } else if (
         !phase &&
-        !sampledValueTemplates[index].phase &&
+        !sampledValueTemplates[index]?.phase &&
         sampledValueTemplates[index]?.measurand === measurand &&
         getConfigurationKey(
           chargingStation,
@@ -392,8 +401,8 @@ export class OCPPServiceUtils {
         return sampledValueTemplates[index];
       } else if (
         measurand === MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER &&
-        (!sampledValueTemplates[index].measurand ||
-          sampledValueTemplates[index].measurand === measurand)
+        (!sampledValueTemplates[index]?.measurand ||
+          sampledValueTemplates[index]?.measurand === measurand)
       ) {
         return sampledValueTemplates[index];
       }
@@ -409,25 +418,27 @@ export class OCPPServiceUtils {
   }
 
   protected static getLimitFromSampledValueTemplateCustomValue(
-    value: string,
-    limit: number,
-    options: { limitationEnabled?: boolean; unitMultiplier?: number } = {
-      limitationEnabled: true,
-      unitMultiplier: 1,
-    },
+    value: string | undefined,
+    maxLimit: number,
+    minLimit: number,
+    options?: { limitationEnabled?: boolean; fallbackValue?: number; unitMultiplier?: number },
   ): number {
     options = {
       ...{
-        limitationEnabled: true,
+        limitationEnabled: false,
         unitMultiplier: 1,
+        fallbackValue: 0,
       },
       ...options,
     };
-    const parsedInt = parseInt(value);
-    const numberValue = isNaN(parsedInt) ? Infinity : parsedInt;
-    return options?.limitationEnabled
-      ? Math.min(numberValue * options.unitMultiplier!, limit)
-      : numberValue * options.unitMultiplier!;
+    const parsedValue = parseInt(value ?? '');
+    if (options?.limitationEnabled) {
+      return max(
+        min((!isNaN(parsedValue) ? parsedValue : Infinity) * options.unitMultiplier!, maxLimit),
+        minLimit,
+      );
+    }
+    return (!isNaN(parsedValue) ? parsedValue : options.fallbackValue!) * options.unitMultiplier!;
   }
 
   private static isIdTagLocalAuthorized(chargingStation: ChargingStation, idTag: string): boolean {