build(deps): apply updates
[e-mobility-charging-stations-simulator.git] / src / charging-station / ChargingStationWorkerBroadcastChannel.ts
index a67b9423649b1e0f97003bcf909bc67ff6b6e319..c03c4acb81db8aae2a01efee281e5558fbfb3a81 100644 (file)
@@ -1,32 +1,45 @@
-import BaseError from '../exception/BaseError';
-import type OCPPError from '../exception/OCPPError';
 import {
-  HeartbeatRequest,
-  RequestCommand,
-  type StatusNotificationRequest,
-} from '../types/ocpp/Requests';
-import type { HeartbeatResponse, StatusNotificationResponse } from '../types/ocpp/Responses';
+  type ChargingStation,
+  ChargingStationConfigurationUtils,
+  WorkerBroadcastChannel,
+} from './internal';
+import { OCPP16ServiceUtils } from './ocpp';
+import { BaseError, type OCPPError } from '../exception';
 import {
   AuthorizationStatus,
-  AuthorizeRequest,
-  AuthorizeResponse,
-  StartTransactionRequest,
-  StartTransactionResponse,
-  StopTransactionRequest,
-  StopTransactionResponse,
-} from '../types/ocpp/Transaction';
-import {
+  type AuthorizeRequest,
+  type AuthorizeResponse,
+  type BootNotificationRequest,
+  type BootNotificationResponse,
   BroadcastChannelProcedureName,
-  BroadcastChannelRequest,
-  BroadcastChannelRequestPayload,
-  BroadcastChannelResponsePayload,
-  MessageEvent,
-} from '../types/WorkerBroadcastChannel';
-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';
+  type BroadcastChannelRequest,
+  type BroadcastChannelRequestPayload,
+  type BroadcastChannelResponsePayload,
+  type DataTransferRequest,
+  type DataTransferResponse,
+  DataTransferStatus,
+  type DiagnosticsStatusNotificationRequest,
+  type DiagnosticsStatusNotificationResponse,
+  type FirmwareStatusNotificationRequest,
+  type FirmwareStatusNotificationResponse,
+  type HeartbeatRequest,
+  type HeartbeatResponse,
+  type MessageEvent,
+  type MeterValuesRequest,
+  type MeterValuesResponse,
+  RegistrationStatusEnumType,
+  RequestCommand,
+  type RequestParams,
+  ResponseStatus,
+  StandardParametersKey,
+  type StartTransactionRequest,
+  type StartTransactionResponse,
+  type StatusNotificationRequest,
+  type StatusNotificationResponse,
+  type StopTransactionRequest,
+  type StopTransactionResponse,
+} from '../types';
+import { Constants, Utils, logger } from '../utils';
 
 const moduleName = 'ChargingStationWorkerBroadcastChannel';
 
@@ -34,20 +47,27 @@ type CommandResponse =
   | StartTransactionResponse
   | StopTransactionResponse
   | AuthorizeResponse
+  | BootNotificationResponse
   | StatusNotificationResponse
-  | HeartbeatResponse;
+  | HeartbeatResponse
+  | MeterValuesResponse
+  | DataTransferResponse
+  | DiagnosticsStatusNotificationResponse
+  | FirmwareStatusNotificationResponse;
 
 type CommandHandler = (
   requestPayload?: BroadcastChannelRequestPayload
 ) => Promise<CommandResponse | void> | void;
 
-export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChannel {
+export class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChannel {
   private readonly commandHandlers: Map<BroadcastChannelProcedureName, CommandHandler>;
-
   private readonly chargingStation: ChargingStation;
 
   constructor(chargingStation: ChargingStation) {
     super();
+    const requestParams: RequestParams = {
+      throwError: true,
+    };
     this.commandHandlers = new Map<BroadcastChannelProcedureName, CommandHandler>([
       [BroadcastChannelProcedureName.START_CHARGING_STATION, () => this.chargingStation.start()],
       [
@@ -62,13 +82,28 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
         BroadcastChannelProcedureName.CLOSE_CONNECTION,
         () => this.chargingStation.closeWSConnection(),
       ],
+      [
+        BroadcastChannelProcedureName.START_AUTOMATIC_TRANSACTION_GENERATOR,
+        (requestPayload?: BroadcastChannelRequestPayload) =>
+          this.chargingStation.startAutomaticTransactionGenerator(requestPayload?.connectorIds),
+      ],
+      [
+        BroadcastChannelProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR,
+        (requestPayload?: BroadcastChannelRequestPayload) =>
+          this.chargingStation.stopAutomaticTransactionGenerator(requestPayload?.connectorIds),
+      ],
+      [
+        BroadcastChannelProcedureName.SET_SUPERVISION_URL,
+        (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),
+          >(this.chargingStation, RequestCommand.START_TRANSACTION, requestPayload, requestParams),
       ],
       [
         BroadcastChannelProcedureName.STOP_TRANSACTION,
@@ -76,23 +111,18 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
           this.chargingStation.ocppRequestService.requestHandler<
             StopTransactionRequest,
             StartTransactionResponse
-          >(this.chargingStation, RequestCommand.STOP_TRANSACTION, {
-            ...requestPayload,
-            meterStop: this.chargingStation.getEnergyActiveImportRegisterByTransactionId(
-              requestPayload.transactionId,
-              true
-            ),
-          }),
-      ],
-      [
-        BroadcastChannelProcedureName.START_AUTOMATIC_TRANSACTION_GENERATOR,
-        (requestPayload?: BroadcastChannelRequestPayload) =>
-          this.chargingStation.startAutomaticTransactionGenerator(requestPayload.connectorIds),
-      ],
-      [
-        BroadcastChannelProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR,
-        (requestPayload?: BroadcastChannelRequestPayload) =>
-          this.chargingStation.stopAutomaticTransactionGenerator(requestPayload.connectorIds),
+          >(
+            this.chargingStation,
+            RequestCommand.STOP_TRANSACTION,
+            {
+              meterStop: this.chargingStation.getEnergyActiveImportRegisterByTransactionId(
+                requestPayload.transactionId,
+                true
+              ),
+              ...requestPayload,
+            },
+            requestParams
+          ),
       ],
       [
         BroadcastChannelProcedureName.AUTHORIZE,
@@ -100,7 +130,29 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
           this.chargingStation.ocppRequestService.requestHandler<
             AuthorizeRequest,
             AuthorizeResponse
-          >(this.chargingStation, RequestCommand.AUTHORIZE, requestPayload),
+          >(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;
+        },
       ],
       [
         BroadcastChannelProcedureName.STATUS_NOTIFICATION,
@@ -108,7 +160,12 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
           this.chargingStation.ocppRequestService.requestHandler<
             StatusNotificationRequest,
             StatusNotificationResponse
-          >(this.chargingStation, RequestCommand.STATUS_NOTIFICATION, requestPayload),
+          >(
+            this.chargingStation,
+            RequestCommand.STATUS_NOTIFICATION,
+            requestPayload,
+            requestParams
+          ),
       ],
       [
         BroadcastChannelProcedureName.HEARTBEAT,
@@ -116,7 +173,74 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
           this.chargingStation.ocppRequestService.requestHandler<
             HeartbeatRequest,
             HeartbeatResponse
-          >(this.chargingStation, RequestCommand.HEARTBEAT, requestPayload),
+          >(this.chargingStation, RequestCommand.HEARTBEAT, requestPayload, requestParams),
+      ],
+      [
+        BroadcastChannelProcedureName.METER_VALUES,
+        async (requestPayload?: BroadcastChannelRequestPayload) => {
+          const configuredMeterValueSampleInterval =
+            ChargingStationConfigurationUtils.getConfigurationKey(
+              chargingStation,
+              StandardParametersKey.MeterValueSampleInterval
+            );
+          return this.chargingStation.ocppRequestService.requestHandler<
+            MeterValuesRequest,
+            MeterValuesResponse
+          >(
+            this.chargingStation,
+            RequestCommand.METER_VALUES,
+            {
+              meterValue: [
+                // FIXME: Implement OCPP version agnostic helpers
+                OCPP16ServiceUtils.buildMeterValue(
+                  this.chargingStation,
+                  requestPayload.connectorId,
+                  this.chargingStation.getConnectorStatus(requestPayload.connectorId)
+                    ?.transactionId,
+                  configuredMeterValueSampleInterval
+                    ? Utils.convertToInt(configuredMeterValueSampleInterval.value) * 1000
+                    : Constants.DEFAULT_METER_VALUES_INTERVAL
+                ),
+              ],
+              ...requestPayload,
+            },
+            requestParams
+          );
+        },
+      ],
+      [
+        BroadcastChannelProcedureName.DATA_TRANSFER,
+        async (requestPayload?: BroadcastChannelRequestPayload) =>
+          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
+          >(
+            this.chargingStation,
+            RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION,
+            requestPayload,
+            requestParams
+          ),
+      ],
+      [
+        BroadcastChannelProcedureName.FIRMWARE_STATUS_NOTIFICATION,
+        async (requestPayload?: BroadcastChannelRequestPayload) =>
+          this.chargingStation.ocppRequestService.requestHandler<
+            FirmwareStatusNotificationRequest,
+            FirmwareStatusNotificationResponse
+          >(
+            this.chargingStation,
+            RequestCommand.FIRMWARE_STATUS_NOTIFICATION,
+            requestPayload,
+            requestParams
+          ),
       ],
     ]);
     this.chargingStation = chargingStation;
@@ -133,16 +257,15 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
       return;
     }
     const [uuid, command, requestPayload] = validatedMessageEvent.data as BroadcastChannelRequest;
-
     if (
-      requestPayload?.hashIds !== undefined &&
+      !Utils.isNullOrUndefined(requestPayload?.hashIds) &&
       requestPayload?.hashIds?.includes(this.chargingStation.stationInfo.hashId) === false
     ) {
       return;
     }
-    if (requestPayload?.hashId !== undefined) {
+    if (!Utils.isNullOrUndefined(requestPayload?.hashId)) {
       logger.error(
-        `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'hashId' field usage in PDU is deprecated, use 'hashIds' instead`
+        `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'hashId' field usage in PDU is deprecated, use 'hashIds' array instead`
       );
       return;
     }
@@ -150,30 +273,20 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
     let commandResponse: CommandResponse | void;
     try {
       commandResponse = await this.commandHandler(command, requestPayload);
-      if (commandResponse === undefined || commandResponse === null) {
+      if (
+        Utils.isNullOrUndefined(commandResponse) ||
+        Utils.isEmptyObject(commandResponse as CommandResponse)
+      ) {
         responsePayload = {
           hashId: this.chargingStation.stationInfo.hashId,
           status: ResponseStatus.SUCCESS,
         };
       } else {
-        const commandResponseStatus = this.commandResponseToResponseStatus(
+        responsePayload = this.commandResponseToResponsePayload(
           command,
+          requestPayload,
           commandResponse as CommandResponse
         );
-        if (commandResponseStatus === ResponseStatus.SUCCESS) {
-          responsePayload = {
-            hashId: this.chargingStation.stationInfo.hashId,
-            status: commandResponseStatus,
-          };
-        } else {
-          responsePayload = {
-            hashId: this.chargingStation.stationInfo.hashId,
-            status: commandResponseStatus,
-            command,
-            requestPayload,
-            commandResponse: commandResponse as CommandResponse,
-          };
-        }
       }
     } catch (error) {
       logger.error(
@@ -190,14 +303,15 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
         errorStack: (error as Error).stack,
         errorDetails: (error as OCPPError).details,
       };
+    } finally {
+      this.sendResponse([uuid, responsePayload]);
     }
-    this.sendResponse([uuid, responsePayload]);
   }
 
   private messageErrorHandler(messageEvent: MessageEvent): void {
     logger.error(
       `${this.chargingStation.logPrefix()} ${moduleName}.messageErrorHandler: Error at handling message:`,
-      { messageEvent }
+      messageEvent
     );
   }
 
@@ -224,6 +338,27 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
     ].includes(command) === false && delete requestPayload.connectorIds;
   }
 
+  private commandResponseToResponsePayload(
+    command: BroadcastChannelProcedureName,
+    requestPayload: BroadcastChannelRequestPayload,
+    commandResponse: CommandResponse
+  ): BroadcastChannelResponsePayload {
+    const responseStatus = this.commandResponseToResponseStatus(command, commandResponse);
+    if (responseStatus === ResponseStatus.SUCCESS) {
+      return {
+        hashId: this.chargingStation.stationInfo.hashId,
+        status: responseStatus,
+      };
+    }
+    return {
+      hashId: this.chargingStation.stationInfo.hashId,
+      status: responseStatus,
+      command,
+      requestPayload,
+      commandResponse,
+    };
+  }
+
   private commandResponseToResponseStatus(
     command: BroadcastChannelProcedureName,
     commandResponse: CommandResponse
@@ -243,7 +378,18 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
           return ResponseStatus.SUCCESS;
         }
         return ResponseStatus.FAILURE;
+      case BroadcastChannelProcedureName.BOOT_NOTIFICATION:
+        if (commandResponse?.status === RegistrationStatusEnumType.ACCEPTED) {
+          return ResponseStatus.SUCCESS;
+        }
+        return ResponseStatus.FAILURE;
+      case BroadcastChannelProcedureName.DATA_TRANSFER:
+        if (commandResponse?.status === DataTransferStatus.ACCEPTED) {
+          return ResponseStatus.SUCCESS;
+        }
+        return ResponseStatus.FAILURE;
       case BroadcastChannelProcedureName.STATUS_NOTIFICATION:
+      case BroadcastChannelProcedureName.METER_VALUES:
         if (Utils.isEmptyObject(commandResponse) === true) {
           return ResponseStatus.SUCCESS;
         }