build(deps-dev): apply updates
[e-mobility-charging-stations-simulator.git] / src / charging-station / broadcast-channel / ChargingStationWorkerBroadcastChannel.ts
index 5188d974c33d6f65d87812b273731b25e0e789bd..9acdd3a42c94c491587ead7839156d95949ec797 100644 (file)
@@ -1,7 +1,7 @@
-import { secondsToMilliseconds } from 'date-fns';
+import { secondsToMilliseconds } from 'date-fns'
+import { isEmpty } from 'rambda'
 
-import { WorkerBroadcastChannel } from './WorkerBroadcastChannel';
-import { BaseError, type OCPPError } from '../../exception';
+import { BaseError, type OCPPError } from '../../exception/index.js'
 import {
   AuthorizationStatus,
   type AuthorizeRequest,
@@ -35,14 +35,15 @@ import {
   type StatusNotificationRequest,
   type StatusNotificationResponse,
   type StopTransactionRequest,
-  type StopTransactionResponse,
-} from '../../types';
-import { Constants, convertToInt, isEmptyObject, isNullOrUndefined, logger } from '../../utils';
-import type { ChargingStation } from '../ChargingStation';
-import { getConfigurationKey } from '../ConfigurationKeyUtils';
-import { OCPP16ServiceUtils } from '../ocpp';
+  type StopTransactionResponse
+} from '../../types/index.js'
+import { Constants, convertToInt, isAsyncFunction, logger } from '../../utils/index.js'
+import type { ChargingStation } from '../ChargingStation.js'
+import { getConfigurationKey } from '../ConfigurationKeyUtils.js'
+import { buildMeterValue } from '../ocpp/index.js'
+import { WorkerBroadcastChannel } from './WorkerBroadcastChannel.js'
 
-const moduleName = 'ChargingStationWorkerBroadcastChannel';
+const moduleName = 'ChargingStationWorkerBroadcastChannel'
 
 type CommandResponse =
   | EmptyObject
@@ -51,311 +52,339 @@ type CommandResponse =
   | AuthorizeResponse
   | BootNotificationResponse
   | HeartbeatResponse
-  | DataTransferResponse;
+  | DataTransferResponse
 
 type CommandHandler = (
-  requestPayload?: BroadcastChannelRequestPayload,
-) => Promise<CommandResponse | void> | void;
+  requestPayload?: BroadcastChannelRequestPayload
+  // eslint-disable-next-line @typescript-eslint/no-invalid-void-type
+) => Promise<CommandResponse | void> | CommandResponse | void
 
 export class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChannel {
-  private readonly commandHandlers: Map<BroadcastChannelProcedureName, CommandHandler>;
-  private readonly chargingStation: ChargingStation;
+  private readonly commandHandlers: Map<BroadcastChannelProcedureName, CommandHandler>
+  private readonly chargingStation: ChargingStation
 
-  constructor(chargingStation: ChargingStation) {
-    super();
+  constructor (chargingStation: ChargingStation) {
+    super()
     const requestParams: RequestParams = {
-      throwError: true,
-    };
+      throwError: true
+    }
     this.commandHandlers = new Map<BroadcastChannelProcedureName, CommandHandler>([
-      [BroadcastChannelProcedureName.START_CHARGING_STATION, () => this.chargingStation.start()],
+      [
+        BroadcastChannelProcedureName.START_CHARGING_STATION,
+        () => {
+          this.chargingStation.start()
+        }
+      ],
       [
         BroadcastChannelProcedureName.STOP_CHARGING_STATION,
-        async () => this.chargingStation.stop(),
+        async () => {
+          await this.chargingStation.stop()
+        }
+      ],
+      [
+        BroadcastChannelProcedureName.DELETE_CHARGING_STATIONS,
+        async (requestPayload?: BroadcastChannelRequestPayload) => {
+          await this.chargingStation.delete(requestPayload?.deleteConfiguration as boolean)
+        }
       ],
       [
         BroadcastChannelProcedureName.OPEN_CONNECTION,
-        () => this.chargingStation.openWSConnection(),
+        () => {
+          this.chargingStation.openWSConnection()
+        }
       ],
       [
         BroadcastChannelProcedureName.CLOSE_CONNECTION,
-        () => this.chargingStation.closeWSConnection(),
+        () => {
+          this.chargingStation.closeWSConnection()
+        }
       ],
       [
         BroadcastChannelProcedureName.START_AUTOMATIC_TRANSACTION_GENERATOR,
-        (requestPayload?: BroadcastChannelRequestPayload) =>
-          this.chargingStation.startAutomaticTransactionGenerator(requestPayload?.connectorIds),
+        (requestPayload?: BroadcastChannelRequestPayload) => {
+          this.chargingStation.startAutomaticTransactionGenerator(requestPayload?.connectorIds)
+        }
       ],
       [
         BroadcastChannelProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR,
-        (requestPayload?: BroadcastChannelRequestPayload) =>
-          this.chargingStation.stopAutomaticTransactionGenerator(requestPayload?.connectorIds),
+        (requestPayload?: BroadcastChannelRequestPayload) => {
+          this.chargingStation.stopAutomaticTransactionGenerator(requestPayload?.connectorIds)
+        }
       ],
       [
         BroadcastChannelProcedureName.SET_SUPERVISION_URL,
-        (requestPayload?: BroadcastChannelRequestPayload) =>
-          this.chargingStation.setSupervisionUrl(requestPayload?.url as string),
+        (requestPayload?: BroadcastChannelRequestPayload) => {
+          this.chargingStation.setSupervisionUrl(requestPayload?.url as string)
+        }
       ],
       [
         BroadcastChannelProcedureName.START_TRANSACTION,
         async (requestPayload?: BroadcastChannelRequestPayload) =>
-          this.chargingStation.ocppRequestService.requestHandler<
-            StartTransactionRequest,
-            StartTransactionResponse
-          >(this.chargingStation, RequestCommand.START_TRANSACTION, requestPayload, requestParams),
+          await this.chargingStation.ocppRequestService.requestHandler<
+          StartTransactionRequest,
+          StartTransactionResponse
+          >(this.chargingStation, RequestCommand.START_TRANSACTION, requestPayload, requestParams)
       ],
       [
         BroadcastChannelProcedureName.STOP_TRANSACTION,
         async (requestPayload?: BroadcastChannelRequestPayload) =>
-          this.chargingStation.ocppRequestService.requestHandler<
-            StopTransactionRequest,
-            StartTransactionResponse
+          await this.chargingStation.ocppRequestService.requestHandler<
+          StopTransactionRequest,
+          StartTransactionResponse
           >(
             this.chargingStation,
             RequestCommand.STOP_TRANSACTION,
             {
               meterStop: this.chargingStation.getEnergyActiveImportRegisterByTransactionId(
-                requestPayload!.transactionId!,
-                true,
+                requestPayload?.transactionId,
+                true
               ),
-              ...requestPayload,
+              ...requestPayload
             },
-            requestParams,
-          ),
+            requestParams
+          )
       ],
       [
         BroadcastChannelProcedureName.AUTHORIZE,
         async (requestPayload?: BroadcastChannelRequestPayload) =>
-          this.chargingStation.ocppRequestService.requestHandler<
-            AuthorizeRequest,
-            AuthorizeResponse
-          >(this.chargingStation, RequestCommand.AUTHORIZE, requestPayload, requestParams),
+          await this.chargingStation.ocppRequestService.requestHandler<
+          AuthorizeRequest,
+          AuthorizeResponse
+          >(this.chargingStation, RequestCommand.AUTHORIZE, requestPayload, requestParams)
       ],
       [
         BroadcastChannelProcedureName.BOOT_NOTIFICATION,
         async (requestPayload?: BroadcastChannelRequestPayload) => {
-          this.chargingStation.bootNotificationResponse =
-            await this.chargingStation.ocppRequestService.requestHandler<
-              BootNotificationRequest,
-              BootNotificationResponse
-            >(
-              this.chargingStation,
-              RequestCommand.BOOT_NOTIFICATION,
-              {
-                ...this.chargingStation.bootNotificationRequest,
-                ...requestPayload,
-              },
-              {
-                skipBufferingOnError: true,
-                throwError: true,
-              },
-            );
-          return this.chargingStation.bootNotificationResponse;
-        },
+          return await this.chargingStation.ocppRequestService.requestHandler<
+          BootNotificationRequest,
+          BootNotificationResponse
+          >(
+            this.chargingStation,
+            RequestCommand.BOOT_NOTIFICATION,
+            {
+              ...this.chargingStation.bootNotificationRequest,
+              ...requestPayload
+            },
+            {
+              skipBufferingOnError: true,
+              throwError: true
+            }
+          )
+        }
       ],
       [
         BroadcastChannelProcedureName.STATUS_NOTIFICATION,
         async (requestPayload?: BroadcastChannelRequestPayload) =>
-          this.chargingStation.ocppRequestService.requestHandler<
-            StatusNotificationRequest,
-            StatusNotificationResponse
-          >(
-            this.chargingStation,
-            RequestCommand.STATUS_NOTIFICATION,
-            requestPayload,
-            requestParams,
-          ),
+          await this.chargingStation.ocppRequestService.requestHandler<
+          StatusNotificationRequest,
+          StatusNotificationResponse
+          >(this.chargingStation, RequestCommand.STATUS_NOTIFICATION, requestPayload, requestParams)
       ],
       [
         BroadcastChannelProcedureName.HEARTBEAT,
         async (requestPayload?: BroadcastChannelRequestPayload) =>
-          this.chargingStation.ocppRequestService.requestHandler<
-            HeartbeatRequest,
-            HeartbeatResponse
-          >(this.chargingStation, RequestCommand.HEARTBEAT, requestPayload, requestParams),
+          await this.chargingStation.ocppRequestService.requestHandler<
+          HeartbeatRequest,
+          HeartbeatResponse
+          >(this.chargingStation, RequestCommand.HEARTBEAT, requestPayload, requestParams)
       ],
       [
         BroadcastChannelProcedureName.METER_VALUES,
         async (requestPayload?: BroadcastChannelRequestPayload) => {
           const configuredMeterValueSampleInterval = getConfigurationKey(
             chargingStation,
-            StandardParametersKey.MeterValueSampleInterval,
-          );
-          return this.chargingStation.ocppRequestService.requestHandler<
-            MeterValuesRequest,
-            MeterValuesResponse
+            StandardParametersKey.MeterValueSampleInterval
+          )
+          return await this.chargingStation.ocppRequestService.requestHandler<
+          MeterValuesRequest,
+          MeterValuesResponse
           >(
             this.chargingStation,
             RequestCommand.METER_VALUES,
             {
               meterValue: [
-                // FIXME: Implement OCPP version agnostic helpers
-                OCPP16ServiceUtils.buildMeterValue(
+                buildMeterValue(
                   this.chargingStation,
+                  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                   requestPayload!.connectorId!,
+                  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                   this.chargingStation.getConnectorStatus(requestPayload!.connectorId!)!
                     .transactionId!,
-                  configuredMeterValueSampleInterval !== undefined
+                  configuredMeterValueSampleInterval != null
                     ? secondsToMilliseconds(convertToInt(configuredMeterValueSampleInterval.value))
-                    : Constants.DEFAULT_METER_VALUES_INTERVAL,
-                ),
+                    : Constants.DEFAULT_METER_VALUES_INTERVAL
+                )
               ],
-              ...requestPayload,
+              ...requestPayload
             },
-            requestParams,
-          );
-        },
+            requestParams
+          )
+        }
       ],
       [
         BroadcastChannelProcedureName.DATA_TRANSFER,
         async (requestPayload?: BroadcastChannelRequestPayload) =>
-          this.chargingStation.ocppRequestService.requestHandler<
-            DataTransferRequest,
-            DataTransferResponse
-          >(this.chargingStation, RequestCommand.DATA_TRANSFER, requestPayload, requestParams),
+          await this.chargingStation.ocppRequestService.requestHandler<
+          DataTransferRequest,
+          DataTransferResponse
+          >(this.chargingStation, RequestCommand.DATA_TRANSFER, requestPayload, requestParams)
       ],
       [
         BroadcastChannelProcedureName.DIAGNOSTICS_STATUS_NOTIFICATION,
         async (requestPayload?: BroadcastChannelRequestPayload) =>
-          this.chargingStation.ocppRequestService.requestHandler<
-            DiagnosticsStatusNotificationRequest,
-            DiagnosticsStatusNotificationResponse
+          await this.chargingStation.ocppRequestService.requestHandler<
+          DiagnosticsStatusNotificationRequest,
+          DiagnosticsStatusNotificationResponse
           >(
             this.chargingStation,
             RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION,
             requestPayload,
-            requestParams,
-          ),
+            requestParams
+          )
       ],
       [
         BroadcastChannelProcedureName.FIRMWARE_STATUS_NOTIFICATION,
         async (requestPayload?: BroadcastChannelRequestPayload) =>
-          this.chargingStation.ocppRequestService.requestHandler<
-            FirmwareStatusNotificationRequest,
-            FirmwareStatusNotificationResponse
+          await this.chargingStation.ocppRequestService.requestHandler<
+          FirmwareStatusNotificationRequest,
+          FirmwareStatusNotificationResponse
           >(
             this.chargingStation,
             RequestCommand.FIRMWARE_STATUS_NOTIFICATION,
             requestPayload,
-            requestParams,
-          ),
-      ],
-    ]);
-    this.chargingStation = chargingStation;
-    this.onmessage = this.requestHandler.bind(this) as (message: unknown) => void;
-    this.onmessageerror = this.messageErrorHandler.bind(this) as (message: unknown) => void;
+            requestParams
+          )
+      ]
+    ])
+    this.chargingStation = chargingStation
+    this.onmessage = this.requestHandler.bind(this) as (message: unknown) => void
+    this.onmessageerror = this.messageErrorHandler.bind(this) as (message: unknown) => void
   }
 
-  private async requestHandler(messageEvent: MessageEvent): Promise<void> {
-    const validatedMessageEvent = this.validateMessageEvent(messageEvent);
+  private requestHandler (messageEvent: MessageEvent): void {
+    const validatedMessageEvent = this.validateMessageEvent(messageEvent)
     if (validatedMessageEvent === false) {
-      return;
+      return
     }
-    if (this.isResponse(validatedMessageEvent.data) === true) {
-      return;
+    if (this.isResponse(validatedMessageEvent.data)) {
+      return
     }
-    const [uuid, command, requestPayload] = validatedMessageEvent.data as BroadcastChannelRequest;
+    const [uuid, command, requestPayload] = validatedMessageEvent.data as BroadcastChannelRequest
     if (
-      !isNullOrUndefined(requestPayload.hashIds) &&
-      requestPayload.hashIds?.includes(this.chargingStation.stationInfo.hashId) === false
+      requestPayload.hashIds != null &&
+      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+      !requestPayload.hashIds.includes(this.chargingStation.stationInfo!.hashId)
     ) {
-      return;
+      return
     }
-    if (!isNullOrUndefined(requestPayload.hashId)) {
+    if (requestPayload.hashId != null) {
       logger.error(
-        `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'hashId' field usage in PDU is deprecated, use 'hashIds' array instead`,
-      );
-      return;
+        `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'hashId' field usage in PDU is deprecated, use 'hashIds' array instead`
+      )
+      return
     }
-    let responsePayload: BroadcastChannelResponsePayload | undefined;
-    let commandResponse: CommandResponse | void | undefined;
-    try {
-      commandResponse = await this.commandHandler(command, requestPayload);
-      if (isNullOrUndefined(commandResponse) || isEmptyObject(commandResponse as CommandResponse)) {
+    let responsePayload: BroadcastChannelResponsePayload | undefined
+    this.commandHandler(command, requestPayload)
+      .then(commandResponse => {
+        if (commandResponse == null || isEmpty(commandResponse)) {
+          responsePayload = {
+            hashId: this.chargingStation.stationInfo?.hashId,
+            status: ResponseStatus.SUCCESS
+          }
+        } else {
+          responsePayload = this.commandResponseToResponsePayload(
+            command,
+            requestPayload,
+            commandResponse
+          )
+        }
+      })
+      .catch((error: unknown) => {
+        logger.error(
+          `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: Handle request error:`,
+          error
+        )
         responsePayload = {
-          hashId: this.chargingStation.stationInfo.hashId,
-          status: ResponseStatus.SUCCESS,
-        };
-      } else {
-        responsePayload = this.commandResponseToResponsePayload(
+          hashId: this.chargingStation.stationInfo?.hashId,
+          status: ResponseStatus.FAILURE,
           command,
           requestPayload,
-          commandResponse as CommandResponse,
-        );
-      }
-    } catch (error) {
-      logger.error(
-        `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: Handle request error:`,
-        error,
-      );
-      responsePayload = {
-        hashId: this.chargingStation.stationInfo.hashId,
-        status: ResponseStatus.FAILURE,
-        command,
-        requestPayload,
-        commandResponse: commandResponse as CommandResponse,
-        errorMessage: (error as Error).message,
-        errorStack: (error as Error).stack,
-        errorDetails: (error as OCPPError).details,
-      };
-    } finally {
-      this.sendResponse([uuid, responsePayload!]);
-    }
+          errorMessage: (error as OCPPError).message,
+          errorStack: (error as OCPPError).stack,
+          errorDetails: (error as OCPPError).details
+        } satisfies BroadcastChannelResponsePayload
+      })
+      .finally(() => {
+        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+        this.sendResponse([uuid, responsePayload!])
+      })
   }
 
-  private messageErrorHandler(messageEvent: MessageEvent): void {
+  private messageErrorHandler (messageEvent: MessageEvent): void {
     logger.error(
       `${this.chargingStation.logPrefix()} ${moduleName}.messageErrorHandler: Error at handling message:`,
-      messageEvent,
-    );
+      messageEvent
+    )
   }
 
-  private async commandHandler(
+  private async commandHandler (
     command: BroadcastChannelProcedureName,
-    requestPayload: BroadcastChannelRequestPayload,
+    requestPayload: BroadcastChannelRequestPayload
+    // eslint-disable-next-line @typescript-eslint/no-invalid-void-type
   ): Promise<CommandResponse | void> {
-    if (this.commandHandlers.has(command) === true) {
-      this.cleanRequestPayload(command, requestPayload);
-      return this.commandHandlers.get(command)!(requestPayload);
+    if (this.commandHandlers.has(command)) {
+      this.cleanRequestPayload(command, requestPayload)
+      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+      const commandHandler = this.commandHandlers.get(command)!
+      if (isAsyncFunction(commandHandler)) {
+        return await commandHandler(requestPayload)
+      }
+      return (
+        commandHandler as (
+          requestPayload?: BroadcastChannelRequestPayload
+          // eslint-disable-next-line @typescript-eslint/no-invalid-void-type
+        ) => CommandResponse | void
+      )(requestPayload)
     }
-    throw new BaseError(`Unknown worker broadcast channel command: '${command}'`);
+    throw new BaseError(`Unknown worker broadcast channel command: '${command}'`)
   }
 
-  private cleanRequestPayload(
+  private cleanRequestPayload (
     command: BroadcastChannelProcedureName,
-    requestPayload: BroadcastChannelRequestPayload,
+    requestPayload: BroadcastChannelRequestPayload
   ): void {
-    delete requestPayload.hashId;
-    delete requestPayload.hashIds;
-    [
+    delete requestPayload.hashId
+    delete requestPayload.hashIds
+    ![
       BroadcastChannelProcedureName.START_AUTOMATIC_TRANSACTION_GENERATOR,
-      BroadcastChannelProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR,
-    ].includes(command) === false && delete requestPayload.connectorIds;
+      BroadcastChannelProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR
+    ].includes(command) && delete requestPayload.connectorIds
   }
 
-  private commandResponseToResponsePayload(
+  private commandResponseToResponsePayload (
     command: BroadcastChannelProcedureName,
     requestPayload: BroadcastChannelRequestPayload,
-    commandResponse: CommandResponse,
+    commandResponse: CommandResponse
   ): BroadcastChannelResponsePayload {
-    const responseStatus = this.commandResponseToResponseStatus(command, commandResponse);
+    const responseStatus = this.commandResponseToResponseStatus(command, commandResponse)
     if (responseStatus === ResponseStatus.SUCCESS) {
       return {
-        hashId: this.chargingStation.stationInfo.hashId,
-        status: responseStatus,
-      };
+        hashId: this.chargingStation.stationInfo?.hashId,
+        status: responseStatus
+      }
     }
     return {
-      hashId: this.chargingStation.stationInfo.hashId,
+      hashId: this.chargingStation.stationInfo?.hashId,
       status: responseStatus,
       command,
       requestPayload,
-      commandResponse,
-    };
+      commandResponse
+    }
   }
 
-  private commandResponseToResponseStatus(
+  private commandResponseToResponseStatus (
     command: BroadcastChannelProcedureName,
-    commandResponse: CommandResponse,
+    commandResponse: CommandResponse
   ): ResponseStatus {
     switch (command) {
       case BroadcastChannelProcedureName.START_TRANSACTION:
@@ -367,34 +396,34 @@ export class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChanne
               | StartTransactionResponse
               | StopTransactionResponse
               | AuthorizeResponse
-          )?.idTagInfo?.status === AuthorizationStatus.ACCEPTED
+          ).idTagInfo?.status === AuthorizationStatus.ACCEPTED
         ) {
-          return ResponseStatus.SUCCESS;
+          return ResponseStatus.SUCCESS
         }
-        return ResponseStatus.FAILURE;
+        return ResponseStatus.FAILURE
       case BroadcastChannelProcedureName.BOOT_NOTIFICATION:
-        if (commandResponse?.status === RegistrationStatusEnumType.ACCEPTED) {
-          return ResponseStatus.SUCCESS;
+        if (commandResponse.status === RegistrationStatusEnumType.ACCEPTED) {
+          return ResponseStatus.SUCCESS
         }
-        return ResponseStatus.FAILURE;
+        return ResponseStatus.FAILURE
       case BroadcastChannelProcedureName.DATA_TRANSFER:
-        if (commandResponse?.status === DataTransferStatus.ACCEPTED) {
-          return ResponseStatus.SUCCESS;
+        if (commandResponse.status === DataTransferStatus.ACCEPTED) {
+          return ResponseStatus.SUCCESS
         }
-        return ResponseStatus.FAILURE;
+        return ResponseStatus.FAILURE
       case BroadcastChannelProcedureName.STATUS_NOTIFICATION:
       case BroadcastChannelProcedureName.METER_VALUES:
-        if (isEmptyObject(commandResponse) === true) {
-          return ResponseStatus.SUCCESS;
+        if (isEmpty(commandResponse)) {
+          return ResponseStatus.SUCCESS
         }
-        return ResponseStatus.FAILURE;
+        return ResponseStatus.FAILURE
       case BroadcastChannelProcedureName.HEARTBEAT:
         if ('currentTime' in commandResponse) {
-          return ResponseStatus.SUCCESS;
+          return ResponseStatus.SUCCESS
         }
-        return ResponseStatus.FAILURE;
+        return ResponseStatus.FAILURE
       default:
-        return ResponseStatus.FAILURE;
+        return ResponseStatus.FAILURE
     }
   }
 }