Commit | Line | Data |
---|---|---|
32de5a57 LM |
1 | import { JsonType } from '@/type/JsonType'; |
2 | import { | |
3 | ProcedureName, | |
4 | ProtocolResponse, | |
5 | ResponsePayload, | |
6 | ResponseStatus, | |
7 | } from '@/type/UIProtocol'; | |
8 | import Utils from './Utils'; | |
9 | import config from '@/assets/config'; | |
10 | import { v4 as uuidv4 } from 'uuid'; | |
11 | ||
12 | type ResponseHandler = { | |
13 | resolve: (value: ResponsePayload | PromiseLike<ResponsePayload>) => void; | |
14 | reject: (reason?: any) => void; | |
15 | procedureName: ProcedureName; | |
16 | }; | |
17 | ||
18 | export default class UIClient { | |
19 | private static _instance: UIClient | null = null; | |
20 | ||
5a010bf0 | 21 | private _ws!: WebSocket; |
32de5a57 LM |
22 | private _responseHandlers: Map<string, ResponseHandler>; |
23 | ||
24 | private constructor() { | |
5a010bf0 | 25 | this.openWS(); |
32de5a57 | 26 | this._responseHandlers = new Map<string, ResponseHandler>(); |
32de5a57 LM |
27 | } |
28 | ||
29 | public static get instance() { | |
30 | if (UIClient._instance === null) { | |
31 | UIClient._instance = new UIClient(); | |
32 | } | |
33 | return UIClient._instance; | |
34 | } | |
35 | ||
36 | public onOpen(listener: (this: WebSocket, ev: Event) => void) { | |
37 | this._ws.addEventListener('open', listener); | |
38 | } | |
39 | ||
5a010bf0 JB |
40 | public async startSimulator(): Promise<ResponsePayload> { |
41 | return this.sendRequest(ProcedureName.START_SIMULATOR, {}); | |
42 | } | |
43 | ||
44 | public async stopSimulator(): Promise<ResponsePayload> { | |
45 | return this.sendRequest(ProcedureName.STOP_SIMULATOR, {}); | |
46 | } | |
32de5a57 | 47 | |
5a010bf0 | 48 | public async listChargingStations(): Promise<ResponsePayload> { |
32de5a57 LM |
49 | return this.sendRequest(ProcedureName.LIST_CHARGING_STATIONS, {}); |
50 | } | |
51 | ||
52 | public async startTransaction( | |
53 | hashId: string, | |
54 | connectorId: number, | |
5a010bf0 | 55 | idTag: string | undefined |
32de5a57 | 56 | ): Promise<ResponsePayload> { |
32de5a57 LM |
57 | return this.sendRequest(ProcedureName.START_TRANSACTION, { |
58 | hashId, | |
59 | connectorId, | |
60 | idTag, | |
61 | }); | |
62 | } | |
63 | ||
5a010bf0 JB |
64 | public async stopTransaction( |
65 | hashId: string, | |
66 | transactionId: number | undefined | |
67 | ): Promise<ResponsePayload> { | |
32de5a57 LM |
68 | return this.sendRequest(ProcedureName.STOP_TRANSACTION, { |
69 | hashId, | |
70 | transactionId, | |
71 | }); | |
72 | } | |
73 | ||
5a010bf0 JB |
74 | public async openConnection(hashId: string): Promise<ResponsePayload> { |
75 | return this.sendRequest(ProcedureName.OPEN_CONNECTION, { | |
76 | hashId, | |
77 | }); | |
78 | } | |
79 | ||
80 | public async closeConnection(hashId: string): Promise<ResponsePayload> { | |
81 | return this.sendRequest(ProcedureName.CLOSE_CONNECTION, { | |
82 | hashId, | |
83 | }); | |
84 | } | |
85 | ||
86 | private openWS(): void { | |
87 | this._ws = new WebSocket( | |
88 | `ws://${config.emobility.host}:${config.emobility.port}`, | |
89 | config.emobility.protocol | |
90 | ); | |
91 | this._ws.onmessage = this.responseHandler.bind(this); | |
92 | } | |
93 | ||
32de5a57 LM |
94 | private setResponseHandler( |
95 | id: string, | |
96 | resolve: (value: ResponsePayload | PromiseLike<ResponsePayload>) => void, | |
97 | reject: (reason?: any) => void, | |
98 | procedureName: ProcedureName | |
99 | ): void { | |
100 | this._responseHandlers.set(id, { resolve, reject, procedureName }); | |
101 | } | |
102 | ||
103 | private getResponseHandler(id: string): ResponseHandler | undefined { | |
104 | return this._responseHandlers.get(id); | |
105 | } | |
106 | ||
107 | private async sendRequest(command: ProcedureName, data: JsonType): Promise<ResponsePayload> { | |
108 | let uuid: string; | |
109 | return Utils.promiseWithTimeout( | |
110 | new Promise((resolve, reject) => { | |
111 | uuid = uuidv4(); | |
112 | const msg = JSON.stringify([uuid, command, data]); | |
113 | ||
5a010bf0 JB |
114 | if (this._ws.readyState !== WebSocket.OPEN) { |
115 | this.openWS(); | |
116 | } | |
117 | if (this._ws.readyState === WebSocket.OPEN) { | |
32de5a57 LM |
118 | this._ws.send(msg); |
119 | } else { | |
120 | throw new Error(`Send request ${command} message: connection not opened`); | |
121 | } | |
122 | ||
123 | this.setResponseHandler(uuid, resolve, reject, command); | |
124 | }), | |
125 | 60 * 1000, | |
126 | Error(`Send request ${command} message timeout`), | |
127 | () => { | |
128 | this._responseHandlers.delete(uuid); | |
129 | } | |
130 | ); | |
131 | } | |
132 | ||
97f0a1a5 | 133 | private responseHandler(messageEvent: MessageEvent<string>): void { |
32de5a57 LM |
134 | const data = JSON.parse(messageEvent.data) as ProtocolResponse; |
135 | ||
53e5fd67 JB |
136 | if (Array.isArray(data) === false) { |
137 | throw new Error('Response not an array: ' + JSON.stringify(data, null, 2)); | |
32de5a57 LM |
138 | } |
139 | ||
140 | const [uuid, response] = data; | |
141 | ||
142 | if (this._responseHandlers.has(uuid) === true) { | |
143 | switch (response.status) { | |
144 | case ResponseStatus.SUCCESS: | |
145 | this.getResponseHandler(uuid)?.resolve(response); | |
146 | break; | |
147 | case ResponseStatus.FAILURE: | |
148 | this.getResponseHandler(uuid)?.reject(response); | |
149 | break; | |
150 | default: | |
151 | throw new Error(`Response status not supported: ${response.status}`); | |
152 | } | |
153 | } else { | |
154 | throw new Error('Not a response to a request: ' + JSON.stringify(data, null, 2)); | |
155 | } | |
156 | } | |
157 | } |