UI Protocol: Build response with the global excecution status on each
authorJérôme Benoit <jerome.benoit@sap.com>
Fri, 2 Sep 2022 14:59:06 +0000 (16:59 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Fri, 2 Sep 2022 14:59:06 +0000 (16:59 +0200)
charging station

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
src/charging-station/UIServiceWorkerBroadcastChannel.ts
src/charging-station/ui-server/AbstractUIServer.ts
src/charging-station/ui-server/UIHttpServer.ts
src/charging-station/ui-server/UIWebSocketServer.ts
src/charging-station/ui-server/ui-services/AbstractUIService.ts
src/charging-station/ui-server/ui-services/UIService001.ts
src/types/UIProtocol.ts

index c64a7ccc2212d021189b373278ad8d5a804961bc..5986a051a1b0480c6ed8d59386095f31ccd13738 100644 (file)
@@ -1,19 +1,31 @@
-import type { ResponsePayload } from '../types/UIProtocol';
-import type { BroadcastChannelResponse, MessageEvent } from '../types/WorkerBroadcastChannel';
+import { ResponsePayload, ResponseStatus } from '../types/UIProtocol';
+import type {
+  BroadcastChannelResponse,
+  BroadcastChannelResponsePayload,
+  MessageEvent,
+} from '../types/WorkerBroadcastChannel';
 import logger from '../utils/Logger';
 import type AbstractUIService from './ui-server/ui-services/AbstractUIService';
 import WorkerBroadcastChannel from './WorkerBroadcastChannel';
 
 const moduleName = 'UIServiceWorkerBroadcastChannel';
 
+type Responses = {
+  responsesExpected: number;
+  responsesReceived: number;
+  responses: BroadcastChannelResponsePayload[];
+};
+
 export default class UIServiceWorkerBroadcastChannel extends WorkerBroadcastChannel {
-  private uiService: AbstractUIService;
+  private readonly uiService: AbstractUIService;
+  private responses: Map<string, Responses>;
 
   constructor(uiService: AbstractUIService) {
     super();
     this.uiService = uiService;
     this.onmessage = this.responseHandler.bind(this) as (message: MessageEvent) => void;
     this.onmessageerror = this.messageErrorHandler.bind(this) as (message: MessageEvent) => void;
+    this.responses = new Map<string, Responses>();
   }
 
   private responseHandler(messageEvent: MessageEvent): void {
@@ -22,10 +34,56 @@ export default class UIServiceWorkerBroadcastChannel extends WorkerBroadcastChan
     }
     this.validateMessageEvent(messageEvent);
     const [uuid, responsePayload] = messageEvent.data as BroadcastChannelResponse;
-    // TODO: handle multiple responses for the same uuid
-    delete responsePayload.hashId;
+    if (this.responses.has(uuid) === false) {
+      this.responses.set(uuid, {
+        responsesExpected: this.uiService.getBroadcastChannelExpectedResponses(uuid),
+        responsesReceived: 1,
+        responses: [responsePayload],
+      });
+    } else if (
+      this.responses.get(uuid)?.responsesReceived + 1 <
+      this.responses.get(uuid)?.responsesExpected
+    ) {
+      this.responses.get(uuid).responsesReceived++;
+      this.responses.get(uuid).responses.push(responsePayload);
+    } else if (
+      this.responses.get(uuid)?.responsesReceived + 1 ===
+      this.responses.get(uuid)?.responsesExpected
+    ) {
+      this.responses.get(uuid).responses.push(responsePayload);
+      this.uiService.sendResponse(uuid, this.buildResponsePayload(uuid));
+      this.responses.delete(uuid);
+      this.uiService.deleteBroadcastChannelRequest(uuid);
+    }
+  }
 
-    this.uiService.sendResponse(uuid, responsePayload as ResponsePayload);
+  private buildResponsePayload(uuid: string): ResponsePayload {
+    const responsesStatus = this.responses
+      .get(uuid)
+      ?.responses.every((response) => response.status === ResponseStatus.SUCCESS)
+      ? ResponseStatus.SUCCESS
+      : ResponseStatus.FAILURE;
+    return {
+      status: responsesStatus,
+      hashIdsSucceeded: this.responses
+        .get(uuid)
+        ?.responses.map(({ status, hashId }) => {
+          if (status === ResponseStatus.SUCCESS) {
+            return hashId;
+          }
+        })
+        .filter((hashId) => hashId !== undefined),
+      ...(responsesStatus === ResponseStatus.FAILURE && {
+        hashIdsFailed: this.responses
+          .get(uuid)
+          ?.responses.map(({ status, hashId }) => {
+            if (status === ResponseStatus.FAILURE) {
+              return hashId;
+            }
+          })
+          .filter((hashId) => hashId !== undefined),
+      }),
+    };
   }
 
   private messageErrorHandler(messageEvent: MessageEvent): void {
index 902b6bd5d70bcd4152f6bcabb7eae33fc11787b4..91c69a76765b835447988ba46e106ed87829caf2 100644 (file)
@@ -39,5 +39,5 @@ export abstract class AbstractUIServer {
   public abstract stop(): void;
   public abstract sendRequest(request: string): void;
   public abstract sendResponse(response: string): void;
-  public abstract logPrefix(modName?: string, methodName?: string): string;
+  public abstract logPrefix(modName?: string, methodName?: string, prefixSuffix?: string): string;
 }
index de4061106619d4985365d778ee959547fa080c6b..98c3f1cc7f27c9d42f35efdb82b19664a9a24975 100644 (file)
@@ -59,14 +59,15 @@ export default class UIHttpServer extends AbstractUIServer {
       this.responseHandlers.delete(uuid);
     } else {
       logger.error(
-        `${this.logPrefix()} ${moduleName}.sendResponse: Response for unknown request: ${response}`
+        `${this.logPrefix(moduleName, 'sendResponse')} Response for unknown request: ${response}`
       );
     }
   }
 
-  public logPrefix(modName?: string, methodName?: string): string {
+  public logPrefix(modName?: string, methodName?: string, prefixSuffix?: string): string {
+    const logMsgPrefix = prefixSuffix ? `UI HTTP Server ${prefixSuffix}` : 'UI HTTP Server';
     const logMsg =
-      modName && methodName ? ` UI HTTP Server | ${modName}.${methodName}:` : ' UI HTTP Server |';
+      modName && methodName ? ` ${logMsgPrefix} | ${modName}.${methodName}:` : ` ${logMsgPrefix} |`;
     return Utils.logPrefix(logMsg);
   }
 
index 85e2d669cf272e280ec1f369cdd65e5d62c1163d..8e11b66da55e09c5b89dea39a998ed0135a3789d 100644 (file)
@@ -65,11 +65,12 @@ export default class UIWebSocketServer extends AbstractUIServer {
     this.broadcastToClients(response);
   }
 
-  public logPrefix(modName?: string, methodName?: string): string {
+  public logPrefix(modName?: string, methodName?: string, prefixSuffix?: string): string {
+    const logMsgPrefix = prefixSuffix
+      ? `UI WebSocket Server ${prefixSuffix}`
+      : 'UI WebSocket Server';
     const logMsg =
-      modName && methodName
-        ? ` UI WebSocket Server | ${modName}.${methodName}:`
-        : ' UI WebSocket Server |';
+      modName && methodName ? ` ${logMsgPrefix} | ${modName}.${methodName}:` : ` ${logMsgPrefix} |`;
     return Utils.logPrefix(logMsg);
   }
 
index 1e0f2a92e0a9f3189f969428c6eb8fda59a7837a..9120f19976e8a682afd136afc1d469ee0af2fe30 100644 (file)
@@ -12,7 +12,12 @@ import {
   ResponsePayload,
   ResponseStatus,
 } from '../../../types/UIProtocol';
+import type {
+  BroadcastChannelProcedureName,
+  BroadcastChannelRequestPayload,
+} from '../../../types/WorkerBroadcastChannel';
 import logger from '../../../utils/Logger';
+import Utils from '../../../utils/Utils';
 import UIServiceWorkerBroadcastChannel from '../../UIServiceWorkerBroadcastChannel';
 import type { AbstractUIServer } from '../AbstractUIServer';
 
@@ -22,7 +27,8 @@ export default abstract class AbstractUIService {
   protected readonly version: ProtocolVersion;
   protected readonly uiServer: AbstractUIServer;
   protected readonly requestHandlers: Map<ProcedureName, ProtocolRequestHandler>;
-  protected uiServiceWorkerBroadcastChannel: UIServiceWorkerBroadcastChannel;
+  private uiServiceWorkerBroadcastChannel: UIServiceWorkerBroadcastChannel;
+  private readonly broadcastChannelRequests: Map<string, number>;
 
   constructor(uiServer: AbstractUIServer, version: ProtocolVersion) {
     this.version = version;
@@ -33,6 +39,7 @@ export default abstract class AbstractUIService {
       [ProcedureName.STOP_SIMULATOR, this.handleStopSimulator.bind(this)],
     ]);
     this.uiServiceWorkerBroadcastChannel = new UIServiceWorkerBroadcastChannel(this);
+    this.broadcastChannelRequests = new Map<string, number>();
   }
 
   public async requestHandler(request: RawData | JsonType): Promise<void> {
@@ -67,9 +74,8 @@ export default abstract class AbstractUIService {
         errorStack: (error as Error).stack,
       };
     }
-
+    // Send response for payload not forwarded to broadcast channel
     if (responsePayload !== undefined) {
-      // Send the response
       this.sendResponse(messageId ?? 'error', responsePayload);
     }
   }
@@ -89,7 +95,42 @@ export default abstract class AbstractUIService {
   }
 
   public logPrefix(modName: string, methodName: string): string {
-    return this.uiServer.logPrefix(modName, methodName);
+    return this.uiServer.logPrefix(modName, methodName, this.version);
+  }
+
+  public deleteBroadcastChannelRequest(uuid: string): void {
+    this.broadcastChannelRequests.delete(uuid);
+  }
+
+  public getBroadcastChannelExpectedResponses(uuid: string): number {
+    return this.broadcastChannelRequests.get(uuid) ?? 0;
+  }
+
+  protected sendBroadcastChannelRequest(
+    uuid: string,
+    procedureName: BroadcastChannelProcedureName,
+    payload: BroadcastChannelRequestPayload
+  ): void {
+    if (!Utils.isEmptyArray(payload.hashIds)) {
+      payload.hashIds = payload.hashIds
+        .map((hashId) => {
+          if (this.uiServer.chargingStations.has(hashId) === true) {
+            return hashId;
+          }
+          logger.warn(
+            `${this.logPrefix(
+              moduleName,
+              'sendBroadcastChannelRequest'
+            )} Charging station with hashId '${hashId}' not found`
+          );
+        })
+        .filter((hashId) => hashId !== undefined);
+    }
+    const expectedNumberOfResponses = !Utils.isEmptyArray(payload.hashIds)
+      ? payload.hashIds.length
+      : this.uiServer.chargingStations.size;
+    this.broadcastChannelRequests.set(uuid, expectedNumberOfResponses);
+    this.uiServiceWorkerBroadcastChannel.sendRequest([uuid, procedureName, payload]);
   }
 
   // Validate the raw data received from the UI server
index 8176772b54bd008ec587cda37546d72f8f611b91..4740b7d738e2c99829232ba86dcfe9326648c571 100644 (file)
@@ -4,10 +4,7 @@ import {
   ProtocolVersion,
   RequestPayload,
 } from '../../../types/UIProtocol';
-import {
-  BroadcastChannelProcedureName,
-  BroadcastChannelRequestPayload,
-} from '../../../types/WorkerBroadcastChannel';
+import { BroadcastChannelProcedureName } from '../../../types/WorkerBroadcastChannel';
 import type { AbstractUIServer } from '../AbstractUIServer';
 import AbstractUIService from './AbstractUIService';
 
@@ -49,66 +46,54 @@ export default class UIService001 extends AbstractUIService {
   }
 
   private handleStartChargingStation(uuid: string, payload: RequestPayload): void {
-    this.uiServiceWorkerBroadcastChannel.sendRequest([
+    this.sendBroadcastChannelRequest(
       uuid,
       BroadcastChannelProcedureName.START_CHARGING_STATION,
-      payload as BroadcastChannelRequestPayload,
-    ]);
+      payload
+    );
   }
 
   private handleStopChargingStation(uuid: string, payload: RequestPayload): void {
-    this.uiServiceWorkerBroadcastChannel.sendRequest([
+    this.sendBroadcastChannelRequest(
       uuid,
       BroadcastChannelProcedureName.STOP_CHARGING_STATION,
-      payload as BroadcastChannelRequestPayload,
-    ]);
+      payload
+    );
   }
 
   private handleOpenConnection(uuid: string, payload: RequestPayload): void {
-    this.uiServiceWorkerBroadcastChannel.sendRequest([
-      uuid,
-      BroadcastChannelProcedureName.OPEN_CONNECTION,
-      payload as BroadcastChannelRequestPayload,
-    ]);
+    this.sendBroadcastChannelRequest(uuid, BroadcastChannelProcedureName.OPEN_CONNECTION, payload);
   }
 
   private handleCloseConnection(uuid: string, payload: RequestPayload): void {
-    this.uiServiceWorkerBroadcastChannel.sendRequest([
-      uuid,
-      BroadcastChannelProcedureName.CLOSE_CONNECTION,
-      payload as BroadcastChannelRequestPayload,
-    ]);
+    this.sendBroadcastChannelRequest(uuid, BroadcastChannelProcedureName.CLOSE_CONNECTION, payload);
   }
 
   private handleStartTransaction(uuid: string, payload: RequestPayload): void {
-    this.uiServiceWorkerBroadcastChannel.sendRequest([
+    this.sendBroadcastChannelRequest(
       uuid,
       BroadcastChannelProcedureName.START_TRANSACTION,
-      payload as BroadcastChannelRequestPayload,
-    ]);
+      payload
+    );
   }
 
   private handleStopTransaction(uuid: string, payload: RequestPayload): void {
-    this.uiServiceWorkerBroadcastChannel.sendRequest([
-      uuid,
-      BroadcastChannelProcedureName.STOP_TRANSACTION,
-      payload as BroadcastChannelRequestPayload,
-    ]);
+    this.sendBroadcastChannelRequest(uuid, BroadcastChannelProcedureName.STOP_TRANSACTION, payload);
   }
 
   private handleStartAutomaticTransactionGenerator(uuid: string, payload: RequestPayload): void {
-    this.uiServiceWorkerBroadcastChannel.sendRequest([
+    this.sendBroadcastChannelRequest(
       uuid,
       BroadcastChannelProcedureName.START_AUTOMATIC_TRANSACTION_GENERATOR,
-      payload as BroadcastChannelRequestPayload,
-    ]);
+      payload
+    );
   }
 
   private handleStopAutomaticTransactionGenerator(uuid: string, payload: RequestPayload): void {
-    this.uiServiceWorkerBroadcastChannel.sendRequest([
+    this.sendBroadcastChannelRequest(
       uuid,
       BroadcastChannelProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR,
-      payload as BroadcastChannelRequestPayload,
-    ]);
+      payload
+    );
   }
 }
index f120f9c9e4dcf3fb5b5be68f8fe7ae762a0f235e..1f78671e2f6a4901c935b41736f1688459c2ff65 100644 (file)
@@ -47,5 +47,6 @@ export enum ResponseStatus {
 
 export interface ResponsePayload extends JsonObject {
   status: ResponseStatus;
-  hashIds?: string[];
+  hashIdsSucceeded?: string[];
+  hashIdsFailed?: string[];
 }