93586ceeee85a927e5fcbbea9f79b70cc2db59d7
[e-mobility-charging-stations-simulator.git] / src / charging-station / broadcast-channel / UIServiceWorkerBroadcastChannel.ts
1 import { WorkerBroadcastChannel } from './WorkerBroadcastChannel.js'
2 import {
3 type BroadcastChannelResponse,
4 type BroadcastChannelResponsePayload,
5 type MessageEvent,
6 type ResponsePayload,
7 ResponseStatus
8 } from '../../types/index.js'
9 import { isNullOrUndefined, logger } from '../../utils/index.js'
10 import type { AbstractUIService } from '../ui-server/ui-services/AbstractUIService.js'
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)) {
38 return
39 }
40 const [uuid, responsePayload] = validatedMessageEvent.data as BroadcastChannelResponse
41 if (!this.responses.has(uuid)) {
42 this.responses.set(uuid, {
43 responsesExpected: this.uiService.getBroadcastChannelExpectedResponses(uuid),
44 responsesReceived: 1,
45 responses: [responsePayload]
46 })
47 } else if (
48 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
49 this.responses.get(uuid)!.responsesReceived <= this.responses.get(uuid)!.responsesExpected
50 ) {
51 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
52 ++this.responses.get(uuid)!.responsesReceived
53 this.responses.get(uuid)?.responses.push(responsePayload)
54 }
55 if (
56 this.responses.get(uuid)?.responsesReceived === this.responses.get(uuid)?.responsesExpected
57 ) {
58 this.uiService.sendResponse(uuid, this.buildResponsePayload(uuid))
59 this.responses.delete(uuid)
60 this.uiService.deleteBroadcastChannelRequest(uuid)
61 }
62 }
63
64 private buildResponsePayload (uuid: string): ResponsePayload {
65 const responsesStatus =
66 this.responses
67 .get(uuid)
68 ?.responses.every(({ status }) => status === ResponseStatus.SUCCESS) === true
69 ? ResponseStatus.SUCCESS
70 : ResponseStatus.FAILURE
71 return {
72 status: responsesStatus,
73 hashIdsSucceeded: this.responses
74 .get(uuid)
75 ?.responses.map(({ status, hashId }) => {
76 if (hashId != null && status === ResponseStatus.SUCCESS) {
77 return hashId
78 }
79 return undefined
80 })
81 .filter((hashId) => !isNullOrUndefined(hashId)) as string[],
82 ...(responsesStatus === ResponseStatus.FAILURE && {
83 hashIdsFailed: this.responses
84 .get(uuid)
85 ?.responses.map(({ status, hashId }) => {
86 if (hashId != null && status === ResponseStatus.FAILURE) {
87 return hashId
88 }
89 return undefined
90 })
91 .filter((hashId) => !isNullOrUndefined(hashId)) as string[]
92 }),
93 ...(responsesStatus === ResponseStatus.FAILURE && {
94 responsesFailed: this.responses
95 .get(uuid)
96 ?.responses.map((response) => {
97 if (response != null && response.status === ResponseStatus.FAILURE) {
98 return response
99 }
100 return undefined
101 })
102 .filter((response) => !isNullOrUndefined(response)) as BroadcastChannelResponsePayload[]
103 })
104 }
105 }
106
107 private messageErrorHandler (messageEvent: MessageEvent): void {
108 logger.error(
109 `${this.uiService.logPrefix(moduleName, 'messageErrorHandler')} Error at handling message:`,
110 messageEvent
111 )
112 }
113 }