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