From 0d2cec7666c01396c8125a752341d5b716d12906 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Fri, 2 Sep 2022 16:59:06 +0200 Subject: [PATCH] UI Protocol: Build response with the global excecution status on each charging station MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- .../UIServiceWorkerBroadcastChannel.ts | 70 +++++++++++++++++-- .../ui-server/AbstractUIServer.ts | 2 +- .../ui-server/UIHttpServer.ts | 7 +- .../ui-server/UIWebSocketServer.ts | 9 +-- .../ui-services/AbstractUIService.ts | 49 +++++++++++-- .../ui-server/ui-services/UIService001.ts | 53 +++++--------- src/types/UIProtocol.ts | 3 +- 7 files changed, 140 insertions(+), 53 deletions(-) diff --git a/src/charging-station/UIServiceWorkerBroadcastChannel.ts b/src/charging-station/UIServiceWorkerBroadcastChannel.ts index c64a7ccc..5986a051 100644 --- a/src/charging-station/UIServiceWorkerBroadcastChannel.ts +++ b/src/charging-station/UIServiceWorkerBroadcastChannel.ts @@ -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; 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(); } 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 { diff --git a/src/charging-station/ui-server/AbstractUIServer.ts b/src/charging-station/ui-server/AbstractUIServer.ts index 902b6bd5..91c69a76 100644 --- a/src/charging-station/ui-server/AbstractUIServer.ts +++ b/src/charging-station/ui-server/AbstractUIServer.ts @@ -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; } diff --git a/src/charging-station/ui-server/UIHttpServer.ts b/src/charging-station/ui-server/UIHttpServer.ts index de406110..98c3f1cc 100644 --- a/src/charging-station/ui-server/UIHttpServer.ts +++ b/src/charging-station/ui-server/UIHttpServer.ts @@ -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); } diff --git a/src/charging-station/ui-server/UIWebSocketServer.ts b/src/charging-station/ui-server/UIWebSocketServer.ts index 85e2d669..8e11b66d 100644 --- a/src/charging-station/ui-server/UIWebSocketServer.ts +++ b/src/charging-station/ui-server/UIWebSocketServer.ts @@ -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); } diff --git a/src/charging-station/ui-server/ui-services/AbstractUIService.ts b/src/charging-station/ui-server/ui-services/AbstractUIService.ts index 1e0f2a92..9120f199 100644 --- a/src/charging-station/ui-server/ui-services/AbstractUIService.ts +++ b/src/charging-station/ui-server/ui-services/AbstractUIService.ts @@ -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; - protected uiServiceWorkerBroadcastChannel: UIServiceWorkerBroadcastChannel; + private uiServiceWorkerBroadcastChannel: UIServiceWorkerBroadcastChannel; + private readonly broadcastChannelRequests: Map; 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(); } public async requestHandler(request: RawData | JsonType): Promise { @@ -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 diff --git a/src/charging-station/ui-server/ui-services/UIService001.ts b/src/charging-station/ui-server/ui-services/UIService001.ts index 8176772b..4740b7d7 100644 --- a/src/charging-station/ui-server/ui-services/UIService001.ts +++ b/src/charging-station/ui-server/ui-services/UIService001.ts @@ -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 + ); } } diff --git a/src/types/UIProtocol.ts b/src/types/UIProtocol.ts index f120f9c9..1f78671e 100644 --- a/src/types/UIProtocol.ts +++ b/src/types/UIProtocol.ts @@ -47,5 +47,6 @@ export enum ResponseStatus { export interface ResponsePayload extends JsonObject { status: ResponseStatus; - hashIds?: string[]; + hashIdsSucceeded?: string[]; + hashIdsFailed?: string[]; } -- 2.34.1