cdbe108df333e9f6b6388f91e5327be60a8a3821
[e-mobility-charging-stations-simulator.git] / src / charging-station / UIServiceWorkerBroadcastChannel.ts
1 import type AbstractUIService from './ui-server/ui-services/AbstractUIService';
2 import WorkerBroadcastChannel from './WorkerBroadcastChannel';
3 import { type ResponsePayload, ResponseStatus } from '../types/UIProtocol';
4 import type {
5 BroadcastChannelResponse,
6 BroadcastChannelResponsePayload,
7 MessageEvent,
8 } from '../types/WorkerBroadcastChannel';
9 import logger from '../utils/Logger';
10
11 const moduleName = 'UIServiceWorkerBroadcastChannel';
12
13 type Responses = {
14 responsesExpected: number;
15 responsesReceived: number;
16 responses: BroadcastChannelResponsePayload[];
17 };
18
19 export default class UIServiceWorkerBroadcastChannel extends WorkerBroadcastChannel {
20 private readonly uiService: AbstractUIService;
21 private readonly responses: Map<string, Responses>;
22
23 constructor(uiService: AbstractUIService) {
24 super();
25 this.uiService = uiService;
26 this.onmessage = this.responseHandler.bind(this) as (message: MessageEvent) => void;
27 this.onmessageerror = this.messageErrorHandler.bind(this) as (message: MessageEvent) => void;
28 this.responses = new Map<string, Responses>();
29 }
30
31 private responseHandler(messageEvent: MessageEvent): void {
32 const validatedMessageEvent = this.validateMessageEvent(messageEvent);
33 if (validatedMessageEvent === false) {
34 return;
35 }
36 if (this.isRequest(validatedMessageEvent.data) === true) {
37 return;
38 }
39 const [uuid, responsePayload] = validatedMessageEvent.data as BroadcastChannelResponse;
40 if (this.responses.has(uuid) === false) {
41 this.responses.set(uuid, {
42 responsesExpected: this.uiService.getBroadcastChannelExpectedResponses(uuid),
43 responsesReceived: 1,
44 responses: [responsePayload],
45 });
46 } else if (
47 this.responses.get(uuid)?.responsesReceived <= this.responses.get(uuid)?.responsesExpected
48 ) {
49 this.responses.get(uuid).responsesReceived++;
50 this.responses.get(uuid).responses.push(responsePayload);
51 }
52 if (
53 this.responses.get(uuid)?.responsesReceived === this.responses.get(uuid)?.responsesExpected
54 ) {
55 this.uiService.sendResponse(uuid, this.buildResponsePayload(uuid));
56 this.responses.delete(uuid);
57 this.uiService.deleteBroadcastChannelRequest(uuid);
58 }
59 }
60
61 private buildResponsePayload(uuid: string): ResponsePayload {
62 const responsesStatus =
63 this.responses
64 .get(uuid)
65 ?.responses.every(({ status }) => status === ResponseStatus.SUCCESS) === true
66 ? ResponseStatus.SUCCESS
67 : ResponseStatus.FAILURE;
68 return {
69 status: responsesStatus,
70 hashIdsSucceeded: this.responses
71 .get(uuid)
72 ?.responses.map(({ status, hashId }) => {
73 if (status === ResponseStatus.SUCCESS) {
74 return hashId;
75 }
76 })
77 .filter((hashId) => hashId !== undefined),
78 ...(responsesStatus === ResponseStatus.FAILURE && {
79 hashIdsFailed: this.responses
80 .get(uuid)
81 ?.responses.map(({ status, hashId }) => {
82 if (status === ResponseStatus.FAILURE) {
83 return hashId;
84 }
85 })
86 .filter((hashId) => hashId !== undefined),
87 }),
88 ...(responsesStatus === ResponseStatus.FAILURE && {
89 responsesFailed: this.responses
90 .get(uuid)
91 ?.responses.map((response) => {
92 if (response.status === ResponseStatus.FAILURE) {
93 return response;
94 }
95 })
96 .filter((response) => response !== undefined),
97 }),
98 };
99 }
100
101 private messageErrorHandler(messageEvent: MessageEvent): void {
102 logger.error(
103 `${this.uiService.logPrefix(moduleName, 'messageErrorHandler')} Error at handling message:`,
104 messageEvent
105 );
106 }
107 }