Simplify stop transaction payload handling
[e-mobility-charging-stations-simulator.git] / src / charging-station / ChargingStationWorkerBroadcastChannel.ts
index 9e88b4189372eaa030de37cfe159e4cdb3a89044..dfb50ab1dd67d318f251ed88a953aae67011e276 100644 (file)
@@ -1,10 +1,11 @@
 import BaseError from '../exception/BaseError';
-import { RequestCommand } from '../types/ocpp/Requests';
+import type OCPPError from '../exception/OCPPError';
+import { RequestCommand, type StatusNotificationRequest } from '../types/ocpp/Requests';
+import type { StatusNotificationResponse } from '../types/ocpp/Responses';
 import {
   AuthorizationStatus,
   StartTransactionRequest,
   StartTransactionResponse,
-  StopTransactionReason,
   StopTransactionRequest,
   StopTransactionResponse,
 } from '../types/ocpp/Transaction';
@@ -15,14 +16,18 @@ import {
   BroadcastChannelResponsePayload,
   MessageEvent,
 } from '../types/WorkerBroadcastChannel';
-import { ResponseStatus } from '../ui/web/src/type/UIProtocol';
+import { ResponseStatus } from '../ui/web/src/types/UIProtocol';
 import logger from '../utils/Logger';
+import Utils from '../utils/Utils';
 import type ChargingStation from './ChargingStation';
 import WorkerBroadcastChannel from './WorkerBroadcastChannel';
 
 const moduleName = 'ChargingStationWorkerBroadcastChannel';
 
-type CommandResponse = StartTransactionResponse | StopTransactionResponse;
+type CommandResponse =
+  | StartTransactionResponse
+  | StopTransactionResponse
+  | StatusNotificationResponse;
 
 export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChannel {
   private readonly chargingStation: ChargingStation;
@@ -35,17 +40,30 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
   }
 
   private async requestHandler(messageEvent: MessageEvent): Promise<void> {
-    if (this.isResponse(messageEvent.data)) {
+    if (this.isResponse(messageEvent.data) === true) {
       return;
     }
-    if (Array.isArray(messageEvent.data) === false) {
-      throw new BaseError('Worker broadcast channel protocol request is not an array');
-    }
-
-    const [uuid, command, requestPayload] = messageEvent.data as BroadcastChannelRequest;
+    const [uuid, command, requestPayload] = this.validateMessageEvent(messageEvent)
+      .data as BroadcastChannelRequest;
 
-    if (requestPayload?.hashId !== this.chargingStation.hashId) {
-      return;
+    if (requestPayload?.hashIds !== undefined || requestPayload?.hashId !== undefined) {
+      if (
+        requestPayload?.hashId === undefined &&
+        requestPayload?.hashIds?.includes(this.chargingStation.stationInfo.hashId) === false
+      ) {
+        return;
+      }
+      if (
+        requestPayload?.hashIds === undefined &&
+        requestPayload?.hashId !== this.chargingStation.stationInfo.hashId
+      ) {
+        return;
+      }
+      if (requestPayload?.hashId !== undefined) {
+        logger.warn(
+          `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'hashId' field usage in PDU is deprecated, use 'hashIds' instead`
+        );
+      }
     }
 
     let responsePayload: BroadcastChannelResponsePayload;
@@ -53,9 +71,15 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
     try {
       commandResponse = await this.commandHandler(command, requestPayload);
       if (commandResponse === undefined) {
-        responsePayload = { status: ResponseStatus.SUCCESS };
+        responsePayload = {
+          hashId: this.chargingStation.stationInfo.hashId,
+          status: ResponseStatus.SUCCESS,
+        };
       } else {
-        responsePayload = { status: this.commandResponseToResponseStatus(commandResponse) };
+        responsePayload = {
+          hashId: this.chargingStation.stationInfo.hashId,
+          status: this.commandResponseToResponseStatus(commandResponse),
+        };
       }
     } catch (error) {
       logger.error(
@@ -63,12 +87,14 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
         error
       );
       responsePayload = {
+        hashId: this.chargingStation.stationInfo.hashId,
         status: ResponseStatus.FAILURE,
         command,
         requestPayload,
         commandResponse,
         errorMessage: (error as Error).message,
         errorStack: (error as Error).stack,
+        errorDetails: (error as OCPPError).details,
       };
     }
     this.sendResponse([uuid, responsePayload]);
@@ -77,7 +103,7 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
   private messageErrorHandler(messageEvent: MessageEvent): void {
     logger.error(
       `${this.chargingStation.logPrefix()} ${moduleName}.messageErrorHandler: Error at handling message:`,
-      { messageEvent, messageEventData: messageEvent.data }
+      { messageEvent }
     );
   }
 
@@ -86,6 +112,18 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
     requestPayload: BroadcastChannelRequestPayload
   ): Promise<CommandResponse | undefined> {
     switch (command) {
+      case BroadcastChannelProcedureName.START_CHARGING_STATION:
+        this.chargingStation.start();
+        break;
+      case BroadcastChannelProcedureName.STOP_CHARGING_STATION:
+        await this.chargingStation.stop();
+        break;
+      case BroadcastChannelProcedureName.OPEN_CONNECTION:
+        this.chargingStation.openWSConnection();
+        break;
+      case BroadcastChannelProcedureName.CLOSE_CONNECTION:
+        this.chargingStation.closeWSConnection();
+        break;
       case BroadcastChannelProcedureName.START_TRANSACTION:
         return this.chargingStation.ocppRequestService.requestHandler<
           StartTransactionRequest,
@@ -101,23 +139,33 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
         >(this.chargingStation, RequestCommand.STOP_TRANSACTION, {
           transactionId: requestPayload.transactionId,
           meterStop: this.chargingStation.getEnergyActiveImportRegisterByTransactionId(
-            requestPayload.transactionId
+            requestPayload.transactionId,
+            true
           ),
-          idTag: this.chargingStation.getTransactionIdTag(requestPayload.transactionId),
-          reason: StopTransactionReason.NONE,
+          idTag: requestPayload.idTag,
+          reason: requestPayload.reason,
         });
-      case BroadcastChannelProcedureName.START_CHARGING_STATION:
-        this.chargingStation.start();
-        break;
-      case BroadcastChannelProcedureName.STOP_CHARGING_STATION:
-        await this.chargingStation.stop();
+      case BroadcastChannelProcedureName.START_AUTOMATIC_TRANSACTION_GENERATOR:
+        this.chargingStation.startAutomaticTransactionGenerator(requestPayload.connectorIds);
         break;
-      case BroadcastChannelProcedureName.OPEN_CONNECTION:
-        this.chargingStation.openWSConnection();
-        break;
-      case BroadcastChannelProcedureName.CLOSE_CONNECTION:
-        this.chargingStation.closeWSConnection();
+      case BroadcastChannelProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR:
+        this.chargingStation.stopAutomaticTransactionGenerator(requestPayload.connectorIds);
         break;
+      case BroadcastChannelProcedureName.STATUS_NOTIFICATION:
+        return this.chargingStation.ocppRequestService.requestHandler<
+          StatusNotificationRequest,
+          StatusNotificationResponse
+        >(this.chargingStation, RequestCommand.STATUS_NOTIFICATION, {
+          connectorId: requestPayload.connectorId,
+          errorCode: requestPayload.errorCode,
+          status: requestPayload.status,
+          ...(requestPayload.info && { info: requestPayload.info }),
+          ...(requestPayload.timestamp && { timestamp: requestPayload.timestamp }),
+          ...(requestPayload.vendorId && { vendorId: requestPayload.vendorId }),
+          ...(requestPayload.vendorErrorCode && {
+            vendorErrorCode: requestPayload.vendorErrorCode,
+          }),
+        });
       default:
         // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
         throw new BaseError(`Unknown worker broadcast channel command: ${command}`);
@@ -125,7 +173,10 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
   }
 
   private commandResponseToResponseStatus(commandResponse: CommandResponse): ResponseStatus {
-    if (commandResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED) {
+    if (
+      Utils.isEmptyObject(commandResponse) ||
+      commandResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED
+    ) {
       return ResponseStatus.SUCCESS;
     }
     return ResponseStatus.FAILURE;